@geminixiang/mama 0.2.0-beta.1 → 0.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -58
- package/dist/adapter.d.ts +8 -6
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +2 -2
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +20 -29
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +16 -20
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +11 -4
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +199 -73
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +27 -30
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +4 -2
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +130 -71
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +9 -95
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/telegram/html.d.ts +3 -0
- package/dist/adapters/telegram/html.d.ts.map +1 -0
- package/dist/adapters/telegram/html.js +98 -0
- package/dist/adapters/telegram/html.js.map +1 -0
- package/dist/agent.d.ts +3 -11
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +63 -70
- package/dist/agent.js.map +1 -1
- package/dist/bindings.d.ts +1 -20
- package/dist/bindings.d.ts.map +1 -1
- package/dist/bindings.js +1 -21
- package/dist/bindings.js.map +1 -1
- package/dist/config.d.ts +7 -27
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +77 -63
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +2 -2
- package/dist/context.js.map +1 -1
- package/dist/events.d.ts +11 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +33 -13
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +1 -3
- package/dist/execution-resolver.js.map +1 -1
- package/dist/instrument.d.ts.map +1 -1
- package/dist/instrument.js +5 -11
- package/dist/instrument.js.map +1 -1
- package/dist/link-server.d.ts +2 -1
- package/dist/link-server.d.ts.map +1 -1
- package/dist/link-server.js +62 -2
- package/dist/link-server.js.map +1 -1
- package/dist/login.d.ts +1 -1
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +1 -1
- package/dist/login.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +96 -112
- package/dist/main.js.map +1 -1
- package/dist/provisioner.d.ts +0 -41
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +0 -45
- package/dist/provisioner.js.map +1 -1
- package/dist/sandbox/host.d.ts +0 -2
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +1 -5
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sentry.d.ts.map +1 -1
- package/dist/sentry.js +2 -0
- package/dist/sentry.js.map +1 -1
- package/dist/session-store.d.ts +1 -1
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +5 -9
- package/dist/session-store.js.map +1 -1
- package/dist/tools/event.d.ts +1 -0
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +6 -5
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/ui-copy.d.ts +1 -0
- package/dist/ui-copy.d.ts.map +1 -1
- package/dist/ui-copy.js +3 -0
- package/dist/ui-copy.js.map +1 -1
- package/dist/vault-routing.d.ts +1 -2
- package/dist/vault-routing.d.ts.map +1 -1
- package/dist/vault-routing.js +1 -7
- package/dist/vault-routing.js.map +1 -1
- package/package.json +1 -1
- package/dist/vault.test.d.ts +0 -2
- package/dist/vault.test.d.ts.map +0 -1
- package/dist/vault.test.js +0 -67
- package/dist/vault.test.js.map +0 -1
|
@@ -3,9 +3,8 @@ import { WebClient } from "@slack/web-api";
|
|
|
3
3
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "fs";
|
|
4
4
|
import { readFile } from "fs/promises";
|
|
5
5
|
import { basename, join } from "path";
|
|
6
|
-
import { parseLoginCommand } from "../../login.js";
|
|
7
6
|
import * as log from "../../log.js";
|
|
8
|
-
import { PRODUCT_NAME,
|
|
7
|
+
import { PRODUCT_NAME, formatForceStopped, formatNothingRunning } from "../../ui-copy.js";
|
|
9
8
|
import { createSlackAdapters } from "./context.js";
|
|
10
9
|
// ============================================================================
|
|
11
10
|
// Exponential backoff utility for Slack API calls
|
|
@@ -94,21 +93,6 @@ export class SlackBot {
|
|
|
94
93
|
setEventsWatcher(watcher) {
|
|
95
94
|
this.eventsWatcher = watcher;
|
|
96
95
|
}
|
|
97
|
-
toBotEvent(event) {
|
|
98
|
-
return {
|
|
99
|
-
type: event.type,
|
|
100
|
-
conversationId: event.channel,
|
|
101
|
-
ts: event.ts,
|
|
102
|
-
thread_ts: event.thread_ts,
|
|
103
|
-
user: event.user,
|
|
104
|
-
text: event.text,
|
|
105
|
-
attachments: event.attachments?.map((attachment) => ({
|
|
106
|
-
name: attachment.original,
|
|
107
|
-
localPath: attachment.localPath,
|
|
108
|
-
})),
|
|
109
|
-
sessionKey: event.sessionKey,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
96
|
// ==========================================================================
|
|
113
97
|
// Public API
|
|
114
98
|
// ==========================================================================
|
|
@@ -136,15 +120,30 @@ export class SlackBot {
|
|
|
136
120
|
getAllChannels() {
|
|
137
121
|
return Array.from(this.channels.values());
|
|
138
122
|
}
|
|
139
|
-
async postMessage(
|
|
123
|
+
async postMessage(channel, text) {
|
|
140
124
|
return withRetry(async () => {
|
|
141
|
-
const result = await this.webClient.chat.postMessage({ channel
|
|
125
|
+
const result = await this.webClient.chat.postMessage({ channel, text });
|
|
142
126
|
return result.ts;
|
|
143
127
|
});
|
|
144
128
|
}
|
|
145
|
-
async
|
|
129
|
+
async postEphemeral(channel, user, text) {
|
|
130
|
+
return withRetry(async () => {
|
|
131
|
+
await this.webClient.chat.postEphemeral({ channel, user, text });
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async openDirectMessage(userId) {
|
|
135
|
+
return withRetry(async () => {
|
|
136
|
+
const result = await this.webClient.conversations.open({ users: userId });
|
|
137
|
+
const channelId = result.channel?.id;
|
|
138
|
+
if (!channelId) {
|
|
139
|
+
throw new Error(`Failed to open DM for user ${userId}`);
|
|
140
|
+
}
|
|
141
|
+
return channelId;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async updateMessage(channel, ts, text) {
|
|
146
145
|
return withRetry(async () => {
|
|
147
|
-
await this.webClient.chat.update({ channel
|
|
146
|
+
await this.webClient.chat.update({ channel, ts, text });
|
|
148
147
|
});
|
|
149
148
|
}
|
|
150
149
|
async deleteMessage(channel, ts) {
|
|
@@ -214,10 +213,10 @@ export class SlackBot {
|
|
|
214
213
|
* This is the ONLY place messages are written to log.jsonl
|
|
215
214
|
*/
|
|
216
215
|
logToFile(channel, entry) {
|
|
217
|
-
const
|
|
218
|
-
if (!existsSync(
|
|
219
|
-
mkdirSync(
|
|
220
|
-
appendFileSync(join(
|
|
216
|
+
const dir = join(this.workingDir, channel);
|
|
217
|
+
if (!existsSync(dir))
|
|
218
|
+
mkdirSync(dir, { recursive: true });
|
|
219
|
+
appendFileSync(join(dir, "log.jsonl"), `${JSON.stringify(entry)}\n`);
|
|
221
220
|
}
|
|
222
221
|
/**
|
|
223
222
|
* Log a bot response to log.jsonl
|
|
@@ -253,20 +252,27 @@ export class SlackBot {
|
|
|
253
252
|
* Returns true if enqueued, false if queue is full (max 5).
|
|
254
253
|
*/
|
|
255
254
|
enqueueEvent(event) {
|
|
256
|
-
const
|
|
255
|
+
const conversationId = event.conversationId;
|
|
256
|
+
const queue = this.getQueue(conversationId);
|
|
257
257
|
if (queue.size() >= 5) {
|
|
258
|
-
log.logWarning(`Event queue full for ${
|
|
258
|
+
log.logWarning(`Event queue full for ${conversationId}, discarding: ${event.text.substring(0, 50)}`);
|
|
259
259
|
return false;
|
|
260
260
|
}
|
|
261
|
-
log.logInfo(`Enqueueing event for ${
|
|
261
|
+
log.logInfo(`Enqueueing event for ${conversationId}: ${event.text.substring(0, 50)}`);
|
|
262
262
|
queue.enqueue(() => {
|
|
263
263
|
const slackEvent = {
|
|
264
|
-
type:
|
|
265
|
-
|
|
264
|
+
type: event.type,
|
|
265
|
+
conversationId,
|
|
266
|
+
conversationKind: event.conversationKind,
|
|
267
|
+
channel: conversationId,
|
|
266
268
|
ts: event.ts,
|
|
267
269
|
thread_ts: event.thread_ts,
|
|
268
270
|
user: event.user,
|
|
269
271
|
text: event.text,
|
|
272
|
+
attachments: event.attachments?.map((attachment) => ({
|
|
273
|
+
original: attachment.name,
|
|
274
|
+
localPath: attachment.localPath,
|
|
275
|
+
})),
|
|
270
276
|
sessionKey: event.sessionKey,
|
|
271
277
|
};
|
|
272
278
|
const adapters = createSlackAdapters(slackEvent, this, true);
|
|
@@ -387,21 +393,22 @@ export class SlackBot {
|
|
|
387
393
|
});
|
|
388
394
|
}
|
|
389
395
|
else {
|
|
390
|
-
const timestampFormatter = new Intl.DateTimeFormat(undefined, {
|
|
391
|
-
month: "short",
|
|
392
|
-
day: "numeric",
|
|
393
|
-
hour: "2-digit",
|
|
394
|
-
minute: "2-digit",
|
|
395
|
-
});
|
|
396
396
|
for (const ev of periodicEvents) {
|
|
397
397
|
const channelLabel = ev.platform === "slack"
|
|
398
398
|
? (() => {
|
|
399
|
-
const channel = this.channels.get(ev.
|
|
400
|
-
const channelName = channel ? `#${channel.name}` : ev.
|
|
399
|
+
const channel = this.channels.get(ev.conversationId);
|
|
400
|
+
const channelName = channel ? `#${channel.name}` : ev.conversationId;
|
|
401
401
|
return `${ev.platform}:${channelName}`;
|
|
402
402
|
})()
|
|
403
|
-
: `${ev.platform}:${ev.
|
|
404
|
-
const nextStr = ev.nextRun
|
|
403
|
+
: `${ev.platform}:${ev.conversationId}`;
|
|
404
|
+
const nextStr = ev.nextRun
|
|
405
|
+
? new Date(ev.nextRun).toLocaleString("en-US", {
|
|
406
|
+
month: "short",
|
|
407
|
+
day: "numeric",
|
|
408
|
+
hour: "2-digit",
|
|
409
|
+
minute: "2-digit",
|
|
410
|
+
})
|
|
411
|
+
: "—";
|
|
405
412
|
blocks.push({
|
|
406
413
|
type: "section",
|
|
407
414
|
text: {
|
|
@@ -438,6 +445,115 @@ export class SlackBot {
|
|
|
438
445
|
}
|
|
439
446
|
return this.handler.isRunning(channelId) ? channelId : null;
|
|
440
447
|
}
|
|
448
|
+
createDirectCommandAdapters(conversationId, userId, userName, text, ts) {
|
|
449
|
+
const message = {
|
|
450
|
+
id: ts,
|
|
451
|
+
sessionKey: conversationId,
|
|
452
|
+
conversationKind: "direct",
|
|
453
|
+
userId,
|
|
454
|
+
userName,
|
|
455
|
+
text,
|
|
456
|
+
attachments: [],
|
|
457
|
+
};
|
|
458
|
+
const responseCtx = {
|
|
459
|
+
respond: async (responseText) => {
|
|
460
|
+
const messageTs = await this.postMessage(conversationId, responseText);
|
|
461
|
+
this.logBotResponse(conversationId, responseText, messageTs);
|
|
462
|
+
},
|
|
463
|
+
replaceResponse: async (responseText) => {
|
|
464
|
+
const messageTs = await this.postMessage(conversationId, responseText);
|
|
465
|
+
this.logBotResponse(conversationId, responseText, messageTs);
|
|
466
|
+
},
|
|
467
|
+
respondInThread: async (responseText) => {
|
|
468
|
+
const messageTs = await this.postMessage(conversationId, responseText);
|
|
469
|
+
this.logBotResponse(conversationId, responseText, messageTs);
|
|
470
|
+
},
|
|
471
|
+
setTyping: async () => { },
|
|
472
|
+
setWorking: async () => { },
|
|
473
|
+
uploadFile: async (filePath, title) => {
|
|
474
|
+
await this.uploadFile(conversationId, filePath, title);
|
|
475
|
+
},
|
|
476
|
+
deleteResponse: async () => { },
|
|
477
|
+
};
|
|
478
|
+
return {
|
|
479
|
+
message,
|
|
480
|
+
responseCtx,
|
|
481
|
+
platform: this.getPlatformInfo(),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
createSlashCommandBot(conversationId, threadTs) {
|
|
485
|
+
return {
|
|
486
|
+
start: async () => { },
|
|
487
|
+
postMessage: async (_channel, text) => {
|
|
488
|
+
if (threadTs) {
|
|
489
|
+
return this.postInThread(conversationId, threadTs, text);
|
|
490
|
+
}
|
|
491
|
+
return this.postMessage(conversationId, text);
|
|
492
|
+
},
|
|
493
|
+
updateMessage: async (channel, ts, text) => {
|
|
494
|
+
await this.updateMessage(channel, ts, text);
|
|
495
|
+
},
|
|
496
|
+
enqueueEvent: (event) => this.enqueueEvent(event),
|
|
497
|
+
getPlatformInfo: () => this.getPlatformInfo(),
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
async routeSlashLoginCommand(payload) {
|
|
501
|
+
const commandSuffix = payload.text?.trim();
|
|
502
|
+
const commandText = commandSuffix ? `${payload.command} ${commandSuffix}` : payload.command;
|
|
503
|
+
const createdAt = new Date();
|
|
504
|
+
const eventTs = (createdAt.getTime() / 1000).toFixed(6);
|
|
505
|
+
const sourceChannelId = payload.channel_id;
|
|
506
|
+
const isDirectMessage = sourceChannelId.startsWith("D");
|
|
507
|
+
const targetChannelId = isDirectMessage
|
|
508
|
+
? sourceChannelId
|
|
509
|
+
: await this.openDirectMessage(payload.user_id);
|
|
510
|
+
const userName = payload.user_name ?? this.getUser(payload.user_id)?.userName;
|
|
511
|
+
this.logToFile(targetChannelId, {
|
|
512
|
+
date: createdAt.toISOString(),
|
|
513
|
+
ts: eventTs,
|
|
514
|
+
user: payload.user_id,
|
|
515
|
+
userName,
|
|
516
|
+
text: commandText,
|
|
517
|
+
attachments: [],
|
|
518
|
+
isBot: false,
|
|
519
|
+
});
|
|
520
|
+
if (!isDirectMessage) {
|
|
521
|
+
await this.postEphemeral(sourceChannelId, payload.user_id, `我已私訊你 ${PRODUCT_NAME} 的登入連結,請到私訊完成設定。`);
|
|
522
|
+
}
|
|
523
|
+
const event = {
|
|
524
|
+
type: "dm",
|
|
525
|
+
conversationId: targetChannelId,
|
|
526
|
+
conversationKind: "direct",
|
|
527
|
+
ts: eventTs,
|
|
528
|
+
user: payload.user_id,
|
|
529
|
+
text: commandText,
|
|
530
|
+
attachments: [],
|
|
531
|
+
sessionKey: targetChannelId,
|
|
532
|
+
};
|
|
533
|
+
const adapters = this.createDirectCommandAdapters(targetChannelId, payload.user_id, userName, commandText, eventTs);
|
|
534
|
+
await this.handler.handleEvent(event, this, adapters, false);
|
|
535
|
+
}
|
|
536
|
+
async routeSlashNewCommand(payload) {
|
|
537
|
+
const conversationId = payload.channel_id;
|
|
538
|
+
if (!conversationId.startsWith("D")) {
|
|
539
|
+
await this.postEphemeral(conversationId, payload.user_id, `為了避免誤清除共享上下文,${payload.command} 目前只能在與 ${PRODUCT_NAME} 的私訊中使用。`);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
const createdAt = new Date();
|
|
543
|
+
const eventTs = (createdAt.getTime() / 1000).toFixed(6);
|
|
544
|
+
const userName = payload.user_name ?? this.getUser(payload.user_id)?.userName;
|
|
545
|
+
this.logToFile(conversationId, {
|
|
546
|
+
date: createdAt.toISOString(),
|
|
547
|
+
ts: eventTs,
|
|
548
|
+
user: payload.user_id,
|
|
549
|
+
userName,
|
|
550
|
+
text: payload.command,
|
|
551
|
+
attachments: [],
|
|
552
|
+
isBot: false,
|
|
553
|
+
});
|
|
554
|
+
const commandBot = this.createSlashCommandBot(conversationId);
|
|
555
|
+
await this.handler.handleNew(conversationId, conversationId, commandBot);
|
|
556
|
+
}
|
|
441
557
|
setupEventHandlers() {
|
|
442
558
|
// Channel @mentions
|
|
443
559
|
this.socketClient.on("app_mention", ({ event, ack }) => {
|
|
@@ -452,6 +568,8 @@ export class SlackBot {
|
|
|
452
568
|
const sessionKey = e.thread_ts ? `${e.channel}:${e.thread_ts}` : e.channel;
|
|
453
569
|
const slackEvent = {
|
|
454
570
|
type: "mention",
|
|
571
|
+
conversationId: e.channel,
|
|
572
|
+
conversationKind: "shared",
|
|
455
573
|
channel: e.channel,
|
|
456
574
|
ts: e.ts,
|
|
457
575
|
thread_ts: e.thread_ts,
|
|
@@ -481,22 +599,10 @@ export class SlackBot {
|
|
|
481
599
|
ack();
|
|
482
600
|
return;
|
|
483
601
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
// SYNC: Check if busy (per-thread)
|
|
491
|
-
if (this.handler.isRunning(sessionKey)) {
|
|
492
|
-
this.postMessage(e.channel, formatAlreadyWorking("slack", "@mama stop", { scope: "thread" }));
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
this.getQueue(sessionKey).enqueue(() => {
|
|
496
|
-
const adapters = createSlackAdapters(slackEvent, this, false);
|
|
497
|
-
return this.handler.handleEvent(this.toBotEvent(slackEvent), this, adapters, false);
|
|
498
|
-
});
|
|
499
|
-
}
|
|
602
|
+
this.getQueue(sessionKey).enqueue(() => {
|
|
603
|
+
const adapters = createSlackAdapters(slackEvent, this, false);
|
|
604
|
+
return this.handler.handleEvent(slackEvent, this, adapters, false);
|
|
605
|
+
});
|
|
500
606
|
ack();
|
|
501
607
|
});
|
|
502
608
|
// All messages (for logging) + DMs (for triggering)
|
|
@@ -516,6 +622,7 @@ export class SlackBot {
|
|
|
516
622
|
return;
|
|
517
623
|
}
|
|
518
624
|
const isDM = e.channel_type === "im";
|
|
625
|
+
const conversationKind = isDM ? "direct" : "shared";
|
|
519
626
|
const isBotMention = e.text?.includes(`<@${this.botUserId}>`);
|
|
520
627
|
// Skip channel @mentions - already handled by app_mention event
|
|
521
628
|
if (!isDM && isBotMention) {
|
|
@@ -524,6 +631,8 @@ export class SlackBot {
|
|
|
524
631
|
}
|
|
525
632
|
const slackEvent = {
|
|
526
633
|
type: isDM ? "dm" : "mention",
|
|
634
|
+
conversationId: e.channel,
|
|
635
|
+
conversationKind,
|
|
527
636
|
channel: e.channel,
|
|
528
637
|
ts: e.ts,
|
|
529
638
|
thread_ts: e.thread_ts,
|
|
@@ -568,24 +677,42 @@ export class SlackBot {
|
|
|
568
677
|
ack();
|
|
569
678
|
return;
|
|
570
679
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
if (this.handler.isRunning(dmSessionKey)) {
|
|
578
|
-
this.postMessage(e.channel, formatAlreadyWorking("slack", "stop"));
|
|
579
|
-
}
|
|
580
|
-
else {
|
|
581
|
-
this.getQueue(dmSessionKey).enqueue(() => {
|
|
582
|
-
const adapters = createSlackAdapters(slackEvent, this, false);
|
|
583
|
-
return this.handler.handleEvent(this.toBotEvent(slackEvent), this, adapters, false);
|
|
584
|
-
});
|
|
585
|
-
}
|
|
680
|
+
this.getQueue(dmSessionKey).enqueue(() => {
|
|
681
|
+
const adapters = createSlackAdapters(slackEvent, this, false);
|
|
682
|
+
return this.handler.handleEvent(slackEvent, this, adapters, false);
|
|
683
|
+
});
|
|
586
684
|
}
|
|
587
685
|
ack();
|
|
588
686
|
});
|
|
687
|
+
this.socketClient.on("slash_commands", async ({ body, ack }) => {
|
|
688
|
+
const payload = body;
|
|
689
|
+
await ack();
|
|
690
|
+
if (!payload.command || !payload.channel_id || !payload.user_id) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
const handlerPromise = payload.command === "/pi-login"
|
|
694
|
+
? this.routeSlashLoginCommand({
|
|
695
|
+
command: payload.command,
|
|
696
|
+
text: payload.text,
|
|
697
|
+
channel_id: payload.channel_id,
|
|
698
|
+
user_id: payload.user_id,
|
|
699
|
+
user_name: payload.user_name,
|
|
700
|
+
})
|
|
701
|
+
: payload.command === "/pi-new"
|
|
702
|
+
? this.routeSlashNewCommand({
|
|
703
|
+
command: payload.command,
|
|
704
|
+
channel_id: payload.channel_id,
|
|
705
|
+
user_id: payload.user_id,
|
|
706
|
+
user_name: payload.user_name,
|
|
707
|
+
})
|
|
708
|
+
: null;
|
|
709
|
+
if (!handlerPromise) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
handlerPromise.catch((err) => {
|
|
713
|
+
log.logWarning("Slack slash command error", err instanceof Error ? err.message : String(err));
|
|
714
|
+
});
|
|
715
|
+
});
|
|
589
716
|
// App Home tab
|
|
590
717
|
this.socketClient.on("app_home_opened", ({ event, ack }) => {
|
|
591
718
|
const e = event;
|
|
@@ -616,8 +743,7 @@ export class SlackBot {
|
|
|
616
743
|
// Use handler's forceStop method
|
|
617
744
|
this.handler.forceStop(sessionKey);
|
|
618
745
|
// Notify in channel
|
|
619
|
-
|
|
620
|
-
await this.postMessage(channelId, formatForceStopped("slack", actorLabel));
|
|
746
|
+
await this.postMessage(channelId, formatForceStopped("slack", userId ?? "unknown"));
|
|
621
747
|
// Refresh home tab
|
|
622
748
|
if (userId) {
|
|
623
749
|
this.webClient.views
|