Skip to content

Commit 9803be0

Browse files
committedJul 8, 2024
fix validator and event payload schemas
1 parent 142c18a commit 9803be0

File tree

9 files changed

+73
-30
lines changed

9 files changed

+73
-30
lines changed
 

‎.tool-versions

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
deno 1.44.4

‎README.md

+11-4
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,24 @@
22

33
A slack bot powered by Slack webhook events.
44

5+
## Slack App
6+
7+
This bot is installed in the devICT work space as a Slack app called **bot**.
8+
9+
- The [OAuth & Permissions](https://api.slack.com/apps/A07B9TL6EMT/oauth) page contains the `SLACK_TOKEN` needed to power the bot.
10+
- The following scopes must be added: `app_mentions:read`, `chat:write`
11+
- The event receiving endpoint must be added on the [Event Subscriptions](https://api.slack.com/apps/A07B9TL6EMT/event-subscriptions) page.
12+
- Events to subscribe to: `app_mention`
13+
514
## Slack Events
615

716
[A list of all the events can be seen here](https://api.slack.com/events).
817

918
The events the bot responds to are defined in the `events.ts` module. Typebox schemas are defined for the events we respond to.
1019

11-
## Running locally
20+
## Development
1221

13-
```
14-
deno task start
15-
```
22+
There is a separate Slack app for development that can be used.
1623

1724
## Deployment
1825

‎commands/types.ts ‎commands/mod.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import { pingCommand } from "./ping.ts";
2+
3+
export const commands = [pingCommand];
4+
15
export interface Command<TSlackEvent> {
26
matcher: RegExp;
37
handler: (event: TSlackEvent) => Promise<void>;

‎commands/ping.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import { Command } from "./mod.ts";
12
import { AppMention } from "../events.ts";
23
import { slack } from "../slack.ts";
3-
import { Command } from "./types.ts";
44

55
export const pingCommand: Command<AppMention> = {
66
matcher: /ping/,

‎config.ts

-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@ const assertEnv = (key: string) => {
66

77
export const config = {
88
SLACK_TOKEN: assertEnv("SLACK_TOKEN"),
9-
SLACK_WEBHOOK_URL: assertEnv("SSLACK_WEBHOOK_URL"),
109
} as const;

‎deno.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"tasks": {
3-
"start": "deno run --allow-net main.ts",
3+
"start": "deno run --allow-net --allow-env main.ts",
44
"deploy": "deployctl deploy --org=devICT --project=devict-bot",
55
"deploy:prod": "deployctl deploy --org=devICT --project=devict-bot --prod"
66
},
@@ -11,9 +11,9 @@
1111
"entrypoint": "main.ts"
1212
},
1313
"imports": {
14-
"@hono/typebox-validator": "npm:@hono/typebox-validator@^0.2.3",
14+
"@hono/hono": "jsr:@hono/hono@^4.4.12",
15+
"@hono/typebox-validator": "npm:@hono/typebox-validator@^0.2.4",
1516
"@sinclair/typebox": "npm:@sinclair/typebox@^0.32.26",
16-
"@slack/web-api": "npm:@slack/web-api@^7.0.4",
17-
"hono": "https://deno.land/x/hono/mod.ts@^4.2.8"
17+
"@slack/web-api": "npm:@slack/web-api@^7.0.4"
1818
}
1919
}

‎deno.lock

+16-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎events.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,7 @@ export const AppMentionSchema = Type.Object({
2323
});
2424
export type AppMention = Static<typeof AppMentionSchema>;
2525

26-
export const SlackEventSchema = Type.Union([ChallengeSchema, AppMentionSchema]);
26+
export const SlackEventSchema = Type.Object({
27+
event: Type.Union([ChallengeSchema, AppMentionSchema]),
28+
});
2729
export type SlackEvent = Static<typeof SlackEventSchema>;

‎main.ts

+33-18
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,45 @@
1-
import { Hono } from "npm:hono";
2-
import { tbValidator } from "npm:@hono/typebox-validator";
1+
import { Hono } from "@hono/hono";
2+
import { validator } from "@hono/hono/validator";
3+
import { logger } from "@hono/hono/logger";
34
import { SlackEventSchema } from "./events.ts";
4-
import { pingCommand } from "./commands/ping.ts";
5+
import { Value as V } from "@sinclair/typebox/value";
6+
import { commands } from "./commands/mod.ts";
57

68
const app = new Hono();
79

8-
const commands = [pingCommand];
9-
10-
app.post("/slack/event", tbValidator("json", SlackEventSchema), async (c) => {
11-
const body = c.req.valid("json");
10+
app.use(logger());
11+
app.post(
12+
"/slack/event",
13+
validator("json", (value, c) => {
14+
if (!V.Check(SlackEventSchema, value)) {
15+
const errors = [...V.Errors(SlackEventSchema, value)].map(
16+
({ path, message }) => ({ path, message })
17+
);
18+
console.log("bad request", errors);
19+
return c.json({ errors }, 400);
20+
}
21+
return V.Cast(SlackEventSchema, value);
22+
}),
23+
async (c) => {
24+
const body = c.req.valid("json");
1225

13-
switch (body.type) {
14-
case "url_verification":
15-
return c.json({ challenge: body.challenge });
26+
switch (body.event.type) {
27+
case "url_verification":
28+
return c.json({ challenge: body.event.challenge });
1629

17-
case "app_mention": {
18-
const command = commands.find((cmd) => cmd.matcher.test(body.text));
19-
if (!command) {
20-
return c.json({ text: "wat?" }, 400);
21-
}
30+
case "app_mention": {
31+
const msg = body.event.text;
32+
const command = commands.find((cmd) => cmd.matcher.test(msg));
33+
if (!command) {
34+
return c.json({ text: "wat?" }, 400);
35+
}
2236

23-
await command.handler(body);
37+
await command.handler(body.event);
2438

25-
return c.text("OK");
39+
return c.text("OK");
40+
}
2641
}
2742
}
28-
});
43+
);
2944

3045
Deno.serve(app.fetch);

0 commit comments

Comments
 (0)
Please sign in to comment.