Receive notification in MS Teams about Jira Cloud issues you are watching


Get Started

Not the template you're looking for? Browse more.

About the template


This template demonstrates how to send a private notification message in Microsoft Teams for a watcher in Confluence Cloud when a page is updated. Get started to learn more.

About ScriptRunner Connect


What is ScriptRunner Connect?

ScriptRunner Connect is an AI assisted code-first (JavaScript/TypeScript) integration platform (iPaaS) for building complex integrations and automations.

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.

Template Content


README

Scripts

TypeScriptOnJiraCloudIssueUpdated
Issue Updated

README


๐Ÿ“‹ Overview

This template demonstrates how to send a private notification message in Microsoft Teams for a watcher in Confluence Cloud when a page is updated.

๐Ÿ–Š๏ธ Setup

  • Configure the API Connection and Event Listener by creating connectors for Jira Cloud and MS Graph, or use existing ones.
  • Note that for if you are using a self-managed Connector for MS Graph, you might need to add certain API permissions to your app and get them approved by an administrator. As a minimum, make sure you have the privileges set by the User.ReadBasic.All and Chat.ReadWrite permissions. After changing permissions you will have to reauthorize your MS Graph Connector.

๐Ÿš€ Usage

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.

API Connections


TypeScriptOnJiraCloudIssueUpdated

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'
        }
    });
}
Documentation ยท Support ยท Suggestions & feature requests