diff options
| author | Fuwn <[email protected]> | 2025-10-23 23:36:36 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-10-23 23:36:39 -0700 |
| commit | 46c40a53a569f5f27de016bc0215bf30dcdba1a5 (patch) | |
| tree | bec0ed98689296b6358e1f1c38c20ba6df01705b /packages | |
| parent | feat(gateway:listeners): Add auto message deletion (diff) | |
| download | umabotdiscord-46c40a53a569f5f27de016bc0215bf30dcdba1a5.tar.xz umabotdiscord-46c40a53a569f5f27de016bc0215bf30dcdba1a5.zip | |
feat(gateway:listeners): Add role persistence
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/gateway/bun.lock | 71 | ||||
| -rw-r--r-- | packages/gateway/package.json | 3 | ||||
| -rw-r--r-- | packages/gateway/prisma/migrations/20251024062804_init/migration.sql | 17 | ||||
| -rw-r--r-- | packages/gateway/prisma/migrations/migration_lock.toml | 3 | ||||
| -rw-r--r-- | packages/gateway/prisma/schema.prisma | 20 | ||||
| -rw-r--r-- | packages/gateway/src/database/prisma.ts | 9 | ||||
| -rw-r--r-- | packages/gateway/src/database/rolePersistence.ts | 118 | ||||
| -rw-r--r-- | packages/gateway/src/listeners/index.ts | 2 | ||||
| -rw-r--r-- | packages/gateway/src/listeners/memberJoin.ts | 8 | ||||
| -rw-r--r-- | packages/gateway/src/listeners/memberLeave.ts | 18 |
10 files changed, 268 insertions, 1 deletions
diff --git a/packages/gateway/bun.lock b/packages/gateway/bun.lock index e23ad2d..1a60726 100644 --- a/packages/gateway/bun.lock +++ b/packages/gateway/bun.lock @@ -5,6 +5,8 @@ "name": "umabot-gateway-client", "dependencies": { "@discordjs/voice": "^0.19.0", + "@prisma/client": "^6.18.0", + "@prisma/extension-accelerate": "^2.0.2", "@snazzah/davey": "^0.1.7", "@types/ws": "^8.18.1", "discord.js": "^14.14.1", @@ -12,6 +14,7 @@ "iqdb-client": "^3.0.0", "libsodium-wrappers": "^0.7.15", "openai": "^6.1.0", + "prisma": "^6.18.0", "sodium": "^3.0.2", "ws": "^8.18.3", }, @@ -97,6 +100,22 @@ "@napi-rs/wasm-runtime": ["@napi-rs/[email protected]", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg=="], + "@prisma/client": ["@prisma/[email protected]", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-jnL2I9gDnPnw4A+4h5SuNn8Gc+1mL1Z79U/3I9eE2gbxJG1oSA+62ByPW4xkeDgwE0fqMzzpAZ7IHxYnLZ4iQA=="], + + "@prisma/config": ["@prisma/[email protected]", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.18.4", "empathic": "2.0.0" } }, "sha512-rgFzspCpwsE+q3OF/xkp0fI2SJ3PfNe9LLMmuSVbAZ4nN66WfBiKqJKo/hLz3ysxiPQZf8h1SMf2ilqPMeWATQ=="], + + "@prisma/debug": ["@prisma/[email protected]", "", {}, "sha512-PMVPMmxPj0ps1VY75DIrT430MoOyQx9hmm174k6cmLZpcI95rAPXOQ+pp8ANQkJtNyLVDxnxVJ0QLbrm/ViBcg=="], + + "@prisma/engines": ["@prisma/[email protected]", "", { "dependencies": { "@prisma/debug": "6.18.0", "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", "@prisma/fetch-engine": "6.18.0", "@prisma/get-platform": "6.18.0" } }, "sha512-i5RzjGF/ex6AFgqEe2o1IW8iIxJGYVQJVRau13kHPYEL1Ck8Zvwuzamqed/1iIljs5C7L+Opiz5TzSsUebkriA=="], + + "@prisma/engines-version": ["@prisma/engines-version@6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", "", {}, "sha512-T7Af4QsJQnSgWN1zBbX+Cha5t4qjHRxoeoWpK4JugJzG/ipmmDMY5S+O0N1ET6sCBNVkf6lz+Y+ZNO9+wFU8pQ=="], + + "@prisma/extension-accelerate": ["@prisma/[email protected]", "", { "peerDependencies": { "@prisma/client": ">=4.16.1" } }, "sha512-yZK6/k7uOEFpEsKoZezQS1CKDboPtBCQ0NyI70e1Un8tDiRgg80iWGyjsJmRpps2ZIut3MroHP+dyR3wVKh8lA=="], + + "@prisma/fetch-engine": ["@prisma/[email protected]", "", { "dependencies": { "@prisma/debug": "6.18.0", "@prisma/engines-version": "6.18.0-8.34b5a692b7bd79939a9a2c3ef97d816e749cda2f", "@prisma/get-platform": "6.18.0" } }, "sha512-TdaBvTtBwP3IoqVYoGIYpD4mWlk0pJpjTJjir/xLeNWlwog7Sl3bD2J0jJ8+5+q/6RBg+acb9drsv5W6lqae7A=="], + + "@prisma/get-platform": ["@prisma/[email protected]", "", { "dependencies": { "@prisma/debug": "6.18.0" } }, "sha512-uXNJCJGhxTCXo2B25Ta91Rk1/Nmlqg9p7G9GKh8TPhxvAyXCvMNQoogj4JLEUy+3ku8g59cpyQIKFhqY2xO2bg=="], + "@sapphire/async-queue": ["@sapphire/[email protected]", "", {}, "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg=="], "@sapphire/shapeshift": ["@sapphire/[email protected]", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "lodash": "^4.17.21" } }, "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg=="], @@ -133,6 +152,8 @@ "@snazzah/davey-win32-x64-msvc": ["@snazzah/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-Z2NhImUYeApi/lNn7MBcn14dPa2dtgnp5taz43JDaPpl+2cinDm9kYjpFzJE9SZMlfsa//p2dhE9B8TEVi9bHQ=="], + "@standard-schema/spec": ["@standard-schema/[email protected]", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@tybys/wasm-util": ["@tybys/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], "@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ=="], @@ -143,14 +164,30 @@ "boolbase": ["[email protected]", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + "c12": ["[email protected]", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="], + "cheerio": ["[email protected]", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.0.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.12.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg=="], "cheerio-select": ["[email protected]", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], + "chokidar": ["[email protected]", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "citty": ["[email protected]", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="], + + "confbox": ["[email protected]", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="], + + "consola": ["[email protected]", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], + "css-select": ["[email protected]", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], "css-what": ["[email protected]", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], + "deepmerge-ts": ["[email protected]", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="], + + "defu": ["[email protected]", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + + "destr": ["[email protected]", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="], + "discord-api-types": ["[email protected]", "", {}, "sha512-xpmPviHjIJ6dFu1eNwNDIGQ3N6qmPUUYFVAx/YZ64h7ZgPkTcKjnciD8bZe8Vbeji7yS5uYljyciunpq0J5NSw=="], "discord.js": ["[email protected]", "", { "dependencies": { "@discordjs/builders": "^1.11.2", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.6.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.16", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-3k+Kisd/v570Jr68A1kNs7qVhNehDwDJAPe4DZ2Syt+/zobf9zEcuYFvsfIaAOgCa0BiHMfOOKQY4eYINl0z7w=="], @@ -165,24 +202,36 @@ "dotenv": ["[email protected]", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "effect": ["[email protected]", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA=="], + + "empathic": ["[email protected]", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], + "encoding-sniffer": ["[email protected]", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], "entities": ["[email protected]", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "esbuild": ["[email protected]", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], + "exsolve": ["[email protected]", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], + + "fast-check": ["[email protected]", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], + "fast-deep-equal": ["[email protected]", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "get-tsconfig": ["[email protected]", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], + "giget": ["[email protected]", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="], + "htmlparser2": ["[email protected]", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.1", "entities": "^6.0.0" } }, "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g=="], "iconv-lite": ["[email protected]", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "iqdb-client": ["[email protected]", "", { "dependencies": { "cheerio": "^1.0.0-rc.12" } }, "sha512-Rr5lNUBeJ663ufMtRhnBVuU0H4HuoyqjdCPz7cbX7oSpIi3KlKqzxYF1xPZWJ/TFtDh4DfM47T57WgFB6wh5DQ=="], + "jiti": ["[email protected]", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "libsodium": ["[email protected]", "", {}, "sha512-sZwRknt/tUpE2AwzHq3jEyUU5uvIZHtSssktXq7owd++3CSgn8RGrv6UZJJBpP7+iBghBqe7Z06/2M31rI2NKw=="], "libsodium-wrappers": ["[email protected]", "", { "dependencies": { "libsodium": "^0.7.15" } }, "sha512-E4anqJQwcfiC6+Yrl01C1m8p99wEhLmJSs0VQqST66SbQXXBoaJY0pF4BNjRYa/sOQAxx6lXAaAFIlx+15tXJQ=="], @@ -195,8 +244,14 @@ "node-addon-api": ["[email protected]", "", {}, "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A=="], + "node-fetch-native": ["[email protected]", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="], + "nth-check": ["[email protected]", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + "nypm": ["[email protected]", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g=="], + + "ohash": ["[email protected]", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], + "openai": ["[email protected]", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-5sqb1wK67HoVgGlsPwcH2bUbkg66nnoIYKoyV9zi5pZPqh7EWlmSrSDjAh4O5jaIg/0rIlcDKBtWvZBuacmGZg=="], "parse5": ["[email protected]", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], @@ -205,14 +260,30 @@ "parse5-parser-stream": ["[email protected]", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], + "pathe": ["[email protected]", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "perfect-debounce": ["[email protected]", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], + + "pkg-types": ["[email protected]", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="], + "prism-media": ["[email protected]", "", { "peerDependencies": { "@discordjs/opus": ">=0.8.0 <1.0.0", "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", "node-opus": "^0.3.3", "opusscript": "^0.0.8" }, "optionalPeers": ["@discordjs/opus", "ffmpeg-static", "node-opus", "opusscript"] }, "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA=="], + "prisma": ["[email protected]", "", { "dependencies": { "@prisma/config": "6.18.0", "@prisma/engines": "6.18.0" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-bXWy3vTk8mnRmT+SLyZBQoC2vtV9Z8u7OHvEu+aULYxwiop/CPiFZ+F56KsNRNf35jw+8wcu8pmLsjxpBxAO9g=="], + + "pure-rand": ["[email protected]", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + + "rc9": ["[email protected]", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], + + "readdirp": ["[email protected]", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "resolve-pkg-maps": ["[email protected]", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], "safer-buffer": ["[email protected]", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "sodium": ["[email protected]", "", { "dependencies": { "node-addon-api": "*" } }, "sha512-IsTwTJeoNBU97km3XkrbCGC/n/9aUQejgD3QPr2YY2gtbSPru3TI6nhCqgoez9Mv88frF9oVZS/jrXFbd6WXyA=="], + "tinyexec": ["[email protected]", "", {}, "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="], + "ts-mixer": ["[email protected]", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="], "tslib": ["[email protected]", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], diff --git a/packages/gateway/package.json b/packages/gateway/package.json index db22d09..bab9e7e 100644 --- a/packages/gateway/package.json +++ b/packages/gateway/package.json @@ -12,6 +12,8 @@ }, "dependencies": { "@discordjs/voice": "^0.19.0", + "@prisma/client": "^6.18.0", + "@prisma/extension-accelerate": "^2.0.2", "@snazzah/davey": "^0.1.7", "@types/ws": "^8.18.1", "discord.js": "^14.14.1", @@ -19,6 +21,7 @@ "iqdb-client": "^3.0.0", "libsodium-wrappers": "^0.7.15", "openai": "^6.1.0", + "prisma": "^6.18.0", "sodium": "^3.0.2", "ws": "^8.18.3" }, diff --git a/packages/gateway/prisma/migrations/20251024062804_init/migration.sql b/packages/gateway/prisma/migrations/20251024062804_init/migration.sql new file mode 100644 index 0000000..ffb366a --- /dev/null +++ b/packages/gateway/prisma/migrations/20251024062804_init/migration.sql @@ -0,0 +1,17 @@ +-- CreateTable +CREATE TABLE "UserRole" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "guildId" TEXT NOT NULL, + "roleId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "UserRole_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "UserRole_userId_guildId_idx" ON "UserRole"("userId", "guildId"); + +-- CreateIndex +CREATE UNIQUE INDEX "UserRole_userId_guildId_roleId_key" ON "UserRole"("userId", "guildId", "roleId"); diff --git a/packages/gateway/prisma/migrations/migration_lock.toml b/packages/gateway/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/packages/gateway/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/packages/gateway/prisma/schema.prisma b/packages/gateway/prisma/schema.prisma new file mode 100644 index 0000000..922ca6f --- /dev/null +++ b/packages/gateway/prisma/schema.prisma @@ -0,0 +1,20 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model UserRole { + id String @id @default(cuid()) + userId String + guildId String + roleId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([userId, guildId, roleId]) + @@index([userId, guildId]) +} diff --git a/packages/gateway/src/database/prisma.ts b/packages/gateway/src/database/prisma.ts new file mode 100644 index 0000000..d9ffaf3 --- /dev/null +++ b/packages/gateway/src/database/prisma.ts @@ -0,0 +1,9 @@ +import { PrismaClient } from "@prisma/client"; +import { withAccelerate } from "@prisma/extension-accelerate"; +import dotenv from "dotenv"; + +dotenv.config({ path: "../../.dev.vars" }); + +const prisma = new PrismaClient().$extends(withAccelerate()); + +export default prisma; diff --git a/packages/gateway/src/database/rolePersistence.ts b/packages/gateway/src/database/rolePersistence.ts new file mode 100644 index 0000000..cd62e6e --- /dev/null +++ b/packages/gateway/src/database/rolePersistence.ts @@ -0,0 +1,118 @@ +/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */ + +import { GuildMember, Role } from "discord.js"; +import prisma from "../database/prisma"; +import { log, LogLevel, logUnexpectedDiscordAPIError } from "../utilities"; + +export class RolePersistenceService { + static async saveUserRoles(member: GuildMember): Promise<void> { + try { + const userId = member.user.id; + const guildId = member.guild.id; + const roles = member.roles.cache.filter( + (role) => role.id !== member.guild.id, + ); + + if (roles.size === 0) return; + + await prisma.userRole.deleteMany({ + where: { + userId, + guildId, + }, + }); + + const roleData = Array.from(roles.keys()).map((roleId) => ({ + userId, + guildId, + roleId, + })); + + await prisma.userRole.createMany({ + data: roleData, + }); + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + } + + static async restoreUserRoles(member: GuildMember): Promise<void> { + try { + const userId = member.user.id; + const guildId = member.guild.id; + const savedRoles = await prisma.userRole.findMany({ + where: { + userId, + guildId, + }, + }); + + if (savedRoles.length === 0) return; + + const validRoles: Role[] = []; + + for (const savedRole of savedRoles) { + const role = member.guild.roles.cache.get(savedRole.roleId); + + if (role && role.id !== member.guild.id) validRoles.push(role); + } + + if (validRoles.length === 0) { + await prisma.userRole.deleteMany({ + where: { + userId, + guildId, + }, + }); + + return; + } + + await member.roles.add(validRoles); + await prisma.userRole.deleteMany({ + where: { + userId, + guildId, + }, + }); + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + } + + static async clearUserRoles(userId: string, guildId: string): Promise<void> { + try { + await prisma.userRole.deleteMany({ + where: { + userId, + guildId, + }, + }); + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + } + + static async getSavedRoles( + userId: string, + guildId: string, + ): Promise<string[]> { + try { + const savedRoles = await prisma.userRole.findMany({ + where: { + userId, + guildId, + }, + select: { + roleId: true, + }, + }); + + return savedRoles.map((role: { roleId: string }) => role.roleId); + } catch (error) { + logUnexpectedDiscordAPIError(error); + + return []; + } + } +} diff --git a/packages/gateway/src/listeners/index.ts b/packages/gateway/src/listeners/index.ts index 7547d4f..21da227 100644 --- a/packages/gateway/src/listeners/index.ts +++ b/packages/gateway/src/listeners/index.ts @@ -6,6 +6,7 @@ import { handleMessageDeletion } from "./messageDeletion"; import { handleMessageEdit } from "./messageEdit"; import { handleClientReady } from "./clientReady"; import { handleMemberJoin } from "./memberJoin"; +import { handleMemberLeave } from "./memberLeave"; import { handleTimeoutMirroring } from "./timeoutMirroring"; import { handleAutoDeletion } from "./autoDeletion"; // import { handleMediaModeration } from "./mediaModeration"; @@ -18,6 +19,7 @@ export const handleListeners = (client: Client) => { handleMessageDeletion(client); handleMessageEdit(client); handleMemberJoin(client); + handleMemberLeave(client); handleTimeoutMirroring(client); handleAutoDeletion(client); // handleMediaModeration(client); diff --git a/packages/gateway/src/listeners/memberJoin.ts b/packages/gateway/src/listeners/memberJoin.ts index cceae35..97df730 100644 --- a/packages/gateway/src/listeners/memberJoin.ts +++ b/packages/gateway/src/listeners/memberJoin.ts @@ -7,6 +7,7 @@ import { import { WELCOME_GREETINGS, WELCOME_ENDINGS } from "./messageCreate/constants"; import { logUnexpectedDiscordAPIError } from "../utilities"; import { logUnexpectedDiscordAPIResult } from "../../../shared/log"; +import { RolePersistenceService } from "../database/rolePersistence"; const CENTRAL_WELCOME_CHANNEL_ID = "1406422619087044670"; const ROLEPLAY_WELCOME_CHANNEL_ID = "1423919140606971927"; @@ -29,7 +30,6 @@ class WelcomeMessageSystem { private async initializePersistentWebhook() { if (!this.client) return; - // Initialize central server webhook try { const centralChannel = this.client.channels.cache.get( CENTRAL_WELCOME_CHANNEL_ID, @@ -99,6 +99,12 @@ class WelcomeMessageSystem { public async handleMemberJoin(member: GuildMember): Promise<void> { if (!this.client) await this.setClient(member.client); + try { + await RolePersistenceService.restoreUserRoles(member); + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + if ( member.guild.id !== CENTRAL_GUILD_ID && member.guild.id !== ROLEPLAY_GUILD_ID diff --git a/packages/gateway/src/listeners/memberLeave.ts b/packages/gateway/src/listeners/memberLeave.ts new file mode 100644 index 0000000..e348917 --- /dev/null +++ b/packages/gateway/src/listeners/memberLeave.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */ + +import { Client, Events, GuildMember } from "discord.js"; +import { RolePersistenceService } from "../database/rolePersistence"; +import { log, LogLevel, logUnexpectedDiscordAPIError } from "../utilities"; + +export const handleMemberLeave = (client: Client) => { + client.on(Events.GuildMemberRemove, async (member) => { + try { + if (!member.user) return; + + if ("roles" in member) + await RolePersistenceService.saveUserRoles(member as GuildMember); + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + }); +}; |