From e805c4e32133802499f3731b6983549c904be88a Mon Sep 17 00:00:00 2001 From: Alissa Renz Date: Fri, 5 Dec 2025 12:16:44 -0800 Subject: [PATCH 1/2] Add missing SocketMode options --- src/App.ts | 121 ++++++++++++++++------------ src/receivers/SocketModeReceiver.ts | 50 +++++++----- 2 files changed, 102 insertions(+), 69 deletions(-) diff --git a/src/App.ts b/src/App.ts index a902f40b3..5774c405a 100644 --- a/src/App.ts +++ b/src/App.ts @@ -2,6 +2,7 @@ import type { Agent } from 'node:http'; import type { SecureContextOptions } from 'node:tls'; import util from 'node:util'; import { ConsoleLogger, LogLevel, type Logger } from '@slack/logger'; +import type { SocketModeOptions } from '@slack/socket-mode'; import { type ChatPostMessageArguments, WebClient, type WebClientOptions, addAppMetadata } from '@slack/web-api'; import axios, { type AxiosInstance, type AxiosResponse } from 'axios'; import type { Assistant } from './Assistant'; @@ -106,43 +107,47 @@ const tokenUsage = /** App initialization options */ export interface AppOptions { - signingSecret?: HTTPReceiverOptions['signingSecret']; - endpoints?: HTTPReceiverOptions['endpoints']; - port?: HTTPReceiverOptions['port']; - customRoutes?: HTTPReceiverOptions['customRoutes']; - processBeforeResponse?: HTTPReceiverOptions['processBeforeResponse']; - signatureVerification?: HTTPReceiverOptions['signatureVerification']; - clientId?: HTTPReceiverOptions['clientId']; - clientSecret?: HTTPReceiverOptions['clientSecret']; - stateSecret?: HTTPReceiverOptions['stateSecret']; // required when using default stateStore - redirectUri?: HTTPReceiverOptions['redirectUri']; - installationStore?: HTTPReceiverOptions['installationStore']; // default MemoryInstallationStore - scopes?: HTTPReceiverOptions['scopes']; - installerOptions?: HTTPReceiverOptions['installerOptions']; agent?: Agent; - clientTls?: Pick; - convoStore?: ConversationStore | false; - token?: AuthorizeResult['botToken']; // either token or authorize appToken?: string; // TODO should this be included in AuthorizeResult + attachFunctionToken?: boolean; + authorize?: Authorize; // either token or authorize + autoReconnectEnabled?: SocketModeOptions['autoReconnectEnabled']; botId?: AuthorizeResult['botId']; // only used when authorize is not defined, shortcut for fetching botUserId?: AuthorizeResult['botUserId']; // only used when authorize is not defined, shortcut for fetching - authorize?: Authorize; // either token or authorize - receiver?: Receiver; - logger?: Logger; - logLevel?: LogLevel; - ignoreSelf?: boolean; + clientId?: HTTPReceiverOptions['clientId']; /** * Configurations for the web client used to send Slack API method requests. * * See {@link https://tools.slack.dev/node-slack-sdk/reference/web-api/interfaces/WebClientOptions} for more information. */ clientOptions?: WebClientOptions; - socketMode?: boolean; - developerMode?: boolean; - tokenVerificationEnabled?: boolean; + clientPingTimeout?: SocketModeOptions['clientPingTimeout']; + clientSecret?: HTTPReceiverOptions['clientSecret']; + clientTls?: Pick; + convoStore?: ConversationStore | false; + customRoutes?: HTTPReceiverOptions['customRoutes']; deferInitialization?: boolean; + developerMode?: boolean; + endpoints?: HTTPReceiverOptions['endpoints']; extendedErrorHandler?: boolean; - attachFunctionToken?: boolean; + ignoreSelf?: boolean; + installationStore?: HTTPReceiverOptions['installationStore']; // default MemoryInstallationStore + installerOptions?: HTTPReceiverOptions['installerOptions']; + logger?: Logger; + logLevel?: LogLevel; + pingPongLoggingEnabled?: SocketModeOptions['pingPongLoggingEnabled']; + port?: HTTPReceiverOptions['port']; + processBeforeResponse?: HTTPReceiverOptions['processBeforeResponse']; + receiver?: Receiver; + redirectUri?: HTTPReceiverOptions['redirectUri']; + scopes?: HTTPReceiverOptions['scopes']; + serverPingTimeout?: SocketModeOptions['serverPingTimeout']; + signatureVerification?: HTTPReceiverOptions['signatureVerification']; + signingSecret?: HTTPReceiverOptions['signingSecret']; + socketMode?: boolean; + stateSecret?: HTTPReceiverOptions['stateSecret']; // required when using default stateStore + token?: AuthorizeResult['botToken']; // either token or authorize + tokenVerificationEnabled?: boolean; } export { LogLevel, Logger } from '@slack/logger'; @@ -275,38 +280,42 @@ export default class App private attachFunctionToken: boolean; public constructor({ - signingSecret = undefined, - endpoints = undefined, - port = undefined, - customRoutes = undefined, + authorize = undefined, agent = undefined, - clientTls = undefined, - receiver = undefined, - convoStore = undefined, - token = undefined, appToken = undefined, + attachFunctionToken = true, + autoReconnectEnabled = undefined, botId = undefined, botUserId = undefined, - authorize = undefined, + clientId = undefined, + clientOptions = undefined, + clientPingTimeout = undefined, + clientSecret = undefined, + clientTls = undefined, + convoStore = undefined, + customRoutes = undefined, + deferInitialization = false, + developerMode = false, + endpoints = undefined, + extendedErrorHandler = false, + ignoreSelf = true, + installationStore = undefined, + installerOptions = undefined, logger = undefined, logLevel = undefined, - ignoreSelf = true, - clientOptions = undefined, processBeforeResponse = false, - signatureVerification = true, - clientId = undefined, - clientSecret = undefined, - stateSecret = undefined, + port = undefined, + receiver = undefined, + token = undefined, + pingPongLoggingEnabled = undefined, redirectUri = undefined, - installationStore = undefined, + serverPingTimeout = undefined, scopes = undefined, - installerOptions = undefined, + signatureVerification = true, + signingSecret = undefined, socketMode = undefined, - developerMode = false, + stateSecret = undefined, tokenVerificationEnabled = true, - extendedErrorHandler = false, - deferInitialization = false, - attachFunctionToken = true, }: AppOptions = {}) { /* ------------------------ Developer mode ----------------------------- */ this.developerMode = developerMode; @@ -420,6 +429,10 @@ export default class App scopes, appToken, logger, + autoReconnectEnabled, + clientPingTimeout, + pingPongLoggingEnabled, + serverPingTimeout, ); /* ------------------------ Set authorize ----------------------------- */ @@ -1267,6 +1280,10 @@ export default class App scopes?: HTTPReceiverOptions['scopes'], appToken?: string, logger?: Logger, + autoReconnectEnabled?: SocketModeOptions['autoReconnectEnabled'], + clientPingTimeout?: SocketModeOptions['clientPingTimeout'], + pingPongLoggingEnabled?: SocketModeOptions['pingPongLoggingEnabled'], + serverPingTimeout?: SocketModeOptions['serverPingTimeout'], ): Receiver { if (receiver !== undefined) { // Custom receiver supplied @@ -1284,16 +1301,20 @@ export default class App this.logger.debug('Initializing SocketModeReceiver'); return new SocketModeReceiver({ appToken, + autoReconnectEnabled, clientId, + clientPingTimeout, clientSecret, - stateSecret, - redirectUri, + customRoutes, + installerOptions: this.installerOptions, installationStore, + pingPongLoggingEnabled, + redirectUri, + serverPingTimeout, + stateSecret, scopes, logger, logLevel: this.logLevel, - installerOptions: this.installerOptions, - customRoutes, }); } if (signatureVerification === true && signingSecret === undefined) { diff --git a/src/receivers/SocketModeReceiver.ts b/src/receivers/SocketModeReceiver.ts index be586f3b9..0edafe3a3 100644 --- a/src/receivers/SocketModeReceiver.ts +++ b/src/receivers/SocketModeReceiver.ts @@ -28,20 +28,24 @@ import { verifyRedirectOpts } from './verify-redirect-opts'; // TODO: we throw away the key names for endpoints, so maybe we should use this interface. is it better for migrations? // if that's the reason, let's document that with a comment. export interface SocketModeReceiverOptions { - logger?: Logger; - logLevel?: LogLevel; + appToken: string; // App Level Token + autoReconnectEnabled?: boolean; clientId?: string; + clientPingTimeout?: number; clientSecret?: string; - stateSecret?: InstallProviderOptions['stateSecret']; // required when using default stateStore - redirectUri?: string; - installationStore?: InstallProviderOptions['installationStore']; // default MemoryInstallationStore - scopes?: InstallURLOptions['scopes']; - installerOptions?: InstallerOptions; - appToken: string; // App Level Token - customRoutes?: CustomRoute[]; // biome-ignore lint/suspicious/noExplicitAny: user-provided custom properties can be anything customPropertiesExtractor?: (args: any) => StringIndexed; + customRoutes?: CustomRoute[]; + installationStore?: InstallProviderOptions['installationStore']; // default MemoryInstallationStore + installerOptions?: InstallerOptions; + logger?: Logger; + logLevel?: LogLevel; + pingPongLoggingEnabled?: boolean; processEventErrorHandler?: (args: SocketModeReceiverProcessEventErrorHandlerArgs) => Promise; + redirectUri?: string; + scopes?: InstallURLOptions['scopes']; + serverPingTimeout?: number; + stateSecret?: InstallProviderOptions['stateSecret']; // required when using default stateStore } export interface CustomRoute { @@ -94,24 +98,32 @@ export default class SocketModeReceiver implements Receiver { public constructor({ appToken, - logger = undefined, - logLevel = LogLevel.INFO, + autoReconnectEnabled = false, clientId = undefined, + clientPingTimeout = undefined, clientSecret = undefined, - stateSecret = undefined, - redirectUri = undefined, - installationStore = undefined, - scopes = undefined, - installerOptions = {}, - customRoutes = [], customPropertiesExtractor = (_args) => ({}), + customRoutes = [], + installerOptions = {}, + installationStore = undefined, + logger = undefined, + logLevel = LogLevel.INFO, + pingPongLoggingEnabled = false, processEventErrorHandler = defaultProcessEventErrorHandler, + redirectUri = undefined, + scopes = undefined, + serverPingTimeout = undefined, + stateSecret = undefined, }: SocketModeReceiverOptions) { this.client = new SocketModeClient({ appToken, - logLevel, - logger, + autoReconnectEnabled, clientOptions: installerOptions.clientOptions, + clientPingTimeout, + logger, + logLevel, + pingPongLoggingEnabled, + serverPingTimeout, }); this.logger = From 4089a3f837c9438cba4df27e58de8003ea329851 Mon Sep 17 00:00:00 2001 From: Alissa Renz Date: Tue, 9 Dec 2025 11:59:29 -0800 Subject: [PATCH 2/2] Bundle SocketModeOptions into single prop --- src/App.ts | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/App.ts b/src/App.ts index 5774c405a..73757fb05 100644 --- a/src/App.ts +++ b/src/App.ts @@ -111,7 +111,6 @@ export interface AppOptions { appToken?: string; // TODO should this be included in AuthorizeResult attachFunctionToken?: boolean; authorize?: Authorize; // either token or authorize - autoReconnectEnabled?: SocketModeOptions['autoReconnectEnabled']; botId?: AuthorizeResult['botId']; // only used when authorize is not defined, shortcut for fetching botUserId?: AuthorizeResult['botUserId']; // only used when authorize is not defined, shortcut for fetching clientId?: HTTPReceiverOptions['clientId']; @@ -121,7 +120,6 @@ export interface AppOptions { * See {@link https://tools.slack.dev/node-slack-sdk/reference/web-api/interfaces/WebClientOptions} for more information. */ clientOptions?: WebClientOptions; - clientPingTimeout?: SocketModeOptions['clientPingTimeout']; clientSecret?: HTTPReceiverOptions['clientSecret']; clientTls?: Pick; convoStore?: ConversationStore | false; @@ -135,13 +133,12 @@ export interface AppOptions { installerOptions?: HTTPReceiverOptions['installerOptions']; logger?: Logger; logLevel?: LogLevel; - pingPongLoggingEnabled?: SocketModeOptions['pingPongLoggingEnabled']; port?: HTTPReceiverOptions['port']; processBeforeResponse?: HTTPReceiverOptions['processBeforeResponse']; receiver?: Receiver; redirectUri?: HTTPReceiverOptions['redirectUri']; scopes?: HTTPReceiverOptions['scopes']; - serverPingTimeout?: SocketModeOptions['serverPingTimeout']; + socketModeOptions?: Omit; signatureVerification?: HTTPReceiverOptions['signatureVerification']; signingSecret?: HTTPReceiverOptions['signingSecret']; socketMode?: boolean; @@ -284,12 +281,10 @@ export default class App agent = undefined, appToken = undefined, attachFunctionToken = true, - autoReconnectEnabled = undefined, botId = undefined, botUserId = undefined, clientId = undefined, clientOptions = undefined, - clientPingTimeout = undefined, clientSecret = undefined, clientTls = undefined, convoStore = undefined, @@ -307,13 +302,12 @@ export default class App port = undefined, receiver = undefined, token = undefined, - pingPongLoggingEnabled = undefined, redirectUri = undefined, - serverPingTimeout = undefined, scopes = undefined, signatureVerification = true, signingSecret = undefined, socketMode = undefined, + socketModeOptions = undefined, stateSecret = undefined, tokenVerificationEnabled = true, }: AppOptions = {}) { @@ -429,10 +423,7 @@ export default class App scopes, appToken, logger, - autoReconnectEnabled, - clientPingTimeout, - pingPongLoggingEnabled, - serverPingTimeout, + socketModeOptions, ); /* ------------------------ Set authorize ----------------------------- */ @@ -1280,10 +1271,7 @@ export default class App scopes?: HTTPReceiverOptions['scopes'], appToken?: string, logger?: Logger, - autoReconnectEnabled?: SocketModeOptions['autoReconnectEnabled'], - clientPingTimeout?: SocketModeOptions['clientPingTimeout'], - pingPongLoggingEnabled?: SocketModeOptions['pingPongLoggingEnabled'], - serverPingTimeout?: SocketModeOptions['serverPingTimeout'], + socketModeOptions?: Omit, ): Receiver { if (receiver !== undefined) { // Custom receiver supplied @@ -1301,16 +1289,13 @@ export default class App this.logger.debug('Initializing SocketModeReceiver'); return new SocketModeReceiver({ appToken, - autoReconnectEnabled, clientId, - clientPingTimeout, clientSecret, customRoutes, installerOptions: this.installerOptions, installationStore, - pingPongLoggingEnabled, redirectUri, - serverPingTimeout, + ...socketModeOptions, stateSecret, scopes, logger,