feat: support multiple URL unfurls at a time
This commit is contained in:
Родитель
5c4c2bc4b9
Коммит
1a6ee47e15
|
@ -1,12 +1,7 @@
|
|||
import { WebClient } from '@slack/web-api';
|
||||
import { MessageAttachment } from '@slack/bolt';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
export async function handleChromiumReviewUnfurl(
|
||||
url: string,
|
||||
message_ts: string,
|
||||
channel: string,
|
||||
client: WebClient,
|
||||
) {
|
||||
export async function handleChromiumReviewUnfurl(url: string): Promise<MessageAttachment | null> {
|
||||
const match = /^https:\/\/chromium-review\.googlesource\.com\/c\/([a-z0-9]+)\/([a-z0-9]+)\/\+\/([0-9]+)/g.exec(
|
||||
url,
|
||||
);
|
||||
|
@ -29,39 +24,29 @@ export async function handleChromiumReviewUnfurl(
|
|||
? message.substr(subject.length + 1).trim()
|
||||
: message;
|
||||
|
||||
const unfurl = await client.chat.unfurl({
|
||||
channel,
|
||||
ts: message_ts,
|
||||
unfurls: {
|
||||
[url]: {
|
||||
color: '#4D394B',
|
||||
author_name: owner.name,
|
||||
author_icon:
|
||||
owner.avatars && owner.avatars.length
|
||||
? owner.avatars[owner.avatars.length - 1].url
|
||||
: ':void',
|
||||
author_link: `https://chromium-review.googlesource.com/q/author:${encodeURIComponent(
|
||||
owner.email,
|
||||
)}`,
|
||||
fallback: `[${niceRepo}] #${cl} ${subject}`,
|
||||
title: `#${cl} ${subject}`,
|
||||
title_link: url,
|
||||
footer_icon: 'https://chromium-review.googlesource.com/favicon.ico',
|
||||
text: messageWithoutSubject,
|
||||
footer: `<https://source.chromium.org/chromium/${niceRepo}|${niceRepo}>`,
|
||||
ts: `${new Date(date).getTime()}`,
|
||||
// TODO: Labels? CQ status?
|
||||
// fields: [{
|
||||
return {
|
||||
color: '#4D394B',
|
||||
author_name: owner.name,
|
||||
author_icon:
|
||||
owner.avatars && owner.avatars.length
|
||||
? owner.avatars[owner.avatars.length - 1].url
|
||||
: ':void',
|
||||
author_link: `https://chromium-review.googlesource.com/q/author:${encodeURIComponent(
|
||||
owner.email,
|
||||
)}`,
|
||||
fallback: `[${niceRepo}] #${cl} ${subject}`,
|
||||
title: `#${cl} ${subject}`,
|
||||
title_link: url,
|
||||
footer_icon: 'https://chromium-review.googlesource.com/favicon.ico',
|
||||
text: messageWithoutSubject,
|
||||
footer: `<https://source.chromium.org/chromium/${niceRepo}|${niceRepo}>`,
|
||||
ts: `${new Date(date).getTime()}`,
|
||||
// TODO: Labels? CQ status?
|
||||
// fields: [{
|
||||
|
||||
// }]
|
||||
},
|
||||
},
|
||||
});
|
||||
if (unfurl.status !== 200 || !unfurl.ok) {
|
||||
console.error('Failed to unfurl', unfurl);
|
||||
}
|
||||
return true;
|
||||
// }]
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
|
122
src/crbug.ts
122
src/crbug.ts
|
@ -1,4 +1,4 @@
|
|||
import { WebClient } from '@slack/web-api';
|
||||
import { MessageAttachment } from '@slack/bolt';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
import { Policy, ConstantBackoff } from 'cockatiel';
|
||||
|
@ -83,14 +83,9 @@ const notNull = <T>(arr: (T | null)[]) => {
|
|||
return arr.filter(Boolean) as T[];
|
||||
};
|
||||
|
||||
export async function handleChromiumBugUnfurl(
|
||||
url: string,
|
||||
message_ts: string,
|
||||
channel: string,
|
||||
client: WebClient,
|
||||
) {
|
||||
export async function handleChromiumBugUnfurl(url: string): Promise<MessageAttachment | null> {
|
||||
const bugIdentifier = parseBugIdentifier(url);
|
||||
if (!bugIdentifier) return false;
|
||||
if (!bugIdentifier) return null;
|
||||
|
||||
const headers = await getMonorailHeaders();
|
||||
const monorailQuery = JSON.stringify({
|
||||
|
@ -109,73 +104,62 @@ export async function handleChromiumBugUnfurl(
|
|||
headers,
|
||||
body: monorailQuery,
|
||||
});
|
||||
if ((await response).status !== 200) return false;
|
||||
if ((await commentResponse).status !== 200) return false;
|
||||
if ((await response).status !== 200) return null;
|
||||
if ((await commentResponse).status !== 200) return null;
|
||||
|
||||
const { issue }: { issue: MonorailIssue } = JSON.parse((await (await response).text()).slice(4));
|
||||
const { comments }: { comments: MonorailComments } = JSON.parse(
|
||||
(await (await commentResponse).text()).slice(4),
|
||||
);
|
||||
|
||||
const unfurl = await client.chat.unfurl({
|
||||
channel,
|
||||
ts: message_ts,
|
||||
unfurls: {
|
||||
[url]: {
|
||||
color: issue.statusRef.meansOpen ? '#36B37E' : '#FF5630',
|
||||
author_name: issue.reporterRef.displayName,
|
||||
// author_icon: owner.avatars && owner.avatars.length ? owner.avatars[owner.avatars.length - 1].url : ':void',
|
||||
author_link: `https://bugs.chromium.org/u/${issue.reporterRef.userId}/`,
|
||||
fallback: `[${issue.projectName}] #${issue.localId} ${issue.summary}`,
|
||||
title: `#${issue.localId} ${issue.summary}`,
|
||||
title_link: url,
|
||||
footer_icon: 'https://bugs.chromium.org/static/images/monorail.ico',
|
||||
text: comments[0].content,
|
||||
footer: `<https://bugs.chromium.org/p/${issue.projectName}|crbug/${issue.projectName}>`,
|
||||
ts: `${issue.openedTimestamp * 1000}`,
|
||||
fields: notNull([
|
||||
issue.componentRefs && issue.componentRefs.length
|
||||
? {
|
||||
title: 'Components',
|
||||
value: issue.componentRefs
|
||||
.map(
|
||||
(ref) =>
|
||||
`• <https://bugs.chromium.org/p/${
|
||||
issue.projectName
|
||||
}/issues/list?q=component%3A${encodeURIComponent(
|
||||
ref.path,
|
||||
)}|\`${ref.path.replace(/>/g, '→')}\`>`,
|
||||
)
|
||||
.join('\n'),
|
||||
short: true,
|
||||
}
|
||||
: null,
|
||||
issue.labelRefs && issue.labelRefs.length
|
||||
? {
|
||||
title: 'Labels',
|
||||
value: issue.labelRefs
|
||||
.map(
|
||||
(ref) =>
|
||||
`• <https://bugs.chromium.org/p/${
|
||||
issue.projectName
|
||||
}/issues/list?q=label%3A${encodeURIComponent(ref.label)}|\`${ref.label}\`>`,
|
||||
)
|
||||
.join('\n'),
|
||||
short: true,
|
||||
}
|
||||
: null,
|
||||
{
|
||||
title: 'Comments',
|
||||
value: `${comments.length}`,
|
||||
return {
|
||||
color: issue.statusRef.meansOpen ? '#36B37E' : '#FF5630',
|
||||
author_name: issue.reporterRef.displayName,
|
||||
author_link: `https://bugs.chromium.org/u/${issue.reporterRef.userId}/`,
|
||||
fallback: `[${issue.projectName}] #${issue.localId} ${issue.summary}`,
|
||||
title: `#${issue.localId} ${issue.summary}`,
|
||||
title_link: url,
|
||||
footer_icon: 'https://bugs.chromium.org/static/images/monorail.ico',
|
||||
text: comments[0].content,
|
||||
footer: `<https://bugs.chromium.org/p/${issue.projectName}|crbug/${issue.projectName}>`,
|
||||
ts: `${issue.openedTimestamp * 1000}`,
|
||||
fields: notNull([
|
||||
issue.componentRefs && issue.componentRefs.length
|
||||
? {
|
||||
title: 'Components',
|
||||
value: issue.componentRefs
|
||||
.map(
|
||||
(ref) =>
|
||||
`• <https://bugs.chromium.org/p/${
|
||||
issue.projectName
|
||||
}/issues/list?q=component%3A${encodeURIComponent(ref.path)}|\`${ref.path.replace(
|
||||
/>/g,
|
||||
'→',
|
||||
)}\`>`,
|
||||
)
|
||||
.join('\n'),
|
||||
short: true,
|
||||
},
|
||||
]),
|
||||
}
|
||||
: null,
|
||||
issue.labelRefs && issue.labelRefs.length
|
||||
? {
|
||||
title: 'Labels',
|
||||
value: issue.labelRefs
|
||||
.map(
|
||||
(ref) =>
|
||||
`• <https://bugs.chromium.org/p/${
|
||||
issue.projectName
|
||||
}/issues/list?q=label%3A${encodeURIComponent(ref.label)}|\`${ref.label}\`>`,
|
||||
)
|
||||
.join('\n'),
|
||||
short: true,
|
||||
}
|
||||
: null,
|
||||
{
|
||||
title: 'Comments',
|
||||
value: `${comments.length}`,
|
||||
short: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (unfurl.status !== 200 || !unfurl.ok) {
|
||||
console.error('Failed to unfurl', unfurl);
|
||||
}
|
||||
|
||||
return true;
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { WebClient } from '@slack/web-api';
|
||||
import { MessageAttachment } from '@slack/bolt';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
type GrimoireMeta = {
|
||||
|
@ -117,19 +117,14 @@ function removeOverIndent(contents: string): string {
|
|||
return lines.map((l) => l.slice(minIndent)).join('\n');
|
||||
}
|
||||
|
||||
export async function handleChromiumSourceUnfurl(
|
||||
url: string,
|
||||
message_ts: string,
|
||||
channel: string,
|
||||
client: WebClient,
|
||||
) {
|
||||
export async function handleChromiumSourceUnfurl(url: string): Promise<MessageAttachment | null> {
|
||||
const parsed = new URL(url);
|
||||
if (parsed.hostname !== 'source.chromium.org') return false;
|
||||
if (parsed.hostname !== 'source.chromium.org') return null;
|
||||
|
||||
const match = /^https:\/\/source\.chromium\.org\/([a-z0-9]+)\/([a-z0-9]+)\/([a-z0-9]+)\/\+\/([a-z0-9]+):([^;]+)(?:;l=([0-9]+(?:-[0-9]+)?))?/.exec(
|
||||
url,
|
||||
);
|
||||
if (!match) return false;
|
||||
if (!match) return null;
|
||||
|
||||
const [, parent, project, projectKey, branch, fileName, lineRange] = match;
|
||||
|
||||
|
@ -148,25 +143,14 @@ export async function handleChromiumSourceUnfurl(
|
|||
}
|
||||
}
|
||||
|
||||
const unfurl = await client.chat.unfurl({
|
||||
channel,
|
||||
ts: message_ts,
|
||||
unfurls: {
|
||||
[url]: {
|
||||
color: '#00B8D9',
|
||||
fallback: `[${project}/${projectKey}] ${fileName}`,
|
||||
title: fileName,
|
||||
title_link: url,
|
||||
footer_icon: 'https://www.gstatic.com/devopsconsole/images/oss/favicons/oss-96x96.png',
|
||||
text: `\`\`\`\n${maybeTruncate(contents)}\n\`\`\``,
|
||||
footer: `<https://source.chromium.org/${parent}/${project}/${projectKey}/+/${branch}|${project}/${projectKey}>`,
|
||||
mrkdwn_in: ['text'],
|
||||
},
|
||||
},
|
||||
});
|
||||
if (unfurl.status !== 200 || !unfurl.ok) {
|
||||
console.error('Failed to unfurl', unfurl);
|
||||
}
|
||||
|
||||
return true;
|
||||
return {
|
||||
color: '#00B8D9',
|
||||
fallback: `[${project}/${projectKey}] ${fileName}`,
|
||||
title: fileName,
|
||||
title_link: url,
|
||||
footer_icon: 'https://www.gstatic.com/devopsconsole/images/oss/favicons/oss-96x96.png',
|
||||
text: `\`\`\`\n${maybeTruncate(contents)}\n\`\`\``,
|
||||
footer: `<https://source.chromium.org/${parent}/${project}/${projectKey}/+/${branch}|${project}/${projectKey}>`,
|
||||
mrkdwn_in: ['text'],
|
||||
};
|
||||
}
|
||||
|
|
41
src/index.ts
41
src/index.ts
|
@ -1,13 +1,15 @@
|
|||
import { App } from '@slack/bolt';
|
||||
import { App, MessageAttachment } from '@slack/bolt';
|
||||
|
||||
import { handleChromiumReviewUnfurl } from './chromium-review';
|
||||
import { handleChromiumBugUnfurl } from './crbug';
|
||||
import { handleChromiumSourceUnfurl } from './crsource';
|
||||
|
||||
const app = new App({
|
||||
token: process.env.SLACK_TOKEN,
|
||||
authorize: async () => ({
|
||||
botToken: process.env.SLACK_TOKEN,
|
||||
botId: process.env.SLACK_BOT_ID,
|
||||
}),
|
||||
signingSecret: process.env.SLACK_SIGNING_SECRET,
|
||||
botId: process.env.SLACK_BOT_ID,
|
||||
});
|
||||
|
||||
app.event('link_shared', async ({ client, body }) => {
|
||||
|
@ -16,13 +18,36 @@ app.event('link_shared', async ({ client, body }) => {
|
|||
// Do not unfurl if there are more than three links, we're nice like that
|
||||
if (links.length > 3) return;
|
||||
|
||||
for (const { url } of links) {
|
||||
if (await handleChromiumReviewUnfurl(url, message_ts, channel, client)) return;
|
||||
if (await handleChromiumBugUnfurl(url, message_ts, channel, client)) return;
|
||||
if (await handleChromiumSourceUnfurl(url, message_ts, channel, client)) return;
|
||||
const linkUnfurls: Record<string, MessageAttachment> = {};
|
||||
|
||||
// Unfurl all the links at the same time
|
||||
await Promise.all(
|
||||
links.map(async ({ url }) => {
|
||||
const unfurls = await Promise.all([
|
||||
handleChromiumReviewUnfurl(url),
|
||||
handleChromiumBugUnfurl(url),
|
||||
handleChromiumSourceUnfurl(url),
|
||||
]);
|
||||
const validUnfurls = unfurls.filter((unfurl) => !!unfurl) as MessageAttachment[];
|
||||
if (validUnfurls.length > 1) {
|
||||
console.error('More than one unfurler responded to a given URL', { url });
|
||||
} else if (validUnfurls.length === 1) {
|
||||
linkUnfurls[url] = validUnfurls[0];
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
const unfurl = await client.chat.unfurl({
|
||||
channel,
|
||||
ts: message_ts,
|
||||
unfurls: linkUnfurls,
|
||||
});
|
||||
|
||||
if (!unfurl.ok) {
|
||||
console.error('Failed to unfurl', { unfurl, linkUnfurls });
|
||||
}
|
||||
});
|
||||
|
||||
app.start(process.env.PORT ? parseInt(process.env.PORT, 10) : 8080).then(() => {
|
||||
app.start(process.env.PORT ? parseInt(process.env.PORT, 10) : 8080).then((server) => {
|
||||
console.log('Chromium Unfurler listening...');
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче