Skip to content

Commit 3b124b1

Browse files
committed
feat: enhance Slack plugin with configuration options and message handling
- Added configuration options for Slack bot, including bot token, signing secret, app token, and allowed channel IDs in the config template. - Refactored Slack plugin to improve message handling and context awareness. - Updated message description to provide clearer guidance on responding to messages. - Ensured proper handling of message events and channel access checks.
1 parent f6f39a1 commit 3b124b1

File tree

2 files changed

+48
-88
lines changed

2 files changed

+48
-88
lines changed

configs/config.yaml-template

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,10 @@ plugins:
7575
browser:
7676
headless: true
7777
cli-ui:
78+
slack:
79+
bot_token: your-slack-bot-token
80+
signing_secret: your-slack-signing-secret
81+
app_token: your-slack-app-token
82+
allowed_channel_ids:
83+
- "1234567890"
84+
- "9876543210"

src/plugins/slack/init.ts

Lines changed: 41 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,31 @@
1-
import Bolt, { type App as BoltApp, LogLevel } from "@slack/bolt";
1+
import bolt from "@slack/bolt";
22
import { AuthTestResponse } from "@slack/web-api";
3+
34
import { Athena, Dict } from "../../core/athena.js";
45
import { PluginBase } from "../plugin-base.js";
56

6-
const { App } = Bolt;
7+
const { App } = bolt;
78

89
export default class Slack extends PluginBase {
9-
app!: BoltApp;
10+
app!: InstanceType<typeof App>;
1011
me!: AuthTestResponse;
11-
boundAthenaPrivateEventHandler!: (event: string, args: Dict<any>) => void;
1212

1313
desc() {
14-
return `You can send and receive messages to and from Slack. Your username in Slack is ${this.me.user_id}. For channels, you don't have to respond to every message - only respond when asked to do something or when you have something useful to contribute. For direct messages, respond to every message unless explicitly told not to. When you receive a message, you can reply by calling the "slack/send-message" tool. Be mindful of the channel or conversation context before sending a message.`;
14+
return `You can send and receive messages to and from Slack. The workspace name is ${this.me.team}. Your user ID in Slack is ${this.me.user_id} and your display name is ${this.me.user}. For channels, you don't have to respond to every message. Just respond when you are asked to do something or have something useful to say. For direct messages, you should respond to every message, unless being explicitly told not to. When you receive a message, you can reply to it by calling the "slack/send-message" tool. Be mindful about which channel you are in and the type of the message before sending a message.`;
1515
}
1616

1717
async load(athena: Athena) {
18-
this.boundAthenaPrivateEventHandler =
19-
this.athenaPrivateEventHandler.bind(this);
20-
2118
this.app = new App({
2219
token: this.config.bot_token,
23-
appToken: this.config.app_token,
20+
signingSecret: this.config.signing_secret,
2421
socketMode: true,
25-
logLevel: LogLevel.DEBUG,
22+
appToken: this.config.app_token,
2623
});
27-
2824
this.me = await this.app.client.auth.test();
2925

30-
athena.on("private-event", this.boundAthenaPrivateEventHandler);
31-
athena.emitPrivateEvent("slack/load", {
32-
content: "Plugin slack loaded with Socket Mode.",
33-
});
34-
35-
// Register events
3626
athena.registerEvent({
3727
name: "slack/message-received",
38-
desc: "Triggered when a message is received from Slack via Socket Mode.",
28+
desc: "Triggered when a message is received from Slack.",
3929
args: {
4030
ts: {
4131
type: "string",
@@ -102,7 +92,6 @@ export default class Slack extends PluginBase {
10292
},
10393
});
10494

105-
// Register tools
10695
athena.registerTool(
10796
{
10897
name: "slack/send-message",
@@ -128,7 +117,7 @@ export default class Slack extends PluginBase {
128117
ts: {
129118
type: "string",
130119
desc: "Timestamp of the sent message.",
131-
required: false,
120+
required: true,
132121
},
133122
},
134123
},
@@ -139,7 +128,7 @@ export default class Slack extends PluginBase {
139128
text: args.text,
140129
thread_ts: args.thread_ts,
141130
});
142-
return { ts: result.ts };
131+
return { ts: result.ts! };
143132
},
144133
},
145134
);
@@ -220,85 +209,49 @@ export default class Slack extends PluginBase {
220209
},
221210
);
222211

223-
this.app.message(async ({ message, say }) => {
224-
console.log("Received message:", message);
225-
if (message.subtype) {
226-
return;
227-
}
228-
if (!this.config.allowed_channel_ids.includes(message.channel)) {
229-
if (
230-
message.channel_type === "im" ||
231-
message.text?.toLowerCase().includes("channel id")
232-
) {
233-
await say(
234-
`You appear to not have access to Athena, but FYI, your channel ID is ${message.channel}.`,
235-
);
212+
athena.once("plugins-loaded", () => {
213+
this.app.message(async ({ message, say }) => {
214+
if (message.subtype) {
215+
return;
216+
}
217+
if (!this.config.allowed_channel_ids.includes(message.channel)) {
218+
if (
219+
message.channel_type === "im" ||
220+
message.text?.toLowerCase().includes("channel id")
221+
) {
222+
await say(
223+
`You appear to not have access to Athena, but FYI, your channel ID is ${message.channel}.`,
224+
);
225+
}
226+
return;
236227
}
237-
return;
238-
}
239228

240-
athena.emitEvent("slack/message-received", {
241-
ts: message.ts,
242-
user: message.user,
243-
channel: message.channel,
244-
channel_type: message.channel_type,
245-
text: message.text,
246-
thread_ts: message.thread_ts,
247-
files:
248-
message.files?.map((file) => ({
249-
id: file.id,
250-
name: file.name,
251-
url_private: file.url_private,
252-
size: file.size,
253-
})) ?? [],
229+
athena.emitEvent("slack/message-received", {
230+
ts: message.ts,
231+
user: message.user,
232+
channel: message.channel,
233+
channel_type: message.channel_type,
234+
text: message.text,
235+
thread_ts: message.thread_ts,
236+
files:
237+
message.files?.map((file) => ({
238+
id: file.id,
239+
name: file.name,
240+
url_private: file.url_private,
241+
size: file.size,
242+
})) ?? [],
243+
});
254244
});
255245
});
256246

257-
this.app.start(3000).then(() => {
258-
console.log("⚡️ Slack Bolt app is running!");
259-
});
247+
await this.app.start();
260248
}
261249

262250
async unload(athena: Athena) {
263-
athena.emitPrivateEvent("slack/unload", {
264-
content: "Plugin slack unloaded.",
265-
});
266-
athena.off("private-event", this.boundAthenaPrivateEventHandler);
267251
await this.app.stop();
268252
athena.deregisterTool("slack/send-message");
269253
athena.deregisterTool("slack/edit-message");
270254
athena.deregisterTool("slack/delete-message");
271255
athena.deregisterEvent("slack/message-received");
272256
}
273-
274-
athenaPrivateEventHandler(event: string, args: Dict<any>) {
275-
if (args.content) {
276-
for (const channelId of this.config.admin_channel_ids) {
277-
this.app.client.chat
278-
.postMessage({
279-
channel: channelId,
280-
text: `${event}\n${args.content}`,
281-
})
282-
.catch(() => {});
283-
}
284-
}
285-
if (
286-
["cerebrum/thinking", "athena/tool-call", "athena/tool-result"].includes(
287-
event,
288-
)
289-
) {
290-
const message =
291-
event === "cerebrum/thinking"
292-
? `Thinking: ${args.content}`
293-
: args.summary;
294-
for (const channelId of this.config.log_channel_ids) {
295-
this.app.client.chat
296-
.postMessage({
297-
channel: channelId,
298-
text: message,
299-
})
300-
.catch(() => {});
301-
}
302-
}
303-
}
304257
}

0 commit comments

Comments
 (0)