mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-23 13:46:45 +02:00
fix: Change slackapp to using scheduleMessage (#4490)
This PR changes the slack-app addon to use slack-api's scheduleMessage instead of postMessage. When using postMessage we had to find the channel id in order to be able to post the message to the channel. scheduleMessage allows using the channel name instead of the id, which saves the entire struggle of finding the channel name. It did mean that we had to move to defining blocks of content instead of the easier formatting we did with postMessage. 
This commit is contained in:
parent
828b8804b1
commit
dc434be0a3
@ -105,7 +105,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@slack/web-api": "^6.8.1",
|
||||
"@slack/web-api": "^6.9.0",
|
||||
"@unleash/express-openapi": "^0.3.0",
|
||||
"ajv": "^8.11.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {
|
||||
WebClient,
|
||||
ConversationsListResponse,
|
||||
ErrorCode,
|
||||
WebClientEvent,
|
||||
CodedError,
|
||||
@ -13,7 +12,7 @@ import Addon from './addon';
|
||||
|
||||
import slackAppDefinition from './slack-app-definition';
|
||||
import { IAddonConfig } from '../types/model';
|
||||
|
||||
const SCHEDULE_MESSAGE_DELAY_IN_SECONDS = 10;
|
||||
import {
|
||||
FeatureEventFormatter,
|
||||
FeatureEventFormatterMd,
|
||||
@ -21,8 +20,6 @@ import {
|
||||
} from './feature-event-formatter-md';
|
||||
import { IEvent } from '../types/events';
|
||||
|
||||
const CACHE_SECONDS = 30;
|
||||
|
||||
interface ISlackAppAddonParameters {
|
||||
accessToken: string;
|
||||
defaultChannels: string;
|
||||
@ -35,17 +32,12 @@ export default class SlackAppAddon extends Addon {
|
||||
|
||||
private slackClient?: WebClient;
|
||||
|
||||
private slackChannels?: ConversationsListResponse['channels'];
|
||||
|
||||
private slackChannelsCacheTimeout?: NodeJS.Timeout;
|
||||
|
||||
constructor(args: IAddonConfig) {
|
||||
super(slackAppDefinition, args);
|
||||
this.msgFormatter = new FeatureEventFormatterMd(
|
||||
args.unleashUrl,
|
||||
LinkStyle.SLACK,
|
||||
);
|
||||
this.startCacheInvalidation();
|
||||
}
|
||||
|
||||
async handleEvent(
|
||||
@ -70,6 +62,7 @@ export default class SlackAppAddon extends Addon {
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.logger.debug(`Found candidate channels: ${eventChannels}.`);
|
||||
|
||||
if (!this.slackClient || this.accessToken !== accessToken) {
|
||||
const client = new WebClient(accessToken);
|
||||
@ -82,81 +75,42 @@ export default class SlackAppAddon extends Addon {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
if (!this.slackChannels) {
|
||||
const slackConversationsList =
|
||||
await this.slackClient.conversations.list({
|
||||
types: 'public_channel,private_channel',
|
||||
exclude_archived: true,
|
||||
limit: 200,
|
||||
});
|
||||
this.slackChannels = slackConversationsList.channels || [];
|
||||
let nextCursor =
|
||||
slackConversationsList.response_metadata?.next_cursor;
|
||||
while (nextCursor !== undefined && nextCursor !== '') {
|
||||
this.logger.debug('Fetching next page of channels');
|
||||
const moreChannels =
|
||||
await this.slackClient.conversations.list({
|
||||
cursor: nextCursor,
|
||||
types: 'public_channel,private_channel',
|
||||
exclude_archived: true,
|
||||
limit: 200,
|
||||
});
|
||||
const channels = moreChannels.channels;
|
||||
if (channels === undefined) {
|
||||
this.logger.debug(
|
||||
'Channels list was empty, breaking pagination',
|
||||
);
|
||||
nextCursor = undefined;
|
||||
break;
|
||||
}
|
||||
nextCursor = moreChannels.response_metadata?.next_cursor;
|
||||
this.logger.debug(
|
||||
`This page had ${channels.length} channels`,
|
||||
);
|
||||
|
||||
channels.forEach((channel) =>
|
||||
this.slackChannels?.push(channel),
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.debug(
|
||||
`Fetched ${this.slackChannels.length} Slack channels`,
|
||||
);
|
||||
}
|
||||
|
||||
const currentSlackChannels = [...this.slackChannels];
|
||||
if (!currentSlackChannels.length) {
|
||||
this.logger.warn('No Slack channels found.');
|
||||
return;
|
||||
}
|
||||
|
||||
const text = this.msgFormatter.format(event);
|
||||
const url = this.msgFormatter.featureLink(event);
|
||||
|
||||
const slackChannelsToPostTo = currentSlackChannels.filter(
|
||||
({ id, name }) => id && name && eventChannels.includes(name),
|
||||
);
|
||||
|
||||
const requests = slackChannelsToPostTo.map(({ id }) =>
|
||||
this.slackClient!.chat.postMessage({
|
||||
channel: id!,
|
||||
const requests = eventChannels.map((name) => {
|
||||
const now = Math.floor(new Date().getTime() / 1000);
|
||||
const postAt = now + SCHEDULE_MESSAGE_DELAY_IN_SECONDS;
|
||||
return this.slackClient!.chat.scheduleMessage({
|
||||
channel: name,
|
||||
text,
|
||||
attachments: [
|
||||
blocks: [
|
||||
{
|
||||
actions: [
|
||||
type: 'section',
|
||||
text: {
|
||||
type: 'mrkdwn',
|
||||
text,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'actions',
|
||||
block_id: url,
|
||||
elements: [
|
||||
{
|
||||
name: 'featureToggle',
|
||||
text: 'Open in Unleash',
|
||||
type: 'button',
|
||||
url,
|
||||
text: {
|
||||
type: 'plain_text',
|
||||
text: 'Open in Unleash',
|
||||
},
|
||||
value: 'featureToggle',
|
||||
style: 'primary',
|
||||
url,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
post_at: postAt,
|
||||
});
|
||||
});
|
||||
|
||||
const results = await Promise.allSettled(requests);
|
||||
|
||||
@ -193,12 +147,6 @@ export default class SlackAppAddon extends Addon {
|
||||
return [];
|
||||
}
|
||||
|
||||
startCacheInvalidation(): void {
|
||||
this.slackChannelsCacheTimeout = setInterval(() => {
|
||||
this.slackChannels = undefined;
|
||||
}, CACHE_SECONDS * 1000);
|
||||
}
|
||||
|
||||
logError(event: IEvent, error: Error | CodedError): void {
|
||||
if (!('code' in error)) {
|
||||
this.logger.warn(`Error handling event ${event.type}.`, error);
|
||||
@ -233,13 +181,6 @@ export default class SlackAppAddon extends Addon {
|
||||
this.logger.warn(`Error handling event ${event.type}.`, error);
|
||||
}
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
if (this.slackChannelsCacheTimeout) {
|
||||
clearInterval(this.slackChannelsCacheTimeout);
|
||||
this.slackChannelsCacheTimeout = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SlackAppAddon;
|
||||
|
22
yarn.lock
22
yarn.lock
@ -1063,24 +1063,24 @@
|
||||
dependencies:
|
||||
"@types/node" ">=12.0.0"
|
||||
|
||||
"@slack/types@^2.0.0":
|
||||
"@slack/types@^2.8.0":
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@slack/types/-/types-2.8.0.tgz#11ea10872262a7e6f86f54e5bcd4f91e3a41fe91"
|
||||
integrity sha512-ghdfZSF0b4NC9ckBA8QnQgC9DJw2ZceDq0BIjjRSv6XAZBXJdWgxIsYz0TYnWSiqsKZGH2ZXbj9jYABZdH3OSQ==
|
||||
|
||||
"@slack/web-api@^6.8.1":
|
||||
version "6.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.8.1.tgz#c6c1e7405c884c4d9048f8b1d3901bd138d00610"
|
||||
integrity sha512-eMPk2S99S613gcu7odSw/LV+Qxr8A+RXvBD0GYW510wJuTERiTjP5TgCsH8X09+lxSumbDE88wvWbuFuvGa74g==
|
||||
"@slack/web-api@^6.9.0":
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.9.0.tgz#d829dcfef490dbce8e338912706b6f39dcde3ad2"
|
||||
integrity sha512-RME5/F+jvQmZHkoP+ogrDbixq1Ms1mBmylzuWq4sf3f7GCpMPWoiZ+WqWk+sism3vrlveKWIgO9R4Qg9fiRyoQ==
|
||||
dependencies:
|
||||
"@slack/logger" "^3.0.0"
|
||||
"@slack/types" "^2.0.0"
|
||||
"@slack/types" "^2.8.0"
|
||||
"@types/is-stream" "^1.1.0"
|
||||
"@types/node" ">=12.0.0"
|
||||
axios "^0.27.2"
|
||||
eventemitter3 "^3.1.0"
|
||||
form-data "^2.5.0"
|
||||
is-electron "2.2.0"
|
||||
is-electron "2.2.2"
|
||||
is-stream "^1.1.0"
|
||||
p-queue "^6.6.1"
|
||||
p-retry "^4.0.0"
|
||||
@ -4211,10 +4211,10 @@ is-date-object@^1.0.1:
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-electron@2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.0.tgz#8943084f09e8b731b3a7a0298a7b5d56f6b7eef0"
|
||||
integrity sha512-SpMppC2XR3YdxSzczXReBjqs2zGscWQpBIKqwXYBFic0ERaxNVgwLCHwOLZeESfdJQjX0RDvrJ1lBXX2ij+G1Q==
|
||||
is-electron@2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.2.tgz#3778902a2044d76de98036f5dc58089ac4d80bb9"
|
||||
integrity sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==
|
||||
|
||||
is-extendable@^0.1.1:
|
||||
version "0.1.1"
|
||||
|
Loading…
Reference in New Issue
Block a user