Skip to content

Commit

Permalink
Merge branch 'main' into docs-helmendorf-assistant-tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
haleychaas authored Nov 14, 2024
2 parents a28e1ba + 5d279da commit 7b23d00
Show file tree
Hide file tree
Showing 17 changed files with 524 additions and 570 deletions.
39 changes: 20 additions & 19 deletions docs/src/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ table tr:nth-child(even) {
}

/* changing the links to blue for accessibility */
p a, .markdown a {
p a,
.markdown a {
color: var(--slack-cloud-blue);
}

Expand Down Expand Up @@ -53,7 +54,7 @@ a:hover {
/* removing ToC line */
.table-of-contents__left-border {
border-left: none !important;
}
}

/* increasing name of SDK in sidebar */
.sidebar-title {
Expand All @@ -64,8 +65,8 @@ a:hover {

/* removing sidebar line and adding space to match ToC */
.theme-doc-sidebar-container {
border-right: none !important;
margin-right: 2rem;
border-right: none !important;
margin-right: 2rem;
}

/* announcement bar up top */
Expand Down Expand Up @@ -112,46 +113,46 @@ html[data-theme="dark"] .navbar-github-link::before {

/* Docs code bubbles */
[data-theme="light"] {
--code-link-background: #CFE9FE;
--code-link-text: rgb(21, 50, 59);
--code-link-background: #cfe9fe;
--code-link-text: rgb(21, 50, 59);

--method-link-background: #CDEFC4;
--method-link-background: #cdefc4;
--method-link-text: rgb(0, 41, 0);

--scope-link-background: #FBF3E0;
--scope-link-background: #fbf3e0;
--scope-link-text: rgb(63, 46, 0);

--event-link-background: #FDDDE3;
--event-link-text: rgb(74, 21, 75);
--event-link-background: #fddde3;
--event-link-text: rgb(74, 21, 75);
}

[data-theme="dark"] {
--code-link-text: white;
--method-link-text: white;
--scope-link-text: white;
--event-link-text: white;
--code-link-background: #1AB9FF50;
--method-link-background: #41B65850;
--scope-link-background: #FCC00350;
--event-link-background: #E3066A50;
--code-link-background: #1ab9ff50;
--method-link-background: #41b65850;
--scope-link-background: #fcc00350;
--event-link-background: #e3066a50;
}

a code {
background-color: var(--code-link-background);
color: var(--code-link-text);
color: var(--code-link-text);
}

a[href^="https://api.slack.com/methods"] > code {
background-color: var(--method-link-background);
color: var(--method-link-text);
color: var(--method-link-text);
}

a[href^="https://api.slack.com/scopes"] > code {
background-color: var(--scope-link-background);
color: var(--scope-link-text);
color: var(--scope-link-text);
}

a[href^="https://api.slack.com/events"] > code {
background-color: var(--event-link-background);
color: var(--event-link-text);
}
color: var(--event-link-text);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"@tsconfig/node18": "^18.2.4",
"@types/chai": "^4.1.7",
"@types/mocha": "^10.0.1",
"@types/node": "22.8.7",
"@types/node": "22.9.0",
"@types/sinon": "^7.0.11",
"@types/tsscmp": "^1.0.0",
"c8": "^10.1.2",
Expand Down
142 changes: 56 additions & 86 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import axios, { type AxiosInstance, type AxiosResponse } from 'axios';
import type { Assistant } from './Assistant';
import {
CustomFunction,
type CustomFunctionMiddleware,
type FunctionCompleteFn,
type FunctionFailFn,
type SlackCustomFunctionMiddlewareArgs,
createFunctionComplete,
createFunctionFail,
} from './CustomFunction';
import type { WorkflowStep } from './WorkflowStep';
import { type ConversationStore, MemoryStore, conversationContext } from './conversation-store';
Expand All @@ -31,9 +29,7 @@ import {
isEventTypeToSkipAuthorize,
} from './helpers';
import {
autoAcknowledge,
ignoreSelf as ignoreSelfMiddleware,
isSlackEventMiddlewareArgsOptions,
matchCommandName,
matchConstraints,
matchEventType,
Expand All @@ -51,6 +47,7 @@ import SocketModeReceiver from './receivers/SocketModeReceiver';
import type {
AckFn,
ActionConstraints,
AllMiddlewareArgs,
AnyMiddlewareArgs,
BlockAction,
BlockElementAction,
Expand All @@ -75,7 +72,6 @@ import type {
SlackActionMiddlewareArgs,
SlackCommandMiddlewareArgs,
SlackEventMiddlewareArgs,
SlackEventMiddlewareArgsOptions,
SlackOptionsMiddlewareArgs,
SlackShortcut,
SlackShortcutMiddlewareArgs,
Expand All @@ -86,7 +82,7 @@ import type {
ViewOutput,
WorkflowStepEdit,
} from './types';
import { type AllMiddlewareArgs, contextBuiltinKeys } from './types/middleware';
import { contextBuiltinKeys } from './types';
import { type StringIndexed, isRejected } from './types/utilities';
const packageJson = require('../package.json');

Expand Down Expand Up @@ -500,7 +496,7 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
* @param m global middleware function
*/
public use<MiddlewareCustomContext extends StringIndexed = StringIndexed>(
m: Middleware<AnyMiddlewareArgs<{ autoAcknowledge: false }>, AppCustomContext & MiddlewareCustomContext>,
m: Middleware<AnyMiddlewareArgs, AppCustomContext & MiddlewareCustomContext>,
): this {
this.middleware.push(m as Middleware<AnyMiddlewareArgs>);
return this;
Expand Down Expand Up @@ -533,31 +529,10 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
/**
* Register CustomFunction middleware
*/
public function<Options extends SlackEventMiddlewareArgsOptions = { autoAcknowledge: true }>(
callbackId: string,
options: Options,
...listeners: Middleware<SlackCustomFunctionMiddlewareArgs<Options>>[]
): this;
public function<Options extends SlackEventMiddlewareArgsOptions = { autoAcknowledge: true }>(
callbackId: string,
...listeners: Middleware<SlackCustomFunctionMiddlewareArgs<Options>>[]
): this;
public function<Options extends SlackEventMiddlewareArgsOptions = { autoAcknowledge: true }>(
callbackId: string,
...optionOrListeners: (Options | Middleware<SlackCustomFunctionMiddlewareArgs<Options>>)[]
): this {
// TODO: fix this casting; edge case is if dev specifically sets AutoAck generic as false, this true assignment is invalid according to TS.
const options = isSlackEventMiddlewareArgsOptions(optionOrListeners[0])
? optionOrListeners[0]
: ({ autoAcknowledge: true } as Options);
const listeners = optionOrListeners.filter(
(optionOrListener): optionOrListener is Middleware<SlackCustomFunctionMiddlewareArgs<Options>> => {
return !isSlackEventMiddlewareArgsOptions(optionOrListener);
},
);

const fn = new CustomFunction<Options>(callbackId, listeners, options);
this.listeners.push(fn.getListeners());
public function(callbackId: string, ...listeners: CustomFunctionMiddleware): this {
const fn = new CustomFunction(callbackId, listeners, this.webClientOptions);
const m = fn.getMiddleware();
this.middleware.push(m);
return this;
}

Expand Down Expand Up @@ -619,7 +594,6 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
this.listeners.push([
onlyEvents,
matchEventType(eventNameOrPattern),
autoAcknowledge,
..._listeners,
] as Middleware<AnyMiddlewareArgs>[]);
}
Expand Down Expand Up @@ -688,7 +662,6 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
this.listeners.push([
onlyEvents,
matchEventType('message'),
autoAcknowledge,
...messageMiddleware,
] as Middleware<AnyMiddlewareArgs>[]);
}
Expand Down Expand Up @@ -1006,7 +979,7 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>

// Factory for say() utility
const createSay = (channelId: string): SayFn => {
const token = selectToken(context, this.attachFunctionToken);
const token = selectToken(context);
return (message) => {
let postMessageArguments: ChatPostMessageArguments;
if (typeof message === 'string') {
Expand Down Expand Up @@ -1067,66 +1040,27 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
respond?: RespondFn;
/** Ack function might be set below */
// biome-ignore lint/suspicious/noExplicitAny: different kinds of acks accept different arguments, TODO: revisit this to see if we can type better
ack: AckFn<any>;
ack?: AckFn<any>;
complete?: FunctionCompleteFn;
fail?: FunctionFailFn;
inputs?: FunctionInputs;
} = {
body: bodyArg,
ack,
payload,
};

// Get the client arg
let { client } = this;

const token = selectToken(context, this.attachFunctionToken);

if (token !== undefined) {
let pool: WebClientPool | undefined = undefined;
const clientOptionsCopy = { ...this.clientOptions };
if (authorizeResult.teamId !== undefined) {
pool = this.clients[authorizeResult.teamId];
if (pool === undefined) {
pool = this.clients[authorizeResult.teamId] = new WebClientPool();
}
// Add teamId to clientOptions so it can be automatically added to web-api calls
clientOptionsCopy.teamId = authorizeResult.teamId;
} else if (authorizeResult.enterpriseId !== undefined) {
pool = this.clients[authorizeResult.enterpriseId];
if (pool === undefined) {
pool = this.clients[authorizeResult.enterpriseId] = new WebClientPool();
}
}
if (pool !== undefined) {
client = pool.getOrCreate(token, clientOptionsCopy);
}
}

// 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
// Set aliases
if (type === IncomingEventType.Event) {
// TODO: assigning eventListenerArgs by reference to set properties of listenerArgs is error prone, there should be a better way to do this!
const eventListenerArgs = listenerArgs as unknown as SlackEventMiddlewareArgs;
const eventListenerArgs = listenerArgs as SlackEventMiddlewareArgs;
eventListenerArgs.event = eventListenerArgs.payload;
if (eventListenerArgs.event.type === 'message') {
const messageEventListenerArgs = eventListenerArgs as SlackEventMiddlewareArgs<'message'>;
messageEventListenerArgs.message = messageEventListenerArgs.payload;
}
if (eventListenerArgs.event.type === 'function_executed') {
listenerArgs.complete = createFunctionComplete(context, client);
listenerArgs.fail = createFunctionFail(context, client);
listenerArgs.inputs = eventListenerArgs.event.inputs;
}
} else if (type === IncomingEventType.Action) {
const actionListenerArgs = listenerArgs as SlackActionMiddlewareArgs;
actionListenerArgs.action = actionListenerArgs.payload;
// Add complete() and fail() utilities for function-related interactivity
if (context.functionExecutionId !== undefined) {
listenerArgs.complete = createFunctionComplete(context, client);
listenerArgs.fail = createFunctionFail(context, client);
listenerArgs.inputs = context.functionInputs;
}
} else if (type === IncomingEventType.Command) {
const commandListenerArgs = listenerArgs as SlackCommandMiddlewareArgs;
commandListenerArgs.command = commandListenerArgs.payload;
Expand Down Expand Up @@ -1154,6 +1088,50 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
listenerArgs.respond = buildRespondFn(this.axios, body.response_urls[0].response_url);
}

// Set ack() utility
if (type !== IncomingEventType.Event) {
listenerArgs.ack = ack;
} else {
// Events API requests are acknowledged right away, since there's no data expected
await ack();
}

// Get the client arg
let { client } = this;

// If functionBotAccessToken exists on context, the incoming event is function-related *and* the
// user has `attachFunctionToken` enabled. In that case, subsequent calls with the client should
// use the function-related/JIT token in lieu of the botToken or userToken.
const token = context.functionBotAccessToken ? context.functionBotAccessToken : selectToken(context);

// Add complete() and fail() utilities for function-related interactivity
if (type === IncomingEventType.Action && context.functionExecutionId !== undefined) {
listenerArgs.complete = CustomFunction.createFunctionComplete(context, client);
listenerArgs.fail = CustomFunction.createFunctionFail(context, client);
listenerArgs.inputs = context.functionInputs;
}

if (token !== undefined) {
let pool: WebClientPool | undefined = undefined;
const clientOptionsCopy = { ...this.clientOptions };
if (authorizeResult.teamId !== undefined) {
pool = this.clients[authorizeResult.teamId];
if (pool === undefined) {
pool = this.clients[authorizeResult.teamId] = new WebClientPool();
}
// Add teamId to clientOptions so it can be automatically added to web-api calls
clientOptionsCopy.teamId = authorizeResult.teamId;
} else if (authorizeResult.enterpriseId !== undefined) {
pool = this.clients[authorizeResult.enterpriseId];
if (pool === undefined) {
pool = this.clients[authorizeResult.enterpriseId] = new WebClientPool();
}
}
if (pool !== undefined) {
client = pool.getOrCreate(token, clientOptionsCopy);
}
}

// Dispatch event through the global middleware chain
try {
await processMiddleware(
Expand Down Expand Up @@ -1597,15 +1575,7 @@ function isBlockActionOrInteractiveMessageBody(
}

// Returns either a bot token or a user token for client, say()
function selectToken(context: Context, attachFunctionToken: boolean): string | undefined {
if (attachFunctionToken) {
// If functionBotAccessToken exists on context, the incoming event is function-related *and* the
// user has `attachFunctionToken` enabled. In that case, subsequent calls with the client should
// use the function-related/JIT token in lieu of the botToken or userToken.
if (context.functionBotAccessToken) {
return context.functionBotAccessToken;
}
}
function selectToken(context: Context): string | undefined {
return context.botToken !== undefined ? context.botToken : context.userToken;
}

Expand Down
10 changes: 5 additions & 5 deletions src/Assistant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import {
type AssistantThreadContext,
type AssistantThreadContextStore,
DefaultThreadContextStore,
type GetThreadContextFn,
type SaveThreadContextFn,
} from './AssistantThreadContextStore';
import { AssistantInitializationError, AssistantMissingPropertyError } from './errors';
import processMiddleware from './middleware/process';
Expand All @@ -30,14 +28,16 @@ export interface AssistantConfig {
* Callback utilities
*/
interface AssistantUtilityArgs {
getThreadContext: GetThreadContextFn;
saveThreadContext: SaveThreadContextFn;
getThreadContext: GetThreadContextUtilFn;
saveThreadContext: SaveThreadContextUtilFn;
say: SayFn;
setStatus: SetStatusFn;
setSuggestedPrompts: SetSuggestedPromptsFn;
setTitle: SetTitleFn;
}

type GetThreadContextUtilFn = () => Promise<AssistantThreadContext>;
type SaveThreadContextUtilFn = () => Promise<void>;
type SetStatusFn = (status: string) => Promise<AssistantThreadsSetStatusResponse>;

type SetSuggestedPromptsFn = (
Expand Down Expand Up @@ -310,7 +310,7 @@ function createSay(args: AllAssistantMiddlewareArgs): SayFn {
const { channelId: channel, threadTs: thread_ts, context } = extractThreadInfo(payload);

return async (message: Parameters<SayFn>[0]) => {
const threadContext = context.channel_id ? context : await args.getThreadContext(args);
const threadContext = context.channel_id ? context : await args.getThreadContext();
const postMessageArgument: ChatPostMessageArguments =
typeof message === 'string' ? { text: message, channel, thread_ts } : { ...message, channel, thread_ts };

Expand Down
Loading

0 comments on commit 7b23d00

Please sign in to comment.