From 9d2fa584ff2af308386da69d7282730376fb0250 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 2 Oct 2024 14:25:45 -0400 Subject: [PATCH] socket-mode(fix): do not handle message events twice (#2059) --- packages/socket-mode/package.json | 4 +- packages/socket-mode/src/SlackWebSocket.ts | 2 +- .../socket-mode/src/SocketModeClient.spec.ts | 80 ++++++++++++++++--- packages/socket-mode/src/SocketModeClient.ts | 2 +- packages/socket-mode/tsconfig.json | 8 +- 5 files changed, 75 insertions(+), 21 deletions(-) diff --git a/packages/socket-mode/package.json b/packages/socket-mode/package.json index f9f82b33c..5ed9ffc8e 100644 --- a/packages/socket-mode/package.json +++ b/packages/socket-mode/package.json @@ -20,9 +20,7 @@ ], "main": "dist/src/index.js", "types": "./dist/src/index.d.ts", - "files": [ - "dist/**/*" - ], + "files": ["dist/**/*"], "engines": { "node": ">= 18", "npm": ">= 8.6.0" diff --git a/packages/socket-mode/src/SlackWebSocket.ts b/packages/socket-mode/src/SlackWebSocket.ts index 42af60ee1..0c1412b1f 100644 --- a/packages/socket-mode/src/SlackWebSocket.ts +++ b/packages/socket-mode/src/SlackWebSocket.ts @@ -124,7 +124,7 @@ export class SlackWebSocket { this.options.client.emit('error', websocketErrorWithOriginal(event.error)); }); ws.on('message', (msg, isBinary) => { - this.options.client.emit('message', msg, isBinary); + this.options.client.emit('ws_message', msg, isBinary); }); ws.on('close', (code: number, data: Buffer) => { this.logger.debug(`WebSocket close frame received (code: ${code}, reason: ${data.toString()})`); diff --git a/packages/socket-mode/src/SocketModeClient.spec.ts b/packages/socket-mode/src/SocketModeClient.spec.ts index 09e68bb13..19229898d 100644 --- a/packages/socket-mode/src/SocketModeClient.spec.ts +++ b/packages/socket-mode/src/SocketModeClient.spec.ts @@ -77,7 +77,7 @@ describe('SocketModeClient', () => { args.retry_num === undefined && args.retry_reason === undefined; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', message, false /* isBinary */); await sleep(30); assert.isTrue(commandListenerCalled); assert.isTrue(slackEventListenerCalled); @@ -89,7 +89,7 @@ describe('SocketModeClient', () => { client.on('slash_commands', async ({ envelope_id }) => { passedEnvelopeId = envelope_id; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', message, false /* isBinary */); await sleep(30); assert.equal(passedEnvelopeId, envelopeId); }); @@ -99,7 +99,7 @@ describe('SocketModeClient', () => { client.on('slack_event', async ({ envelope_id }) => { passedEnvelopeId = envelope_id; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', message, false /* isBinary */); await sleep(30); assert.equal(passedEnvelopeId, envelopeId); }); @@ -107,7 +107,7 @@ describe('SocketModeClient', () => { describe('events_api messages', () => { const envelopeId = 'cda4159a-72a5-4744-aba3-4d66eb52682b'; - const message = JSON.stringify({ + const appMention = JSON.stringify({ envelope_id: envelopeId, payload: { token: 'verification-token', @@ -160,6 +160,59 @@ describe('SocketModeClient', () => { retry_attempt: 2, retry_reason: 'timeout', }); + const message = JSON.stringify({ + envelope_id: envelopeId, + payload: { + token: 'verification-token', + team_id: 'T111', + api_app_id: 'A111', + event: { + client_msg_id: 'f0582a78-72db-4feb-b2f3-1e47d66365c8', + type: 'message', + text: '<@U111>', + user: 'U222', + ts: '1610241741.000200', + team: 'T111', + blocks: [ + { + type: 'rich_text', + block_id: 'Sesm', + elements: [ + { + type: 'rich_text_section', + elements: [ + { + type: 'user', + user_id: 'U111', + }, + ], + }, + ], + }, + ], + channel: 'C111', + event_ts: '1610241741.000200', + }, + type: 'event_callback', + event_id: 'Ev111', + event_time: 1610241741, + authorizations: [ + { + enterprise_id: null, + team_id: 'T111', + user_id: 'U222', + is_bot: true, + is_enterprise_install: false, + }, + ], + is_ext_shared_channel: false, + event_context: '1-app_mention-T111-C111', + }, + type: 'events_api', + accepts_response_payload: false, + retry_attempt: 2, + retry_reason: 'timeout', + }); it('should be sent to the specific and generic event listeners, and should not trip an unrelated event listener', async () => { const client = new SocketModeClient({ appToken: 'xapp-' }); @@ -183,7 +236,7 @@ describe('SocketModeClient', () => { args.retry_num === 2 && args.retry_reason === 'timeout'; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', appMention, false /* isBinary */); await sleep(30); assert.isFalse(otherListenerCalled); assert.isTrue(eventsApiListenerCalled); @@ -196,7 +249,7 @@ describe('SocketModeClient', () => { client.on('app_mention', async ({ envelope_id }) => { passedEnvelopeId = envelope_id; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', appMention, false /* isBinary */); await sleep(30); assert.equal(passedEnvelopeId, envelopeId); }); @@ -206,10 +259,17 @@ describe('SocketModeClient', () => { client.on('slack_event', async ({ envelope_id }) => { passedEnvelopeId = envelope_id; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', appMention, false /* isBinary */); await sleep(30); assert.equal(passedEnvelopeId, envelopeId); }); + it('should process message events once', async () => { + const client = new SocketModeClient({ appToken: 'xapp-' }); + const spy = sinon.spy(); + client.on('message', spy); + client.emit('ws_message', message, false /* isBinary */); + sinon.assert.callCount(spy, 1); + }); }); describe('interactivity messages', () => { @@ -252,7 +312,7 @@ describe('SocketModeClient', () => { client.on('slack_event', async (args) => { slackEventListenerCalled = args.ack !== undefined && args.body !== undefined; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', message, false /* isBinary */); await sleep(30); assert.isFalse(otherListenerCalled); assert.isTrue(interactiveListenerCalled); @@ -265,7 +325,7 @@ describe('SocketModeClient', () => { client.on('interactive', async ({ envelope_id }) => { passedEnvelopeId = envelope_id; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', message, false /* isBinary */); await sleep(30); assert.equal(passedEnvelopeId, envelopeId); }); @@ -275,7 +335,7 @@ describe('SocketModeClient', () => { client.on('slack_event', async ({ envelope_id }) => { passedEnvelopeId = envelope_id; }); - client.emit('message', message, false /* isBinary */); + client.emit('ws_message', message, false /* isBinary */); await sleep(30); assert.equal(passedEnvelopeId, envelopeId); }); diff --git a/packages/socket-mode/src/SocketModeClient.ts b/packages/socket-mode/src/SocketModeClient.ts index 09df604bf..08c31a678 100644 --- a/packages/socket-mode/src/SocketModeClient.ts +++ b/packages/socket-mode/src/SocketModeClient.ts @@ -147,7 +147,7 @@ export class SocketModeClient extends EventEmitter { this.emit(State.Disconnected); } }); - this.on('message', this.onWebSocketMessage.bind(this)); + this.on('ws_message', this.onWebSocketMessage.bind(this)); this.logger.debug('The Socket Mode client has successfully initialized'); } diff --git a/packages/socket-mode/tsconfig.json b/packages/socket-mode/tsconfig.json index 4e01adcd7..a586a02ab 100644 --- a/packages/socket-mode/tsconfig.json +++ b/packages/socket-mode/tsconfig.json @@ -13,12 +13,8 @@ "sourceMap": true }, "extends": "@tsconfig/recommended/tsconfig.json", - "include": [ - "src/**/*" - ], - "exclude": [ - "src/**/*.spec.*" - ], + "include": ["src/**/*"], + "exclude": ["src/**/*.spec.*"], "jsdoc": { "out": "support/jsdoc", "access": "public"