Skip to content

Instantly share code, notes, and snippets.

@asmagin
Created March 9, 2020 04:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save asmagin/b81130d1e54fb5f3df47dbe77f565588 to your computer and use it in GitHub Desktop.
Save asmagin/b81130d1e54fb5f3df47dbe77f565588 to your computer and use it in GitHub Desktop.
MS Teams Notifications Lambda
const fetch = require("node-fetch");
const IN_PROGRESS_IMAGE = "data:image/jpeg;base64,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";
const SUCCEEDED_IMAGE = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBmRXhpZgAATU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAAExAAIAAAAQAAAATgAAAAAAAABgAAAAAQAAAGAAAAABcGFpbnQubmV0IDQuMi44AP/bAEMABgQFBgUEBgYFBgcHBggKEAoKCQkKFA4PDBAXFBgYFxQWFhodJR8aGyMcFhYgLCAjJicpKikZHy0wLSgwJSgpKP/bAEMBBwcHCggKEwoKEygaFhooKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKP/AABEIACgAKAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APpPxNr1n4e01ru9YnJ2xxr96RvQf414n4g8c61rErf6S9pbHgQ27FRj3PU/y9qPiPrT6x4nucNm2tWMEQB4wDyfxOfwxXL15levKUuWOx+d51nVXEVZUqUrQWmnXzJPPl8zf5sm/wDvbjmuk8P+Oda0eVf9Je7thwYbhiwx7HqP5e1cvXo/gXwXAbNtb8TBYrBU3xxSHaGH95vb0Hf+eVJTcvcPOy2GKq1ksNJp7t30S7vyPTfDOvWfiHTVu7JiMHbJG33o29D/AI0V494N1q20fxz/AMS1pRpV1L5GJTztJ+Un6H9M0V6VGrzx13PvspzNYyjeo1zRdn2fmvU5uz+zf21H/a/m/ZfO/f8AlfexnnFdx468FwCzXW/DIWWwZN8kUZ3BR/eX29R2/lgfEjRX0fxPc4XFtdMZ4iBxgnkfgc/hip/APjKbw7c/Z7rdLpkjfOnUxn+8v9R3rgjyxbpz+8+JoRoUalTBYyNrv4uqfR+hteAfBsKW39veJNsVlGvmRRS8Bh/fb29B3+nXF8feMpvEVz9ntd0WmRt8idDIf7zf0Hajx94ym8RXP2e13RaZG3yJ0Mh/vN/Qdq5CipUUVyQ2/MMbjadKn9Twfwfal1k/8uyJLbd9oi8v7+8bfrmiuk+G+ivrHie2yuba1YTykjjAPA/E4/DNFXQoOcb3OrJ8mqYyk6vNyq/3ntnibQbPxDprWl6pGDujkX70beo/wrxPxB4G1rR5W/0Z7u2HImt1LDHuOo/l70UV1V6UZrme59NnWV4fE03WmrSS3X6nN+RL5mzypN/93ac10nh/wNrWsSrm2e0tjyZrhSox7Dqf5e9FFcVClGcrM+SyfLqWMr8lW9ke2eGdBs/D2mraWSk5O6SRvvSN6n/CiiivUSSVkfo9OnClBU6askf/2Q==";
const FAILED_IMAGE = "data:image/jpeg;base64,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";
const detectSourceType = (source) => {
const branch = source || '';
const shaRegex = /(\w|\d){40}/;
const prRegex = /pr\/(\d+)/
if (branch.match(shaRegex)) {
return `/commit/${branch}`;
}
if (branch.match(prRegex)) {
return `/pull/${branch.replace(prRegex, '$1')}`;
}
return `/tree/${branch}`
}
exports.handler = async (event, context, callback) => {
const webhook_url = process.env.TEAMS_WEBHOOK_URL;
if (event && event.Records && event.Records.length) {
event.Records.map(async record => {
const raw_msg = record && record.Sns && record.Sns.Message;
const msg = JSON.parse(raw_msg);
const detail = msg && msg.detail;
const time = msg && msg.time;
if (detail) {
console.log('CodeBuild Event payload.\n', JSON.stringify(detail, null, 2))
const status = detail["build-status"];
const themeColor = status === "SUCCEEDED" ? "#CEDB56" : status === "IN_PROGRESS" ? "#76CDD8" : "#D35D47";
const buildId = detail["build-id"].split('/')[1];
const region = 'us-east-1';
const project = detail["project-name"];
const summary = `[Build [${project}] notification](https://console.aws.amazon.com/codesuite/codebuild/projects/${project}/build/${buildId}/log?region=${region})`;
const additional = detail["additional-information"];
const branch = additional && additional["source-version"];
const branch_url = (additional.source && additional.source.location && additional.source.location.replace(".git", detectSourceType(branch))) || "#";
const environment = additional && additional.environment;
const variables = environment && environment['environment-variables'];
const env = variables.filter(x => x.name === 'env_name')[0].value
const image = status === "SUCCEEDED" ? SUCCEEDED_IMAGE : status === "IN_PROGRESS" ? IN_PROGRESS_IMAGE : FAILED_IMAGE;
const webhook_payload = {
"@type": "MessageCard",
"@context": "http://schema.org/extensions",
themeColor,
summary,
sections: [
{
activityTitle: summary,
activitySubtitle: `On project [${project}]`,
activityImage: image,
facts: [
{
name: "Status",
value: status
},
{
name: "Date",
value: time.replace("Z", " UTC")
},
{
name: "Source Branch",
value: `[${branch}](${branch_url})`
},
{
name: "Target Env",
value: env
}
],
markdown: true
}
]
};
try {
const response = await fetch(webhook_url, {
method: "post",
body: JSON.stringify(webhook_payload),
headers: { "Content-Type": "application/json" }
});
const data = await response.text();
console.log(`Successfully send message to MS Teams\n`, data);
} catch (error) {
console.error(`Unexpected error happened while sending message to MS Teams.\n`, error);
}
} else {
console.warn("Sns record doesn't contain details");
}
});
}
callback(null, "Success");
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment