Skip to content

Commit 913ee1f

Browse files
committed
feat: just a spoonful of state machines
1 parent 2e37604 commit 913ee1f

24 files changed

+397
-189
lines changed

CONTRIBUTING.md

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ docker compose up --build
2121
docker compose exec bot yarn prisma db push
2222
```
2323

24+
```
25+
/pair channel:UC5CwaMl1eIgY8h02uZw7u8A role:@☄️Hoshiyomi
26+
```
27+
2428
## Deploy Guide (Maintainers only)
2529

2630
```bash

package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
"scripts": {
77
"build": "tsc",
88
"clean": "shx rm -rf lib && docker-compose -f docker-compose.production.yml down --rmi local",
9+
"db:push": "docker compose exec bot yarn prisma db push",
910
"deploy-commands": "ts-node src/discord/deploy-commands.ts",
1011
"dev": "run-p dev:*",
1112
"dev:prisma": "prisma generate --watch",
12-
"dev:server": "nodemon -w lib .",
13+
"dev:server": "nodemon -w lib -d 3 -C .",
1314
"dev:tsc": "tsc -w --preserveWatchOutput",
1415
"prepare": "husky install",
1516
"start": "node ."
@@ -31,6 +32,7 @@
3132
"express": "^4.18.1",
3233
"jsonwebtoken": "^8.5.1",
3334
"luxon": "^3.0.1",
35+
"masterchat": "^1.1.0",
3436
"mongoose": "~6.4.4",
3537
"node-schedule": "^2.1.0",
3638
"prisma": "^4.0.0"
@@ -44,7 +46,6 @@
4446
"@types/node": "^18.0.6",
4547
"@types/node-schedule": "^2.1.0",
4648
"husky": "^8.0.1",
47-
"masterchat": "^1.1.0",
4849
"nodemon": "^2.0.19",
4950
"npm-run-all": "^4.1.5",
5051
"prettier": "^2.7.1",

prisma/schema.prisma

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ model Tie {
2424
model Attestation {
2525
id Int @id @default(autoincrement())
2626
attestedAt DateTime @default(now())
27-
valid Boolean
28-
since String?
2927
originChannelId String
28+
isMember Boolean
29+
since String?
3030
user User @relation(fields: [userDiscordId], references: [discordId])
3131
userDiscordId String
3232

src/db.ts

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { Attestation, PrismaClient, Tie, User } from "@prisma/client";
22

3+
export interface AttestationOptions {
4+
user: User;
5+
originChannelId: string;
6+
isMember: boolean;
7+
since?: string;
8+
}
9+
310
const client = new PrismaClient();
411

512
export async function getUserByDiscordId(discordId: string) {
613
return await client.user.findFirst({ where: { discordId } });
714
}
815

9-
export async function createOrUpdateUser({
16+
export async function upsertUser({
1017
discordId,
1118
youtubeChannelId,
1219
}: {
@@ -25,7 +32,7 @@ export async function createOrUpdateUser({
2532
return newOrUpdatedUser;
2633
}
2734

28-
export async function createPair({
35+
export async function createTie({
2936
guildId,
3037
roleId,
3138
originChannelId,
@@ -40,12 +47,12 @@ export async function createPair({
4047
return pair;
4148
}
4249

43-
export async function getPairsForGuild(guildId: string): Promise<Tie[]> {
50+
export async function getTiesForGuild(guildId: string): Promise<Tie[]> {
4451
const pairs = await client.tie.findMany({ where: { guildId } });
4552
return pairs;
4653
}
4754

48-
export async function findCertificate({
55+
export async function findAttestation({
4956
user,
5057
originChannelId,
5158
}: {
@@ -57,25 +64,18 @@ export async function findCertificate({
5764
});
5865
}
5966

60-
export interface ModifyOptions {
61-
user: User;
62-
originChannelId: string;
63-
valid: boolean;
64-
since?: string;
65-
}
66-
67-
export async function createOrUpdateCertificate({
67+
export async function upsertAttestation({
6868
user,
6969
originChannelId,
70-
valid,
70+
isMember,
7171
since,
72-
}: ModifyOptions): Promise<Attestation> {
73-
const cert = await findCertificate({ user, originChannelId });
72+
}: AttestationOptions): Promise<Attestation> {
73+
const cert = await findAttestation({ user, originChannelId });
7474
if (cert) {
7575
const updated = await client.attestation.update({
7676
where: { id: cert.id },
7777
data: {
78-
valid,
78+
isMember,
7979
since: since ?? null,
8080
},
8181
});
@@ -85,10 +85,10 @@ export async function createOrUpdateCertificate({
8585
const newCert = await client.attestation.create({
8686
data: {
8787
user: {
88-
connect: user,
88+
connect: { discordId: user.discordId },
8989
},
9090
originChannelId,
91-
valid,
91+
isMember,
9292
since,
9393
},
9494
});

src/discord/bot.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Client, GatewayIntentBits } from "discord.js";
22
import { HONEYBEE_URI } from "../constants";
3-
import { Honeybee } from "../honeybee";
3+
import { Honeybee } from "../notaries/chat";
44
import { debugLog, log } from "../util";
55
import { commands } from "./commands";
66
import { CommandContext } from "./interfaces";

src/discord/commands/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Command } from "../interfaces";
22
import pair from "./pair";
3-
import verify from "./verify";
3+
import verify from "./reverify";
44

55
export const commands: Command[] = [verify, pair];

src/discord/commands/pair.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import {
55
CommandInteraction,
66
PermissionsBitField,
77
} from "discord.js";
8-
import { createPair } from "../../db";
8+
import { createTie } from "../../db";
99
import { debugLog } from "../../util";
1010

1111
export default {
1212
data: new SlashCommandBuilder()
1313
.setName("pair")
14-
.setDescription("Pair role with YouTube channel")
14+
.setDescription("Pair membership role with YouTube channel")
1515
.addStringOption((option) =>
1616
option
1717
.setName("channel")
@@ -36,13 +36,13 @@ export default {
3636

3737
debugLog("channel:", channel, "roleName:", role);
3838

39-
const pair = await createPair({
39+
const tie = await createTie({
4040
guildId: guild.id,
4141
roleId: role.id,
4242
originChannelId: channel,
4343
});
4444

45-
debugLog(pair);
45+
debugLog(tie);
4646

4747
await intr.reply({
4848
content: `Success:

src/discord/commands/reverify.ts

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { SlashCommandBuilder } from "@discordjs/builders";
2+
import { debugLog } from "../../util";
3+
import { Command, RoleChangeset } from "../interfaces";
4+
import { applyRoleChangesState } from "../states/applyRoles";
5+
import { notifyState } from "../states/notify";
6+
import { onboardState } from "../states/onboard";
7+
import { verifyWithChatState } from "../states/verifyWithChat";
8+
import { verifyWithCommentState } from "../states/verifyWithComment";
9+
10+
export enum State {
11+
START,
12+
END,
13+
ERROR,
14+
ONBOARD,
15+
VERIFY_WITH_CHAT,
16+
VERIFY_WITH_COMMENT,
17+
REGISTER_COMMENT_ID,
18+
APPLY_ROLE_CHANGES,
19+
NOTIFY,
20+
}
21+
22+
export interface StateContext {
23+
rolesToChange?: RoleChangeset[];
24+
error?: string;
25+
}
26+
27+
const command: Command = {
28+
data: new SlashCommandBuilder()
29+
.setName("reverify")
30+
.setDescription("Force verify membership status"),
31+
32+
async execute(intr, { hb }) {
33+
debugLog("reverify:", intr.user.username);
34+
35+
const initialState = State.VERIFY_WITH_CHAT;
36+
37+
let currentState: State = initialState;
38+
let context: StateContext = {};
39+
40+
while (currentState !== State.END) {
41+
let next!: [State, StateContext];
42+
switch (currentState) {
43+
case State.VERIFY_WITH_CHAT:
44+
next = await verifyWithChatState(intr, context);
45+
break;
46+
case State.VERIFY_WITH_COMMENT:
47+
next = await verifyWithCommentState(intr, context);
48+
break;
49+
case State.ONBOARD:
50+
next = await onboardState(intr, context);
51+
break;
52+
case State.APPLY_ROLE_CHANGES:
53+
next = await applyRoleChangesState(intr, context);
54+
break;
55+
case State.NOTIFY:
56+
next = await notifyState(intr, context);
57+
break;
58+
default:
59+
throw new Error(`Unrecognized state: ${currentState}`);
60+
}
61+
currentState = next[0];
62+
context = { ...context, ...next[1] };
63+
}
64+
},
65+
};
66+
67+
export default command;

src/discord/commands/verify.ts

-66
This file was deleted.

src/discord/interfaces.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SlashCommandBuilder } from "@discordjs/builders";
22
import { ChatInputCommandInteraction } from "discord.js";
3-
import { Honeybee } from "../honeybee";
3+
import { Honeybee } from "../notaries/chat";
44

55
export interface CommandContext {
66
hb: Honeybee;
@@ -18,7 +18,7 @@ export interface Command {
1818

1919
export interface RoleChangeset {
2020
roleId: string;
21-
valid: boolean;
21+
isMember: boolean;
2222
since: string | null;
2323
}
2424

src/discord/phases/applyRoles.ts

-31
This file was deleted.

src/discord/phases/onboarding.ts

-21
This file was deleted.

0 commit comments

Comments
 (0)