feat(slack): add configurable loading messages and typing status
Add support for customizing the Slack thread status messages shown while
processing requests:
- loadingMessages: string[] - Array of messages that rotate randomly
during processing (passed to Slack's assistant.threads.setStatus API)
- typingStatus: string - Custom typing indicator text (default: 'is typing...')
These can be configured per Slack account in channels.slack config:
```yaml
channels:
slack:
typingStatus: 'is thinking...'
loadingMessages:
- 'Pondering...'
- 'Processing...'
- 'Almost there...'
```
This commit is contained in:
parent
50b4126c79
commit
a65f457f52
@ -77,6 +77,10 @@ export type SlackThreadConfig = {
|
|||||||
export type SlackAccountConfig = {
|
export type SlackAccountConfig = {
|
||||||
/** Optional display name for this account (used in CLI/UI lists). */
|
/** Optional display name for this account (used in CLI/UI lists). */
|
||||||
name?: string;
|
name?: string;
|
||||||
|
/** Custom loading messages shown while processing (rotates randomly). */
|
||||||
|
loadingMessages?: string[];
|
||||||
|
/** Status text shown while typing (default: "is typing..."). */
|
||||||
|
typingStatus?: string;
|
||||||
/** Slack connection mode (socket|http). Default: socket. */
|
/** Slack connection mode (socket|http). Default: socket. */
|
||||||
mode?: "socket" | "http";
|
mode?: "socket" | "http";
|
||||||
/** Slack signing secret (required for HTTP mode). */
|
/** Slack signing secret (required for HTTP mode). */
|
||||||
|
|||||||
@ -111,7 +111,10 @@ export type SlackMonitorContext = {
|
|||||||
channelId: string;
|
channelId: string;
|
||||||
threadTs?: string;
|
threadTs?: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
loadingMessages?: string[];
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
|
loadingMessages?: string[];
|
||||||
|
typingStatus: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createSlackMonitorContext(params: {
|
export function createSlackMonitorContext(params: {
|
||||||
@ -148,6 +151,8 @@ export function createSlackMonitorContext(params: {
|
|||||||
ackReactionScope: string;
|
ackReactionScope: string;
|
||||||
mediaMaxBytes: number;
|
mediaMaxBytes: number;
|
||||||
removeAckAfterReply: boolean;
|
removeAckAfterReply: boolean;
|
||||||
|
loadingMessages?: string[];
|
||||||
|
typingStatus?: string;
|
||||||
}): SlackMonitorContext {
|
}): SlackMonitorContext {
|
||||||
const channelHistories = new Map<string, HistoryEntry[]>();
|
const channelHistories = new Map<string, HistoryEntry[]>();
|
||||||
const logger = getChildLogger({ module: "slack-auto-reply" });
|
const logger = getChildLogger({ module: "slack-auto-reply" });
|
||||||
@ -247,14 +252,18 @@ export function createSlackMonitorContext(params: {
|
|||||||
channelId: string;
|
channelId: string;
|
||||||
threadTs?: string;
|
threadTs?: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
loadingMessages?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!p.threadTs) return;
|
if (!p.threadTs) return;
|
||||||
const payload = {
|
const payload: Record<string, unknown> = {
|
||||||
token: params.botToken,
|
token: params.botToken,
|
||||||
channel_id: p.channelId,
|
channel_id: p.channelId,
|
||||||
thread_ts: p.threadTs,
|
thread_ts: p.threadTs,
|
||||||
status: p.status,
|
status: p.status,
|
||||||
};
|
};
|
||||||
|
if (p.loadingMessages && p.loadingMessages.length > 0) {
|
||||||
|
payload.loading_messages = p.loadingMessages;
|
||||||
|
}
|
||||||
const client = params.app.client as unknown as {
|
const client = params.app.client as unknown as {
|
||||||
assistant?: {
|
assistant?: {
|
||||||
threads?: {
|
threads?: {
|
||||||
@ -400,5 +409,7 @@ export function createSlackMonitorContext(params: {
|
|||||||
resolveChannelName,
|
resolveChannelName,
|
||||||
resolveUserName,
|
resolveUserName,
|
||||||
setSlackThreadStatus,
|
setSlackThreadStatus,
|
||||||
|
loadingMessages: params.loadingMessages,
|
||||||
|
typingStatus: params.typingStatus ?? "is typing...",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,7 +63,8 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
|||||||
await ctx.setSlackThreadStatus({
|
await ctx.setSlackThreadStatus({
|
||||||
channelId: message.channel,
|
channelId: message.channel,
|
||||||
threadTs: statusThreadTs,
|
threadTs: statusThreadTs,
|
||||||
status: "is typing...",
|
status: ctx.typingStatus,
|
||||||
|
loadingMessages: ctx.loadingMessages,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
stop: async () => {
|
stop: async () => {
|
||||||
|
|||||||
@ -123,6 +123,8 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions";
|
const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions";
|
||||||
const mediaMaxBytes = (opts.mediaMaxMb ?? slackCfg.mediaMaxMb ?? 20) * 1024 * 1024;
|
const mediaMaxBytes = (opts.mediaMaxMb ?? slackCfg.mediaMaxMb ?? 20) * 1024 * 1024;
|
||||||
const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
|
const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
|
||||||
|
const loadingMessages = slackCfg.loadingMessages;
|
||||||
|
const typingStatus = slackCfg.typingStatus;
|
||||||
|
|
||||||
const receiver =
|
const receiver =
|
||||||
slackMode === "http"
|
slackMode === "http"
|
||||||
@ -204,6 +206,8 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
ackReactionScope,
|
ackReactionScope,
|
||||||
mediaMaxBytes,
|
mediaMaxBytes,
|
||||||
removeAckAfterReply,
|
removeAckAfterReply,
|
||||||
|
loadingMessages,
|
||||||
|
typingStatus,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSlackMessage = createSlackMessageHandler({ ctx, account });
|
const handleSlackMessage = createSlackMessageHandler({ ctx, account });
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user