flingit 0.0.19 → 0.0.22
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/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +9 -0
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/plugin.d.ts +3 -10
- package/dist/cli/commands/plugin.d.ts.map +1 -1
- package/dist/cli/commands/plugin.js +103 -209
- package/dist/cli/commands/plugin.js.map +1 -1
- package/dist/cli/commands/push.d.ts +5 -0
- package/dist/cli/commands/push.d.ts.map +1 -1
- package/dist/cli/commands/push.js +21 -33
- package/dist/cli/commands/push.js.map +1 -1
- package/dist/cli/deploy/bundler.d.ts +19 -9
- package/dist/cli/deploy/bundler.d.ts.map +1 -1
- package/dist/cli/deploy/bundler.js +168 -69
- package/dist/cli/deploy/bundler.js.map +1 -1
- package/dist/cli/utils/config.d.ts +12 -4
- package/dist/cli/utils/config.d.ts.map +1 -1
- package/dist/cli/utils/config.js +49 -6
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/runtime/discord.d.ts +20 -58
- package/dist/runtime/discord.d.ts.map +1 -1
- package/dist/runtime/discord.js +22 -59
- package/dist/runtime/discord.js.map +1 -1
- package/dist/runtime/slack.d.ts +83 -0
- package/dist/runtime/slack.d.ts.map +1 -0
- package/dist/runtime/slack.js +87 -0
- package/dist/runtime/slack.js.map +1 -0
- package/dist/shared/discord-types.d.ts +23 -24
- package/dist/shared/discord-types.d.ts.map +1 -1
- package/dist/shared/plugin-metadata.d.ts +28 -0
- package/dist/shared/plugin-metadata.d.ts.map +1 -0
- package/dist/shared/plugin-metadata.js +65 -0
- package/dist/shared/plugin-metadata.js.map +1 -0
- package/dist/shared/slack-types.d.ts +61 -0
- package/dist/shared/slack-types.d.ts.map +1 -0
- package/dist/shared/slack-types.js +8 -0
- package/dist/shared/slack-types.js.map +1 -0
- package/dist/worker-runtime/discord.d.ts +80 -27
- package/dist/worker-runtime/discord.d.ts.map +1 -1
- package/dist/worker-runtime/discord.js +163 -108
- package/dist/worker-runtime/discord.js.map +1 -1
- package/dist/worker-runtime/index.d.ts +1 -1
- package/dist/worker-runtime/index.d.ts.map +1 -1
- package/dist/worker-runtime/index.js +0 -4
- package/dist/worker-runtime/index.js.map +1 -1
- package/dist/worker-runtime/plugin-common.d.ts +27 -0
- package/dist/worker-runtime/plugin-common.d.ts.map +1 -0
- package/dist/worker-runtime/plugin-common.js +91 -0
- package/dist/worker-runtime/plugin-common.js.map +1 -0
- package/dist/worker-runtime/slack.d.ts +67 -0
- package/dist/worker-runtime/slack.d.ts.map +1 -0
- package/dist/worker-runtime/slack.js +135 -0
- package/dist/worker-runtime/slack.js.map +1 -0
- package/package.json +8 -1
- package/templates/default/skills/fling/.hash +1 -1
- package/templates/default/skills/fling/DISCORD.md +112 -122
- package/templates/default/skills/fling/SKILL.md +7 -1
- package/templates/default/skills/fling/SLACK.md +214 -0
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
# Discord Skill
|
|
2
2
|
|
|
3
|
-
Build Discord chatops bots with Fling.
|
|
3
|
+
Build Discord chatops bots with Fling. Handle slash commands, send messages, handle interactions, and react to events — all from your Fling project.
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
7
7
|
Discord requires a one-time setup before use:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm exec fling plugin install discord # Connect Discord account (opens browser)
|
|
11
|
-
npm exec fling
|
|
12
|
-
npm exec fling push # Deploy code + register slash commands
|
|
10
|
+
npm exec fling plugin install discord # Connect Discord account + add bot to a server (opens browser)
|
|
11
|
+
npm exec fling push # Deploy code
|
|
13
12
|
```
|
|
14
13
|
|
|
15
14
|
Run these commands yourself — you have bash access.
|
|
@@ -20,48 +19,33 @@ Run these commands yourself — you have bash access.
|
|
|
20
19
|
import { app } from "flingit";
|
|
21
20
|
import { discord } from "flingit/plugin/discord";
|
|
22
21
|
|
|
23
|
-
// 1.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const branch = interaction.data.options?.[0]?.value as string;
|
|
48
|
-
await discord.replyToInteraction(interaction, {
|
|
49
|
-
content: `Deploying ${branch}...`
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Do work after the initial reply...
|
|
53
|
-
const result = await runDeploy(branch);
|
|
54
|
-
|
|
55
|
-
// Send a followup message (no 3-second limit)
|
|
56
|
-
await discord.sendFollowup(interaction, {
|
|
57
|
-
content: `Deployed ${branch}: ${result}`
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return c.json({ ok: true });
|
|
22
|
+
// 1. Handle slash commands using onCommand(name, description, handler)
|
|
23
|
+
// Commands are auto-registered with Discord on `fling push`
|
|
24
|
+
discord.onCommand("ping", "Check if the bot is alive", async (interaction) => {
|
|
25
|
+
await discord.reply(interaction, {
|
|
26
|
+
content: "Pong!",
|
|
27
|
+
ephemeral: true // Only visible to the user who ran the command
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
discord.onCommand("deploy", "Deploy a branch", [
|
|
32
|
+
{ name: "branch", type: "string", description: "Branch to deploy", required: false },
|
|
33
|
+
], async (interaction, options) => {
|
|
34
|
+
const branch = options.getString("branch") ?? "main";
|
|
35
|
+
await discord.reply(interaction, {
|
|
36
|
+
content: `Deploying ${branch}...`
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Do work after the initial reply...
|
|
40
|
+
const result = await runDeploy(branch);
|
|
41
|
+
|
|
42
|
+
// Send a followup message (no 3-second limit)
|
|
43
|
+
await discord.followup(interaction, {
|
|
44
|
+
content: `Deployed ${branch}: ${result}`
|
|
45
|
+
});
|
|
62
46
|
});
|
|
63
47
|
|
|
64
|
-
//
|
|
48
|
+
// 2. Send messages proactively (e.g., from webhooks or cron)
|
|
65
49
|
app.post("/api/notify", async (c) => {
|
|
66
50
|
const { channelId, text } = await c.req.json();
|
|
67
51
|
|
|
@@ -82,67 +66,82 @@ All methods are imported from `"flingit/plugin/discord"`:
|
|
|
82
66
|
import { discord } from "flingit/plugin/discord";
|
|
83
67
|
```
|
|
84
68
|
|
|
85
|
-
### discord.
|
|
69
|
+
### discord.onCommand(name, description, handler)
|
|
70
|
+
### discord.onCommand(name, description, options, handler)
|
|
86
71
|
|
|
87
|
-
Register slash
|
|
72
|
+
Register a handler for a specific slash command. Commands are **automatically registered** with Discord when you run `fling push` — no need to use the Discord Developer Portal.
|
|
88
73
|
|
|
89
74
|
```typescript
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
75
|
+
// Simple command (no parameters)
|
|
76
|
+
discord.onCommand("ping", "Check if the bot is alive", async (interaction, options) => {
|
|
77
|
+
await discord.reply(interaction, "Pong!");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Command with parameters — Discord shows autocomplete & validation
|
|
81
|
+
discord.onCommand("deploy", "Deploy a branch", [
|
|
82
|
+
{ name: "branch", type: "string", description: "Branch to deploy" },
|
|
83
|
+
{ name: "force", type: "boolean", description: "Force deploy", required: false },
|
|
84
|
+
], async (interaction, options) => {
|
|
85
|
+
const branch = options.getString("branch") ?? "main";
|
|
86
|
+
const force = options.getBoolean("force") ?? false;
|
|
87
|
+
await discord.reply(interaction, `Deploying ${branch}${force ? " (force)" : ""}...`);
|
|
88
|
+
});
|
|
101
89
|
```
|
|
102
90
|
|
|
103
|
-
**
|
|
91
|
+
**Supported option types:**
|
|
92
|
+
|
|
93
|
+
| Type | Discord type | Value |
|
|
94
|
+
|------|-------------|-------|
|
|
95
|
+
| `"string"` | STRING (3) | `options.getString("name")` |
|
|
96
|
+
| `"integer"` | INTEGER (4) | `options.getNumber("name")` |
|
|
97
|
+
| `"boolean"` | BOOLEAN (5) | `options.getBoolean("name")` |
|
|
98
|
+
| `"number"` | NUMBER (10) | `options.getNumber("name")` |
|
|
104
99
|
|
|
105
|
-
**Option
|
|
100
|
+
**Option properties:**
|
|
101
|
+
- `name` — Parameter name (required)
|
|
102
|
+
- `type` — One of `"string"`, `"integer"`, `"boolean"`, `"number"` (required)
|
|
103
|
+
- `description` — Shown in Discord's UI (required)
|
|
104
|
+
- `required` — Defaults to `true`. Set `false` for optional parameters.
|
|
105
|
+
- `choices` — Enum-style choices: `[{ name: "Production", value: "prod" }, { name: "Staging", value: "staging" }]`
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
**Choices example:**
|
|
108
108
|
|
|
109
109
|
```typescript
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
discord.onCommand("env", "Switch environment", [
|
|
111
|
+
{
|
|
112
|
+
name: "target",
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "Target environment",
|
|
115
|
+
choices: [
|
|
116
|
+
{ name: "Production", value: "prod" },
|
|
117
|
+
{ name: "Staging", value: "staging" },
|
|
118
|
+
{ name: "Development", value: "dev" },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
], async (interaction, options) => {
|
|
122
|
+
const target = options.getString("target")!;
|
|
123
|
+
await discord.reply(interaction, `Switching to ${target}`);
|
|
124
|
+
});
|
|
120
125
|
```
|
|
121
126
|
|
|
122
|
-
|
|
127
|
+
The platform verifies Discord's cryptographic signature before forwarding to your code — you don't need to handle that.
|
|
123
128
|
|
|
124
|
-
|
|
129
|
+
### discord.onEvent(handler)
|
|
130
|
+
|
|
131
|
+
Register a fallback handler for interactions that don't match any `onCommand`. Use for advanced cases.
|
|
125
132
|
|
|
126
133
|
```typescript
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// interaction.data.name = command name
|
|
131
|
-
// interaction.data.options = command arguments
|
|
132
|
-
// interaction.member.user = who ran the command (in servers)
|
|
133
|
-
// interaction.guild_id = which server
|
|
134
|
-
// interaction.channel_id = which channel
|
|
134
|
+
discord.onEvent(async (interaction) => {
|
|
135
|
+
// Called for any interaction not handled by onCommand
|
|
135
136
|
});
|
|
136
137
|
```
|
|
137
138
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
### discord.replyToInteraction(interaction, options)
|
|
139
|
+
### discord.reply(interaction, options)
|
|
141
140
|
|
|
142
141
|
Reply to a slash command. **Must be called within 3 seconds** of receiving the interaction.
|
|
143
142
|
|
|
144
143
|
```typescript
|
|
145
|
-
|
|
144
|
+
discord.reply(interaction, {
|
|
146
145
|
content: "Hello!", // Text content (max 2000 chars)
|
|
147
146
|
ephemeral: true, // Only visible to command user (optional)
|
|
148
147
|
embeds: [{ // Rich embeds (optional, max 10)
|
|
@@ -153,16 +152,16 @@ await discord.replyToInteraction(interaction, {
|
|
|
153
152
|
});
|
|
154
153
|
```
|
|
155
154
|
|
|
156
|
-
### discord.
|
|
155
|
+
### discord.followup(interaction, options)
|
|
157
156
|
|
|
158
157
|
Send additional messages after the initial reply. No 3-second time limit.
|
|
159
158
|
|
|
160
159
|
```typescript
|
|
161
|
-
|
|
160
|
+
discord.reply(interaction, { content: "Working on it..." });
|
|
162
161
|
|
|
163
162
|
// ... do async work ...
|
|
164
163
|
|
|
165
|
-
const msg = await discord.
|
|
164
|
+
const msg = await discord.followup(interaction, {
|
|
166
165
|
content: "Done! Here are the results.",
|
|
167
166
|
embeds: [{ title: "Results", description: resultText }]
|
|
168
167
|
});
|
|
@@ -243,23 +242,21 @@ Content max: 2000 characters. Embeds max: 10 per message.
|
|
|
243
242
|
|
|
244
243
|
## Reading Command Options
|
|
245
244
|
|
|
246
|
-
|
|
245
|
+
The `options` helper provides typed accessors for command options. Declare options in the `onCommand` call so Discord shows parameter hints and validation:
|
|
247
246
|
|
|
248
247
|
```typescript
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
})
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return c.json({ ok: true });
|
|
248
|
+
discord.onCommand("deploy", "Deploy a branch", [
|
|
249
|
+
{ name: "branch", type: "string", description: "Branch to deploy" },
|
|
250
|
+
{ name: "force", type: "boolean", description: "Force deploy", required: false },
|
|
251
|
+
{ name: "count", type: "integer", description: "Number of instances", required: false },
|
|
252
|
+
], async (interaction, options) => {
|
|
253
|
+
const branch = options.getString("branch") ?? "main";
|
|
254
|
+
const force = options.getBoolean("force") ?? false;
|
|
255
|
+
const count = options.getNumber("count") ?? 1;
|
|
256
|
+
|
|
257
|
+
await discord.reply(interaction, {
|
|
258
|
+
content: `Deploying ${branch}${force ? " (force)" : ""} (×${count})...`
|
|
259
|
+
});
|
|
263
260
|
});
|
|
264
261
|
```
|
|
265
262
|
|
|
@@ -268,9 +265,7 @@ app.post("/discord", async (c) => {
|
|
|
268
265
|
Access who ran the command and where:
|
|
269
266
|
|
|
270
267
|
```typescript
|
|
271
|
-
|
|
272
|
-
const interaction = await discord.verifyInteraction(c);
|
|
273
|
-
|
|
268
|
+
discord.onCommand("info", "Show context info", async (interaction) => {
|
|
274
269
|
// Who ran it
|
|
275
270
|
const user = interaction.member?.user; // In servers
|
|
276
271
|
// user.id, user.username
|
|
@@ -289,14 +284,12 @@ app.post("/discord", async (c) => {
|
|
|
289
284
|
|
|
290
285
|
```bash
|
|
291
286
|
# Setup
|
|
292
|
-
npm exec fling plugin install discord # Connect Discord (OAuth)
|
|
293
|
-
npm exec fling plugin discord add-server # Claim a server
|
|
294
|
-
npm exec fling plugin discord remove-server # Release a server
|
|
287
|
+
npm exec fling plugin install discord # Connect Discord (OAuth), auto-claims server
|
|
295
288
|
|
|
296
289
|
# Status
|
|
297
290
|
npm exec fling plugin permissions discord # Show connection status + servers
|
|
298
291
|
|
|
299
|
-
# Deployment
|
|
292
|
+
# Deployment
|
|
300
293
|
npm exec fling push
|
|
301
294
|
|
|
302
295
|
# Teardown
|
|
@@ -305,24 +298,21 @@ npm exec fling plugin remove discord # Disconnect, release all servers
|
|
|
305
298
|
|
|
306
299
|
## How It Works
|
|
307
300
|
|
|
308
|
-
1. **`fling plugin install discord`** opens a browser for Discord OAuth. You authorize Fling to
|
|
309
|
-
2. **`
|
|
310
|
-
3. **`
|
|
311
|
-
4. When a user runs a slash command, Discord sends it to the platform, which routes it to your worker
|
|
312
|
-
5. Your
|
|
301
|
+
1. **`fling plugin install discord`** opens a browser for Discord OAuth. You authorize Fling, select a server to add the bot to, and that server is automatically claimed for your project.
|
|
302
|
+
2. **`discord.onCommand(name, description, handler)`** registers a named command handler with a description.
|
|
303
|
+
3. **`fling push`** bundles your code, deploys it, then automatically registers your slash commands with Discord's API for each claimed server. Commands appear instantly in Discord's slash command picker.
|
|
304
|
+
4. When a user runs a slash command, Discord sends it to the platform, which routes it to your worker.
|
|
305
|
+
5. Your handler receives the interaction and calls `discord.reply()` to respond.
|
|
306
|
+
6. To add the bot to additional servers, re-run `fling plugin install discord` (remove and reinstall to re-OAuth), then `fling push` again to register commands in the new server.
|
|
313
307
|
|
|
314
308
|
## Important Constraints
|
|
315
309
|
|
|
316
310
|
1. **Discord features only work in deployed workers** — They throw errors locally. Use `fling push` to deploy, then test in Discord.
|
|
317
311
|
|
|
318
|
-
2. **Reply within 3 seconds** — `
|
|
319
|
-
|
|
320
|
-
3. **Commands must be exported** — `fling push` looks for `export const commands = discord.defineCommands(...)`. If the export is missing, commands won't be registered.
|
|
321
|
-
|
|
322
|
-
4. **Handle interactions at POST /discord** — The platform routes all interactions to this exact path on your worker.
|
|
312
|
+
2. **Reply within 3 seconds** — `discord.reply()` must be called within 3 seconds. For slow operations, reply immediately with "Working..." then use `discord.followup()` for the result.
|
|
323
313
|
|
|
324
|
-
|
|
314
|
+
3. **Channels must be in claimed servers** — `sendMessage()`, `editMessage()`, and `addReaction()` only work in channels belonging to servers claimed by your project.
|
|
325
315
|
|
|
326
|
-
|
|
316
|
+
4. **One project per server** — A Discord server can only be claimed by one Fling project at a time.
|
|
327
317
|
|
|
328
|
-
|
|
318
|
+
5. **Plugin must be installed first** — Run `fling plugin install discord` before using any Discord features. Check with `fling plugin permissions discord`.
|
|
@@ -215,6 +215,8 @@ When the user's request is complete and working locally, offer to deploy it. Bu
|
|
|
215
215
|
To deploy:
|
|
216
216
|
**Run `npm exec fling push` directly** - you have bash access, don't ask the user to run commands.
|
|
217
217
|
|
|
218
|
+
After each deploy, tell the user what the deployed URL is.
|
|
219
|
+
|
|
218
220
|
### After First Deployment
|
|
219
221
|
|
|
220
222
|
After your first `fling push`, consider asking the user if they want a custom URL.
|
|
@@ -251,7 +253,7 @@ Slugs must be:
|
|
|
251
253
|
|
|
252
254
|
## Security
|
|
253
255
|
|
|
254
|
-
If the user does not want their app to be
|
|
256
|
+
If the user does not want their app to be deployed because of security issues, or asks about that, offer implementing proper backend-supported security. In particular, suggest these two auth methods first:
|
|
255
257
|
|
|
256
258
|
- Login with Google, with a filter on which emails/domains are allowed
|
|
257
259
|
- Simple password-based auth, where the backend has a list of allowed passwords (hashed)
|
|
@@ -344,3 +346,7 @@ This approach:
|
|
|
344
346
|
**For large assets:** Serve them from the frontend (`public/` folder) instead.
|
|
345
347
|
|
|
346
348
|
See API.md for detailed API reference, EXAMPLES.md for common patterns, and FEEDBACK.md for collecting user feedback.
|
|
349
|
+
|
|
350
|
+
## Updates
|
|
351
|
+
|
|
352
|
+
When the fling CLI mentions that there is a new version available, strongly suggest to the user to update, becasue it might contain bug fixes or new features.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Slack Skill
|
|
2
|
+
|
|
3
|
+
Build Slack chatops bots with Fling. Respond to @mentions, send messages, and react to events — all from your Fling project.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
Slack requires a one-time setup before use:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm exec fling plugin install slack # Connect Slack workspace (opens browser)
|
|
11
|
+
npm exec fling push # Deploy code
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Run these commands yourself — you have bash access.
|
|
15
|
+
|
|
16
|
+
## Quick Reference
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { app } from "flingit";
|
|
20
|
+
import { slack } from "flingit/plugin/slack";
|
|
21
|
+
|
|
22
|
+
// 1. Handle @mentions of your bot
|
|
23
|
+
slack.onMention(async (event) => {
|
|
24
|
+
// Parse the mention text (remove the bot mention prefix)
|
|
25
|
+
const text = event.text.replace(/<@[A-Z0-9]+>\s*/, "").trim();
|
|
26
|
+
|
|
27
|
+
if (text.startsWith("deploy")) {
|
|
28
|
+
const branch = text.split(" ")[1] ?? "main";
|
|
29
|
+
await slack.sendMessage({
|
|
30
|
+
channelId: event.channel,
|
|
31
|
+
threadTs: event.ts,
|
|
32
|
+
text: `Deploying ${branch}...`,
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
await slack.sendMessage({
|
|
36
|
+
channelId: event.channel,
|
|
37
|
+
threadTs: event.ts,
|
|
38
|
+
text: "Hello! How can I help?",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 2. Send messages proactively (e.g., from webhooks or cron)
|
|
44
|
+
app.post("/api/notify", async (c) => {
|
|
45
|
+
const { channelId, text } = await c.req.json();
|
|
46
|
+
|
|
47
|
+
await slack.sendMessage({
|
|
48
|
+
channelId,
|
|
49
|
+
text
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return c.json({ sent: true });
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API Reference
|
|
57
|
+
|
|
58
|
+
All methods are imported from `"flingit/plugin/slack"`:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { slack } from "flingit/plugin/slack";
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### slack.onMention(handler)
|
|
65
|
+
|
|
66
|
+
Register a handler for @mentions of your bot. Triggered when someone mentions your Fling app in a channel.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
slack.onMention(async (event) => {
|
|
70
|
+
// event.channel = which channel
|
|
71
|
+
// event.user = who mentioned the bot
|
|
72
|
+
// event.text = full mention text (includes <@BOT_ID>)
|
|
73
|
+
// event.ts = message timestamp
|
|
74
|
+
// event.thread_ts = thread timestamp (if in thread)
|
|
75
|
+
|
|
76
|
+
await slack.sendMessage({
|
|
77
|
+
channelId: event.channel,
|
|
78
|
+
threadTs: event.ts, // Reply in thread
|
|
79
|
+
text: "Got it!",
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The platform verifies Slack's HMAC-SHA256 signature before forwarding to your code — you don't need to handle that.
|
|
85
|
+
|
|
86
|
+
### slack.onEvent(handler)
|
|
87
|
+
|
|
88
|
+
Register a fallback handler for raw Slack events that aren't handled by `onMention`. Use this for advanced cases.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
slack.onEvent(async (event) => {
|
|
92
|
+
console.log("Raw event:", event.type);
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### slack.sendMessage(options)
|
|
97
|
+
|
|
98
|
+
Send a message to any channel. Use this for notifications, alerts, or proactive messages.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const msg = await slack.sendMessage({
|
|
102
|
+
channelId: "C123456789",
|
|
103
|
+
text: "Deployment complete!",
|
|
104
|
+
blocks: [{
|
|
105
|
+
type: "section",
|
|
106
|
+
text: { type: "mrkdwn", text: "*Deploy Report*\nv2.1.0 is now live" }
|
|
107
|
+
}]
|
|
108
|
+
});
|
|
109
|
+
// Returns: { channelId, ts, text }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
To reply in a thread:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
await slack.sendMessage({
|
|
116
|
+
channelId: "C123456789",
|
|
117
|
+
text: "Thread reply here",
|
|
118
|
+
threadTs: "1234567890.123456" // Parent message timestamp
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### slack.editMessage(channelId, ts, options)
|
|
123
|
+
|
|
124
|
+
Edit a previously sent message.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const msg = await slack.sendMessage({
|
|
128
|
+
channelId: channel,
|
|
129
|
+
text: "Deploying..."
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Later, update it
|
|
133
|
+
await slack.editMessage(channel, msg.ts, {
|
|
134
|
+
text: "Deploy complete!"
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### slack.addReaction(channelId, ts, emoji)
|
|
139
|
+
|
|
140
|
+
Add an emoji reaction to a message.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
await slack.addReaction(channelId, ts, "white_check_mark");
|
|
144
|
+
await slack.addReaction(channelId, ts, "rocket");
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Note: Use emoji names without colons (e.g., `"thumbsup"` not `":thumbsup:"`).
|
|
148
|
+
|
|
149
|
+
## Block Kit
|
|
150
|
+
|
|
151
|
+
Slack uses Block Kit for rich message formatting. Common block types:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Section block with markdown
|
|
155
|
+
{
|
|
156
|
+
type: "section",
|
|
157
|
+
text: { type: "mrkdwn", text: "*Bold* and _italic_" }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Section with fields
|
|
161
|
+
{
|
|
162
|
+
type: "section",
|
|
163
|
+
fields: [
|
|
164
|
+
{ type: "mrkdwn", text: "*Status:*\nActive" },
|
|
165
|
+
{ type: "mrkdwn", text: "*Region:*\nus-east-1" }
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Divider
|
|
170
|
+
{ type: "divider" }
|
|
171
|
+
|
|
172
|
+
// Context (small text)
|
|
173
|
+
{
|
|
174
|
+
type: "context",
|
|
175
|
+
elements: [
|
|
176
|
+
{ type: "mrkdwn", text: "Deployed by <@U123> at 3:42 PM" }
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
For more block types, see the Slack Block Kit documentation.
|
|
182
|
+
|
|
183
|
+
## CLI Commands
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# Setup
|
|
187
|
+
npm exec fling plugin install slack # Connect Slack (OAuth), auto-claims workspace
|
|
188
|
+
|
|
189
|
+
# Status
|
|
190
|
+
npm exec fling plugin permissions slack # Show connection status + workspaces
|
|
191
|
+
|
|
192
|
+
# Deployment
|
|
193
|
+
npm exec fling push
|
|
194
|
+
|
|
195
|
+
# Teardown
|
|
196
|
+
npm exec fling plugin remove slack # Disconnect, release all workspaces
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## How It Works
|
|
200
|
+
|
|
201
|
+
1. **`fling plugin install slack`** opens a browser for Slack OAuth. You authorize the Fling app for your workspace, and it's automatically claimed for your project.
|
|
202
|
+
2. **`slack.onMention(handler)`** registers a handler that receives `app_mention` events when someone @mentions your bot in a channel.
|
|
203
|
+
3. The platform verifies Slack's HMAC-SHA256 signature, looks up the owning project, and forwards the event to your worker.
|
|
204
|
+
4. Your handler receives the event and uses `slack.sendMessage()` to respond (typically replying in-thread).
|
|
205
|
+
|
|
206
|
+
## Important Constraints
|
|
207
|
+
|
|
208
|
+
1. **Slack features only work in deployed workers** — They throw errors locally. Use `fling push` to deploy, then test in Slack.
|
|
209
|
+
|
|
210
|
+
2. **Mention-based interaction** — Users interact with your bot by @mentioning it in a channel. Parse the mention text to understand intent.
|
|
211
|
+
|
|
212
|
+
3. **One project per workspace** — A Slack workspace can only be claimed by one Fling project at a time.
|
|
213
|
+
|
|
214
|
+
4. **Plugin must be installed first** — Run `fling plugin install slack` before using any Slack features. Check with `fling plugin permissions slack`.
|