Skip to content

Commit 07eb78e

Browse files
authored
Merge pull request #1 from purduehackers/ray/discord-stream-api
2 parents 53d97ac + 3b27e3d commit 07eb78e

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

src/env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const env = createEnv({
55
server: {
66
SANITY_PROJECT_ID: z.string(),
77
SANITY_TOKEN: z.string(),
8+
DISCORD_API_KEY: z.string(),
89
PHONE_API_KEY: z.string(),
910
DOOR_OPENER_API_KEY: z.string(),
1011
},

src/routes/discord.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { Elysia } from "elysia";
2+
import type { ElysiaWS } from "elysia/ws";
3+
import { z } from "zod";
4+
5+
import { env } from "../env";
6+
7+
const authMessageSchema = z.object({
8+
token: z.string(),
9+
});
10+
type AuthenticationMessage = z.infer<typeof authMessageSchema>;
11+
12+
const discordMessageSchema = z.object({
13+
image: z.string().url().optional(),
14+
timestamp: z.string(),
15+
username: z.string(),
16+
content: z.string(),
17+
attachments: z.array(z.string()).default([]),
18+
});
19+
type DiscordMessage = z.infer<typeof discordMessageSchema>;
20+
21+
const router = new Elysia();
22+
23+
router.group("/discord", (app) =>
24+
app
25+
.decorate("bots", new BotClientManager())
26+
.decorate("dashboards", new DashboardClientManager())
27+
.ws("/bot", {
28+
message: (ws, data) => {
29+
let result: unknown;
30+
31+
try {
32+
result = JSON.parse(String(data));
33+
} catch {
34+
result = data as unknown;
35+
}
36+
37+
const verified = ws.data.bots.checkVerified(ws);
38+
39+
if (!verified) {
40+
const validation = authMessageSchema.safeParse(result);
41+
if (!validation || !validation.success) return;
42+
43+
const message = validation.data;
44+
45+
if (message.token !== env.DISCORD_API_KEY) {
46+
ws.send(JSON.stringify({ auth: "rejected" }));
47+
ws.close();
48+
return;
49+
}
50+
51+
ws.data.bots.addClient(ws);
52+
ws.send(JSON.stringify({ auth: "complete" }));
53+
return;
54+
}
55+
56+
const validation = discordMessageSchema.safeParse(result);
57+
58+
if (!validation || !validation.success) return;
59+
60+
const message = validation.data;
61+
62+
ws.data.dashboards.sendToConnectedClients(message);
63+
},
64+
close: (ws) => {
65+
const verified = ws.data.bots.checkVerified(ws);
66+
if (verified) {
67+
ws.data.bots.removeClient(ws);
68+
}
69+
},
70+
})
71+
.ws("/dashboard", {
72+
open: (ws) => {
73+
ws.data.dashboards.addClient(ws);
74+
},
75+
close: (ws) => {
76+
ws.data.dashboards.removeClient(ws);
77+
},
78+
}),
79+
);
80+
81+
export default router;
82+
83+
class BotClientManager {
84+
protected clients: Set<ElysiaWS>;
85+
86+
constructor() {
87+
this.clients = new Set();
88+
}
89+
90+
checkVerified(client: ElysiaWS) {
91+
if (this.clients.has(client)) return true;
92+
return false;
93+
}
94+
95+
addClient(client: ElysiaWS) {
96+
this.clients.add(client);
97+
}
98+
99+
removeClient(client: ElysiaWS) {
100+
this.clients.delete(client);
101+
}
102+
103+
sendMessageToClient(client: ElysiaWS, message: DiscordMessage) {
104+
return client.send(JSON.stringify(message));
105+
}
106+
107+
sendToConnectedClients(message: DiscordMessage) {
108+
for (const client of this.clients) {
109+
this.sendMessageToClient(client, message);
110+
}
111+
}
112+
}
113+
114+
class DashboardClientManager {
115+
protected clients: Set<ElysiaWS>;
116+
117+
constructor() {
118+
this.clients = new Set();
119+
}
120+
121+
addClient(client: ElysiaWS) {
122+
this.clients.add(client);
123+
}
124+
125+
removeClient(client: ElysiaWS) {
126+
this.clients.delete(client);
127+
}
128+
129+
sendMessageToClient(client: ElysiaWS, message: DiscordMessage) {
130+
return client.send(JSON.stringify(message));
131+
}
132+
133+
sendToConnectedClients(message: DiscordMessage) {
134+
for (const client of this.clients) {
135+
this.sendMessageToClient(client, message);
136+
}
137+
}
138+
}

0 commit comments

Comments
 (0)