a fun bot for the hc slack
1import { SlackApp } from "slack-edge";
2
3import { takes } from "./features/index";
4
5import { t } from "./libs/template";
6import { blog } from "./libs/Logger";
7import { version, name } from "../package.json";
8import { apiRouter, video } from "./features/api";
9const environment = process.env.NODE_ENV;
10
11import * as Sentry from "@sentry/bun";
12
13// Check required environment variables
14const requiredVars = [
15 "SLACK_BOT_TOKEN",
16 "SLACK_SIGNING_SECRET",
17 "SLACK_REVIEW_CHANNEL",
18 "SLACK_LOG_CHANNEL",
19 "SLACK_SPAM_CHANNEL",
20 "SLACK_USER_TOKEN",
21 "API_URL",
22 "SENTRY_DSN",
23] as const;
24const missingVars = requiredVars.filter((varName) => !process.env[varName]);
25
26if (missingVars.length > 0) {
27 throw new Error(
28 `Missing required environment variables: ${missingVars.join(", ")}`,
29 );
30}
31
32if (process.env.NODE_ENV === "production") {
33 Sentry.init({
34 dsn: process.env.SENTRY_DSN,
35 environment,
36 release: version,
37 sampleRate: 0.1,
38 });
39}
40
41console.log(
42 `----------------------------------\n${name} Server\n----------------------------------\n`,
43);
44console.log(`🏗️ Starting ${name}...`);
45console.log("📦 Loading Slack App...");
46console.log("🔑 Loading environment variables...");
47
48const slackApp = new SlackApp({
49 env: {
50 SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN as string,
51 SLACK_SIGNING_SECRET: process.env.SLACK_SIGNING_SECRET as string,
52 SLACK_LOGGING_LEVEL: "INFO",
53 },
54 startLazyListenerAfterAck: true,
55});
56const slackClient = slackApp.client;
57
58takes();
59
60export default {
61 port: process.env.PORT || 3000,
62 development: environment === "dev",
63 async fetch(request: Request) {
64 const url = new URL(request.url);
65 const path = url.pathname.split("/").filter(Boolean)[0]
66 ? `/${url.pathname.split("/").filter(Boolean)[0]}`
67 : "/";
68
69 // CORS headers to allow all origins
70 const corsHeaders = {
71 "Access-Control-Allow-Origin": "*",
72 "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
73 "Access-Control-Allow-Headers": "Content-Type, Authorization",
74 };
75
76 // Handle preflight OPTIONS request
77 if (request.method === "OPTIONS") {
78 return new Response(null, {
79 status: 204,
80 headers: corsHeaders,
81 });
82 }
83
84 let response: Response;
85 switch (path) {
86 case "/":
87 response = new Response(`Hello World from ${name}@${version}`);
88 break;
89 case "/health":
90 response = new Response("OK");
91 break;
92 case "/slack":
93 response = await slackApp.run(request);
94 break;
95 case "/api":
96 response = await apiRouter(url);
97 break;
98 default:
99 response = new Response("404 Not Found", { status: 404 });
100 }
101
102 // Add CORS headers to all responses
103 const newHeaders = new Headers(response.headers);
104 for (const [key, value] of Object.entries(corsHeaders)) {
105 newHeaders.set(key, value);
106 }
107
108 return new Response(response.body, {
109 status: response.status,
110 statusText: response.statusText,
111 headers: newHeaders,
112 });
113 },
114};
115
116console.log(
117 `🚀 Server Started in ${
118 Bun.nanoseconds() / 1000000
119 } milliseconds on version: ${version}!\n\n----------------------------------\n`,
120);
121
122blog(
123 t("app.startup", {
124 environment,
125 }),
126 "start",
127 {
128 channel: process.env.SLACK_SPAM_CHANNEL || "",
129 },
130);
131
132console.log("\n----------------------------------\n");
133
134export { slackApp, slackClient, version, name, environment };