@@ -7,9 +7,11 @@ import axios, { type AxiosInstance, type AxiosResponse } from 'axios';
77import type { Assistant } from './Assistant' ;
88import {
99 CustomFunction ,
10- type CustomFunctionMiddleware ,
1110 type FunctionCompleteFn ,
1211 type FunctionFailFn ,
12+ type SlackCustomFunctionMiddlewareArgs ,
13+ createFunctionComplete ,
14+ createFunctionFail ,
1315} from './CustomFunction' ;
1416import type { WorkflowStep } from './WorkflowStep' ;
1517import { type ConversationStore , MemoryStore , conversationContext } from './conversation-store' ;
@@ -529,10 +531,9 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
529531 /**
530532 * Register CustomFunction middleware
531533 */
532- public function ( callbackId : string , ...listeners : CustomFunctionMiddleware ) : this {
533- const fn = new CustomFunction ( callbackId , listeners , this . webClientOptions ) ;
534- const m = fn . getMiddleware ( ) ;
535- this . middleware . push ( m ) ;
534+ public function ( callbackId : string , ...listeners : Middleware < SlackCustomFunctionMiddlewareArgs > [ ] ) : this {
535+ const fn = new CustomFunction ( callbackId , listeners ) ;
536+ this . listeners . push ( fn . getListeners ( ) ) ;
536537 return this ;
537538 }
538539
@@ -978,8 +979,9 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
978979 }
979980
980981 // Factory for say() utility
982+ // TODO: could this be move out of processEvent, use the same token from below or perhaps even a client from the pool
981983 const createSay = ( channelId : string ) : SayFn => {
982- const token = selectToken ( context ) ;
984+ const token = selectToken ( context , this . attachFunctionToken ) ;
983985 return ( message ) => {
984986 let postMessageArguments : ChatPostMessageArguments ;
985987 if ( typeof message === 'string' ) {
@@ -1049,6 +1051,37 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
10491051 payload,
10501052 } ;
10511053
1054+ // Get the client arg
1055+ let { client } = this ;
1056+
1057+ const token = selectToken ( context , this . attachFunctionToken ) ;
1058+
1059+ // TODO: this logic should be isolated and tested according to the expected behavior
1060+ if ( token !== undefined ) {
1061+ let pool : WebClientPool | undefined = undefined ;
1062+ const clientOptionsCopy = { ...this . clientOptions } ;
1063+ if ( authorizeResult . teamId !== undefined ) {
1064+ pool = this . clients [ authorizeResult . teamId ] ;
1065+ if ( pool === undefined ) {
1066+ pool = this . clients [ authorizeResult . teamId ] = new WebClientPool ( ) ;
1067+ }
1068+ // Add teamId to clientOptions so it can be automatically added to web-api calls
1069+ clientOptionsCopy . teamId = authorizeResult . teamId ;
1070+ } else if ( authorizeResult . enterpriseId !== undefined ) {
1071+ pool = this . clients [ authorizeResult . enterpriseId ] ;
1072+ if ( pool === undefined ) {
1073+ pool = this . clients [ authorizeResult . enterpriseId ] = new WebClientPool ( ) ;
1074+ }
1075+ }
1076+
1077+ if ( this . attachFunctionToken && context . functionBotAccessToken ) {
1078+ // workflow tokens are always unique, they should not be added to the pool
1079+ client = new WebClient ( token , clientOptionsCopy ) ;
1080+ } else if ( pool !== undefined ) {
1081+ client = pool . getOrCreate ( token , clientOptionsCopy ) ;
1082+ }
1083+ }
1084+
10521085 // TODO: can we instead use type predicates in these switch cases to allow for narrowing of the body simultaneously? we have isEvent, isView, isShortcut, isAction already in types/utilities / helpers
10531086 // Set aliases
10541087 if ( type === IncomingEventType . Event ) {
@@ -1058,9 +1091,21 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
10581091 const messageEventListenerArgs = eventListenerArgs as SlackEventMiddlewareArgs < 'message' > ;
10591092 messageEventListenerArgs . message = messageEventListenerArgs . payload ;
10601093 }
1094+ // Add complete() and fail() utilities for function-related interactivity
1095+ if ( eventListenerArgs . event . type === 'function_executed' ) {
1096+ listenerArgs . complete = createFunctionComplete ( context , client ) ;
1097+ listenerArgs . fail = createFunctionFail ( context , client ) ;
1098+ listenerArgs . inputs = eventListenerArgs . event . inputs ;
1099+ }
10611100 } else if ( type === IncomingEventType . Action ) {
10621101 const actionListenerArgs = listenerArgs as SlackActionMiddlewareArgs ;
10631102 actionListenerArgs . action = actionListenerArgs . payload ;
1103+ // Add complete() and fail() utilities for function-related interactivity
1104+ if ( context . functionExecutionId !== undefined ) {
1105+ listenerArgs . complete = createFunctionComplete ( context , client ) ;
1106+ listenerArgs . fail = createFunctionFail ( context , client ) ;
1107+ listenerArgs . inputs = context . functionInputs ;
1108+ }
10641109 } else if ( type === IncomingEventType . Command ) {
10651110 const commandListenerArgs = listenerArgs as SlackCommandMiddlewareArgs ;
10661111 commandListenerArgs . command = commandListenerArgs . payload ;
@@ -1096,42 +1141,6 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
10961141 await ack ( ) ;
10971142 }
10981143
1099- // Get the client arg
1100- let { client } = this ;
1101-
1102- // If functionBotAccessToken exists on context, the incoming event is function-related *and* the
1103- // user has `attachFunctionToken` enabled. In that case, subsequent calls with the client should
1104- // use the function-related/JIT token in lieu of the botToken or userToken.
1105- const token = context . functionBotAccessToken ? context . functionBotAccessToken : selectToken ( context ) ;
1106-
1107- // Add complete() and fail() utilities for function-related interactivity
1108- if ( type === IncomingEventType . Action && context . functionExecutionId !== undefined ) {
1109- listenerArgs . complete = CustomFunction . createFunctionComplete ( context , client ) ;
1110- listenerArgs . fail = CustomFunction . createFunctionFail ( context , client ) ;
1111- listenerArgs . inputs = context . functionInputs ;
1112- }
1113-
1114- if ( token !== undefined ) {
1115- let pool : WebClientPool | undefined = undefined ;
1116- const clientOptionsCopy = { ...this . clientOptions } ;
1117- if ( authorizeResult . teamId !== undefined ) {
1118- pool = this . clients [ authorizeResult . teamId ] ;
1119- if ( pool === undefined ) {
1120- pool = this . clients [ authorizeResult . teamId ] = new WebClientPool ( ) ;
1121- }
1122- // Add teamId to clientOptions so it can be automatically added to web-api calls
1123- clientOptionsCopy . teamId = authorizeResult . teamId ;
1124- } else if ( authorizeResult . enterpriseId !== undefined ) {
1125- pool = this . clients [ authorizeResult . enterpriseId ] ;
1126- if ( pool === undefined ) {
1127- pool = this . clients [ authorizeResult . enterpriseId ] = new WebClientPool ( ) ;
1128- }
1129- }
1130- if ( pool !== undefined ) {
1131- client = pool . getOrCreate ( token , clientOptionsCopy ) ;
1132- }
1133- }
1134-
11351144 // Dispatch event through the global middleware chain
11361145 try {
11371146 await processMiddleware (
@@ -1574,8 +1583,11 @@ function isBlockActionOrInteractiveMessageBody(
15741583 return ( body as SlackActionMiddlewareArgs < BlockAction | InteractiveMessage > [ 'body' ] ) . actions !== undefined ;
15751584}
15761585
1577- // Returns either a bot token or a user token for client, say()
1578- function selectToken ( context : Context ) : string | undefined {
1586+ // Returns either a bot token, a user token or a workflow token for client, say()
1587+ function selectToken ( context : Context , attachFunctionToken : boolean ) : string | undefined {
1588+ if ( attachFunctionToken && context . functionBotAccessToken ) {
1589+ return context . functionBotAccessToken ;
1590+ }
15791591 return context . botToken !== undefined ? context . botToken : context . userToken ;
15801592}
15811593
0 commit comments