mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
fix: Change slackapp to using scheduleMessage (#4490)
### What This PR changes the slack-app addon to use slack-api's scheduleMessage instead of postMessage. ### Why 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. ### Message look 
This commit is contained in:
parent
3227e30f12
commit
4ad370450d
@ -104,7 +104,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(
|
||||
@ -83,95 +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
|
||||
} available Slack channels: ${this.slackChannels.map(
|
||||
({ name }) => name,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const currentSlackChannels = [...this.slackChannels];
|
||||
if (!currentSlackChannels.length) {
|
||||
this.logger.warn('No available 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),
|
||||
);
|
||||
|
||||
if (!slackChannelsToPostTo.length) {
|
||||
this.logger.info('No eligible Slack channel found.');
|
||||
return;
|
||||
}
|
||||
this.logger.debug(
|
||||
`Posting event to ${slackChannelsToPostTo.map(
|
||||
({ name }) => 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);
|
||||
|
||||
@ -208,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);
|
||||
@ -248,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
@ -1068,24 +1068,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"
|
||||
@ -4359,10 +4359,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