Template Content
About the template
About ScriptRunner Connect
What is ScriptRunner Connect?
Can I try it out for free?
Yes. ScriptRunner Connect comes with a forever free tier.
Can I customize the integration logic?
Absolutely. The main value proposition of ScriptRunner Connect is that you'll get full access to the code that is powering the integration, which means you can make any changes to the the integration logic yourself.
Can I change the integration to communicate with additional apps?
Yes. Since ScriptRunner Connect specializes in enabling complex integrations, you can easily change the integration logic to connect to as many additional apps as you need, no limitations.
What if I don't feel comfortable making changes to the code?
First you can try out our AI assistant which can help you understand what the code does, and also help you make changes to the code. Alternatively you can hire our professionals to make the changes you need or build new integrations from scratch.
Do I have to host it myself?
No. ScriptRunner Connect is a fully managed SaaS (Software-as-a-Service) product.
What about security?
ScriptRunner Connect is ISO 27001 and SOC 2 certified. Learn more about our security.
This template demonstrates how to send a private notification message in Microsoft Teams for a watcher in Confluence Cloud when a page is updated.
User.ReadBasic.All
and Chat.ReadWrite
permissions. After changing permissions you will have to reauthorize your MS Graph Connector.Update an issue in your Jira Cloud instance, after successful event processing a private chat message should be sent in Microsoft Teams to each issue watcher, providing that their Jira Cloud user can be matched with an account on Microsoft Teams.
import JiraCloud from './api/jira/cloud';
import Microsoft from './api/microsoft';
import { IssueUpdatedEvent } from '@sr-connect/jira-cloud/events';
import { MicrosoftGraphUserAsResponse } from '@managed-api/microsoft-graph-v1-core/definitions/MicrosoftGraphUserAsResponse';
/**
* This function listens to Jira Cloud issue updated event, and when triggered, finds all the watchers for given issue, tries to match them with users in Teams (by email) and then send a message to them.
*
* @param event Object that holds Issue Updated event data
* @param context Object that holds function invocation context data
*/
export default async function (event: IssueUpdatedEvent, context: Context): Promise<void> {
if (context.triggerType === 'MANUAL') {
console.error('This script is designed to be triggered externally or manually from the Event Listener. Please consider using Event Listener Test Event Payload if you need to trigger this script manually.');
return;
}
console.log(`Issue updated event received for issue with ID: ${event.issue.id}`);
// Get all issue watchers
const watchers = await JiraCloud.Issue.Watcher.getWatchers({
issueIdOrKey: event.issue.id
});
console.log(`Watchers found: ${watchers.watchers.length}`);
// Extract watcher emails and cast them to lower case
const watcherEmails = watchers.watchers.map(w => w.emailAddress?.toLowerCase());
// Get the Jira Cloud instance base URL
const baseUrl = event.issue.self.substring(0, event.issue.self.indexOf('/rest/'));
// Check if the base URL was found
if (!baseUrl) {
// If not, log a warning into console
console.warn('No base url found')
}
// Construct the URL for the issue that triggered the event
const issueUrl = baseUrl ? `${baseUrl}/browse/${event.issue.key}` : '';
// Construct an HTML notification message using data about the updated issue
const notificationMessage = `<p>Jira issue <a href='${issueUrl}'>${event.issue.fields.summary}</a> updated by <b>${event.user.displayName}</b> at ${new Date(event.timestamp)}</p>`;
// Get details for authenticated user in Microsoft Teams
const sender = await Microsoft.Users.getMyUser();
// Get all users in Microsoft Teams instance, explicitly selecting the properties needed
const users = (await Microsoft.Users.getUsers({ $select: ['displayName', 'mail', 'otherMails', 'id'] })).value ?? [];
// Match Jira Cloud users with their Microsoft Teams counterparts by comparing emails (excluding the authenticated user)
const matchingUsers = users.filter((user => hasMatchingEmail(user, watcherEmails) && user.id !== sender.id));
console.log(`Matching users in Teams found: ${matchingUsers.length}`);
// Iterate over all the matching users
for (const receiver of matchingUsers) {
try {
// Send a message to each user
await sendOneToOneTeamMessage(sender.id ?? '', receiver.id ?? '', notificationMessage);
console.log(`Notification message sent to: ${receiver.displayName} (${receiver.id})`);
} catch (e) {
// If something goes wrong, log it out
console.error('Failed to send notification message in Teams', e, {
sender,
receiver
});
}
}
}
// Function that checks if a Jira Cloud user can be matched to a Teams user by comparing emails
function hasMatchingEmail(user: MicrosoftGraphUserAsResponse, emails: string[]) {
// Cast the Teams user main and additional emails to lower case
const mainMail = user.mail?.toLocaleLowerCase() || '';
const otherMails = user.otherMails.map(m => m.toLocaleLowerCase());
// Check if any of the emails in Teams matches the one of the Jira Cloud user
return emails.includes(mainMail.toLocaleLowerCase()) || emails.some(m => otherMails.includes(m));
}
// Function to create one-to-one chat with a reciver user in Teams and then send a notification message into it
async function sendOneToOneTeamMessage(senderUserId: string, receiverUserId: string, notificationMessage: string) {
const chat = await Microsoft.Teams.Messaging.Chat.createChat({
body: {
chatType: 'oneOnOne',
members: [
{
id: senderUserId,
roles: ['owner']
},
{
id: receiverUserId,
roles: ['owner']
}
]
}
});
await Microsoft.Teams.Messaging.Chat.sendMessage({
chat_id: chat.id ?? '',
body: {
body: {
content: notificationMessage,
contentType: 'html'
},
importance: 'normal',
subject: 'Jira issue updated'
}
});
}