A discord bot for teal.fm
discord tealfm music

monorepo

besaid.zone 58749f93 d628d3cf

verified
+1 -1
.gitignore
··· 1 1 .env 2 - node_modules 2 + **/node_modules 3 3 dist
+21
LICENSE
··· 1 + MIT License 2 + 3 + Copyright (c) 2025 Dane Miller 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE.
+22
apps/bot/package.json
··· 1 + { 2 + "name": "bot", 3 + "version": "0.0.1", 4 + "type": "module", 5 + "private": true, 6 + "scripts": { 7 + "dev": "tsx main.ts", 8 + "deploy-commands": "tsx deploy-commands.ts", 9 + "build": "tsc", 10 + "typecheck": "tsc --noEmit" 11 + }, 12 + "dependencies": { 13 + "@tealfmbot/common": "workspace:*", 14 + "@tealfmbot/tsconfig": "workspace:*", 15 + "discord.js": "^14.25.1" 16 + }, 17 + "devDependencies": { 18 + "@types/node": "^25.0.3", 19 + "tsx": "^4.21.0", 20 + "typescript": "^5.9.3" 21 + } 22 + }
+9
apps/bot/tsconfig.json
··· 1 + { 2 + "extends": "@tealfmbot/tsconfig/tsconfig.node.json", 3 + "compilerOptions": { 4 + "outDir": "./dist", 5 + }, 6 + "exclude": [ 7 + "node_modules" 8 + ] 9 + }
+21
apps/tapper/package.json
··· 1 + { 2 + "name": "tapper", 3 + "version": "0.0.1", 4 + "type": "module", 5 + "private": true, 6 + "scripts": { 7 + "dev": "NODE_ENV=development tsx index.ts", 8 + "start": "NODE_ENV=production tsx index.ts", 9 + "typecheck": "tsc --noEmit" 10 + }, 11 + "dependencies": { 12 + "@atproto/tap": "^0.0.2", 13 + "@tealfmbot/common": "workspace:*", 14 + "@tealfmbot/tsconfig": "workspace:*" 15 + }, 16 + "devDependencies": { 17 + "@types/node": "^25.0.3", 18 + "tsx": "^4.21.0", 19 + "typescript": "^5.9.3" 20 + } 21 + }
+9
apps/tapper/tsconfig.json
··· 1 + { 2 + "extends": "@tealfmbot/tsconfig/tsconfig.node.json", 3 + "compilerOptions": { 4 + "outDir": "./dist" 5 + }, 6 + "exclude": [ 7 + "node_modules" 8 + ] 9 + }
+1 -1
commands/auth.ts apps/bot/commands/auth.ts
··· 1 1 import { CommandInteraction, SlashCommandBuilder } from "discord.js"; 2 - import { logger } from "../logger.ts"; 2 + import { logger } from "@tealfmbot/common/logger.ts"; 3 3 4 4 export default { 5 5 data: new SlashCommandBuilder().setName("auth").setDescription(
+1 -1
commands/ping.ts apps/bot/commands/ping.ts
··· 1 1 import { CommandInteraction, SlashCommandBuilder } from "discord.js"; 2 - import { logger } from "../logger.ts"; 2 + import { logger } from "@tealfmbot/common/logger.ts"; 3 3 4 4 export default { 5 5 data: new SlashCommandBuilder().setName("ping").setDescription(
+1 -1
commands/top.ts apps/bot/commands/top.ts
··· 1 1 import { CommandInteraction, SlashCommandBuilder } from "discord.js"; 2 - import { logger } from "../logger.ts"; 2 + import { logger } from "@tealfmbot/common/logger.ts"; 3 3 4 4 export default { 5 5 data: new SlashCommandBuilder().setName("top").setDescription(
+2 -1
constants.ts packages/common/constants.ts
··· 1 1 import { loadEnvFile } from "node:process" 2 + import path from "node:path" 2 3 3 - loadEnvFile() 4 + loadEnvFile(path.join(import.meta.dirname, "../../.env")) 4 5 5 6 export const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN as string; 6 7 export const DISCORD_APPLICATION_ID = process.env.DISCORD_APPLICATION_ID as string;
+1 -1
deploy-commands.ts apps/bot/deploy-commands.ts
··· 5 5 DISCORD_APPLICATION_ID, 6 6 DISCORD_BOT_TOKEN, 7 7 DISCORD_GUILD_ID, 8 - } from "./constants.ts"; 8 + } from "@tealfmbot/common/constants.ts"; 9 9 10 10 const commands = []; 11 11 const commandPaths = fs.globSync("commands/**/*.ts");
discord.d.ts apps/bot/discord.d.ts
+1 -1
kysely/db.ts packages/database/db.ts
··· 1 1 import type { DB } from "kysely-codegen" 2 2 import { Pool } from "pg" 3 3 import { Kysely, PostgresDialect } from "kysely" 4 - import { DATABASE_URL } from "../constants.ts" 4 + import { DATABASE_URL } from "@tealfmbot/common/constants.ts" 5 5 6 6 const dialect = new PostgresDialect({ 7 7 pool: new Pool({
+1 -1
kysely/migrator.ts packages/database/migrator.ts
··· 3 3 import fs from "node:fs/promises" 4 4 import type { DB } from "kysely-codegen" 5 5 import { Kysely, Migrator, PostgresDialect, FileMigrationProvider } from "kysely" 6 - import { DATABASE_URL } from "../constants.ts" 6 + import { DATABASE_URL } from "@tealfmbot/common/constants.ts" 7 7 8 8 async function migrateToLatest() { 9 9 const db = new Kysely<DB>({
kysely/seed.ts packages/database/seed.ts
logger.ts packages/common/logger.ts
+2 -2
main.ts apps/bot/main.ts
··· 7 7 } from "discord.js"; 8 8 import fs from "node:fs"; 9 9 import path from "node:path"; 10 - import { DISCORD_BOT_TOKEN } from "./constants.ts"; 11 - import { logger } from "./logger.ts"; 10 + import { DISCORD_BOT_TOKEN } from "@tealfmbot/common/constants.ts"; 11 + import { logger } from "@tealfmbot/common/logger.ts"; 12 12 13 13 const client = new Client({ intents: [GatewayIntentBits.Guilds] }); 14 14
migrations/schema.ts packages/database/migrations/schema.ts
+4 -22
package.json
··· 1 1 { 2 2 "name": "tealfmbot", 3 3 "version": "0.0.1", 4 - "type": "module", 4 + "private": true, 5 5 "description": "A discord bot for teal.fm", 6 - "main": "main.ts", 7 6 "scripts": { 8 - "dev": "tsx main.ts", 9 - "deploy-commands": "tsx deploy-commands.ts", 10 - "tapper:dev": "NODE_ENV=development tsx tapper.ts", 11 - "tapper:prod": "NODE_ENV=production tsx tapper.ts", 12 - "migrate": "tsx kysely/migrator.ts", 13 - "codegen": "kysely-codegen --dialect postgres", 14 - "seed": "tsx kysely/seed.ts", 15 - "typecheck": "tsc --noEmit" 7 + "bot": "pnpm --filter bot dev", 8 + "tap": "pnpm --filter tapper dev", 9 + "typecheck": "pnpm --filter './{packages,apps}/**' typecheck" 16 10 }, 17 11 "keywords": [ 18 12 "teal.fm", ··· 22 16 "author": "Dane Miller <me@dane.computer>", 23 17 "license": "MIT", 24 18 "packageManager": "pnpm@10.15.0", 25 - "dependencies": { 26 - "@atproto/tap": "^0.0.2", 27 - "discord.js": "^14.25.1", 28 - "kysely": "^0.28.9", 29 - "pg": "^8.16.3", 30 - "pino": "^10.1.0" 31 - }, 32 19 "devDependencies": { 33 - "@types/node": "^25.0.3", 34 - "@types/pg": "^8.16.0", 35 - "kysely-codegen": "^0.19.0", 36 - "pino-pretty": "^13.1.3", 37 - "tsx": "^4.21.0", 38 20 "typescript": "^5.9.3" 39 21 } 40 22 }
+21
packages/common/package.json
··· 1 + { 2 + "name": "@tealfmbot/common", 3 + "version": "0.0.0", 4 + "type": "module", 5 + "private": true, 6 + "exports": { 7 + "./*": "./*" 8 + }, 9 + "scripts": { 10 + "typecheck": "tsc --noEmit" 11 + }, 12 + "devDependencies": { 13 + "@tealfmbot/tsconfig": "workspace:*", 14 + "@types/node": "^22.15.3", 15 + "typescript": "5.9.2", 16 + "pino-pretty": "^13.1.3" 17 + }, 18 + "dependencies": { 19 + "pino": "^10.1.0" 20 + } 21 + }
+3
packages/common/tsconfig.json
··· 1 + { 2 + "extends": "@tealfmbot/tsconfig/tsconfig.node.json", 3 + }
+28
packages/database/package.json
··· 1 + { 2 + "name": "@tealfmbot/database", 3 + "version": "0.0.0", 4 + "type": "module", 5 + "private": true, 6 + "exports": { 7 + "./db.ts": "./db.ts" 8 + }, 9 + "scripts": { 10 + "migrate": "tsx migrator.ts", 11 + "codegen": "kysely-codegen --dialect postgres --env-file='../../.env'", 12 + "seed": "tsx seed.ts", 13 + "typecheck": "tsc --noEmit" 14 + }, 15 + "devDependencies": { 16 + "@types/node": "^22.15.3", 17 + "@types/pg": "^8.16.0", 18 + "kysely-codegen": "^0.19.0", 19 + "tsx": "^4.21.0", 20 + "typescript": "5.9.2" 21 + }, 22 + "dependencies": { 23 + "@tealfmbot/common": "workspace:*", 24 + "@tealfmbot/tsconfig": "workspace:*", 25 + "kysely": "^0.28.9", 26 + "pg": "^8.16.3" 27 + } 28 + }
+6
packages/database/tsconfig.json
··· 1 + { 2 + "extends": "@tealfmbot/tsconfig/tsconfig.node.json", 3 + "exclude": [ 4 + "node_modules" 5 + ] 6 + }
+9
packages/tsconfig/package.json
··· 1 + { 2 + "name": "@tealfmbot/tsconfig", 3 + "version": "0.0.0", 4 + "private": true, 5 + "license": "MIT", 6 + "publishConfig": { 7 + "access": "public" 8 + } 9 + }
+11
packages/tsconfig/tsconfig.node.json
··· 1 + { 2 + "extends": "./tsconfig.base.json", 3 + "compilerOptions": { 4 + "lib": [ 5 + "esnext" 6 + ], 7 + "types": [ 8 + "node" 9 + ], 10 + } 11 + }
+103 -19
pnpm-lock.yaml
··· 7 7 importers: 8 8 9 9 .: 10 + devDependencies: 11 + typescript: 12 + specifier: ^5.9.3 13 + version: 5.9.3 14 + 15 + apps/bot: 16 + dependencies: 17 + '@tealfmbot/common': 18 + specifier: workspace:* 19 + version: link:../../packages/common 20 + '@tealfmbot/tsconfig': 21 + specifier: workspace:* 22 + version: link:../../packages/tsconfig 23 + discord.js: 24 + specifier: ^14.25.1 25 + version: 14.25.1 26 + devDependencies: 27 + '@types/node': 28 + specifier: ^25.0.3 29 + version: 25.0.3 30 + tsx: 31 + specifier: ^4.21.0 32 + version: 4.21.0 33 + typescript: 34 + specifier: ^5.9.3 35 + version: 5.9.3 36 + 37 + apps/tapper: 10 38 dependencies: 11 39 '@atproto/tap': 12 40 specifier: ^0.0.2 13 41 version: 0.0.2 14 - discord.js: 15 - specifier: ^14.25.1 16 - version: 14.25.1 42 + '@tealfmbot/common': 43 + specifier: workspace:* 44 + version: link:../../packages/common 45 + '@tealfmbot/tsconfig': 46 + specifier: workspace:* 47 + version: link:../../packages/tsconfig 48 + devDependencies: 49 + '@types/node': 50 + specifier: ^25.0.3 51 + version: 25.0.3 52 + tsx: 53 + specifier: ^4.21.0 54 + version: 4.21.0 55 + typescript: 56 + specifier: ^5.9.3 57 + version: 5.9.3 58 + 59 + packages/common: 60 + dependencies: 61 + pino: 62 + specifier: ^10.1.0 63 + version: 10.1.0 64 + devDependencies: 65 + '@tealfmbot/tsconfig': 66 + specifier: workspace:* 67 + version: link:../tsconfig 68 + '@types/node': 69 + specifier: ^22.15.3 70 + version: 22.19.3 71 + pino-pretty: 72 + specifier: ^13.1.3 73 + version: 13.1.3 74 + typescript: 75 + specifier: 5.9.2 76 + version: 5.9.2 77 + 78 + packages/database: 79 + dependencies: 80 + '@tealfmbot/common': 81 + specifier: workspace:* 82 + version: link:../common 83 + '@tealfmbot/tsconfig': 84 + specifier: workspace:* 85 + version: link:../tsconfig 17 86 kysely: 18 87 specifier: ^0.28.9 19 88 version: 0.28.9 20 89 pg: 21 90 specifier: ^8.16.3 22 91 version: 8.16.3 23 - pino: 24 - specifier: ^10.1.0 25 - version: 10.1.0 26 92 devDependencies: 27 93 '@types/node': 28 - specifier: ^25.0.3 29 - version: 25.0.3 94 + specifier: ^22.15.3 95 + version: 22.19.3 30 96 '@types/pg': 31 97 specifier: ^8.16.0 32 98 version: 8.16.0 33 99 kysely-codegen: 34 100 specifier: ^0.19.0 35 - version: 0.19.0(kysely@0.28.9)(pg@8.16.3)(typescript@5.9.3) 36 - pino-pretty: 37 - specifier: ^13.1.3 38 - version: 13.1.3 101 + version: 0.19.0(kysely@0.28.9)(pg@8.16.3)(typescript@5.9.2) 39 102 tsx: 40 103 specifier: ^4.21.0 41 104 version: 4.21.0 42 105 typescript: 43 - specifier: ^5.9.3 44 - version: 5.9.3 106 + specifier: 5.9.2 107 + version: 5.9.2 108 + 109 + packages/tsconfig: {} 45 110 46 111 packages: 47 112 ··· 278 343 '@sapphire/snowflake@3.5.3': 279 344 resolution: {integrity: sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==} 280 345 engines: {node: '>=v14.0.0', npm: '>=7.0.0'} 346 + 347 + '@types/node@22.19.3': 348 + resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} 281 349 282 350 '@types/node@25.0.3': 283 351 resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} ··· 807 875 engines: {node: '>=18.0.0'} 808 876 hasBin: true 809 877 878 + typescript@5.9.2: 879 + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} 880 + engines: {node: '>=14.17'} 881 + hasBin: true 882 + 810 883 typescript@5.9.3: 811 884 resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 812 885 engines: {node: '>=14.17'} ··· 814 887 815 888 uint8arrays@3.0.0: 816 889 resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 890 + 891 + undici-types@6.21.0: 892 + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 817 893 818 894 undici-types@7.16.0: 819 895 resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} ··· 1053 1129 1054 1130 '@sapphire/snowflake@3.5.3': {} 1055 1131 1132 + '@types/node@22.19.3': 1133 + dependencies: 1134 + undici-types: 6.21.0 1135 + 1056 1136 '@types/node@25.0.3': 1057 1137 dependencies: 1058 1138 undici-types: 7.16.0 1059 1139 1060 1140 '@types/pg@8.16.0': 1061 1141 dependencies: 1062 - '@types/node': 25.0.3 1142 + '@types/node': 22.19.3 1063 1143 pg-protocol: 1.10.3 1064 1144 pg-types: 2.2.0 1065 1145 ··· 1132 1212 1133 1213 concat-map@0.0.1: {} 1134 1214 1135 - cosmiconfig@9.0.0(typescript@5.9.3): 1215 + cosmiconfig@9.0.0(typescript@5.9.2): 1136 1216 dependencies: 1137 1217 env-paths: 2.2.1 1138 1218 import-fresh: 3.3.1 1139 1219 js-yaml: 4.1.1 1140 1220 parse-json: 5.2.0 1141 1221 optionalDependencies: 1142 - typescript: 5.9.3 1222 + typescript: 5.9.2 1143 1223 1144 1224 dateformat@4.6.3: {} 1145 1225 ··· 1305 1385 1306 1386 json-parse-even-better-errors@2.3.1: {} 1307 1387 1308 - kysely-codegen@0.19.0(kysely@0.28.9)(pg@8.16.3)(typescript@5.9.3): 1388 + kysely-codegen@0.19.0(kysely@0.28.9)(pg@8.16.3)(typescript@5.9.2): 1309 1389 dependencies: 1310 1390 chalk: 4.1.2 1311 - cosmiconfig: 9.0.0(typescript@5.9.3) 1391 + cosmiconfig: 9.0.0(typescript@5.9.2) 1312 1392 dotenv: 17.2.3 1313 1393 dotenv-expand: 12.0.3 1314 1394 git-diff: 2.0.6 ··· 1580 1660 optionalDependencies: 1581 1661 fsevents: 2.3.3 1582 1662 1663 + typescript@5.9.2: {} 1664 + 1583 1665 typescript@5.9.3: {} 1584 1666 1585 1667 uint8arrays@3.0.0: 1586 1668 dependencies: 1587 1669 multiformats: 9.9.0 1670 + 1671 + undici-types@6.21.0: {} 1588 1672 1589 1673 undici-types@7.16.0: {} 1590 1674
+4
pnpm-workspace.yaml
··· 1 1 onlyBuiltDependencies: 2 2 - esbuild 3 + 4 + packages: 5 + - "apps/*" 6 + - "packages/*"
+2 -1
tapper.ts apps/tapper/index.ts
··· 1 1 import { SimpleIndexer, Tap } from "@atproto/tap"; 2 - import { TAP_ADMIN_PASSWORD } from "./constants.ts"; 2 + import { TAP_ADMIN_PASSWORD } from "@tealfmbot/common/constants.ts"; 3 3 // import { db } from "./kysely/db.ts" 4 4 5 5 const tap = new Tap("https://tap.xero.systems", { ··· 21 21 } else { 22 22 console.log(`deleted: ${uri}`); 23 23 } 24 + 24 25 25 26 if (process.env.NODE_ENV === "development") { 26 27 // we don't want to ack in development
-7
tsconfig.json packages/tsconfig/tsconfig.base.json
··· 1 1 { 2 2 "compilerOptions": { 3 - "outDir": "./dist", 4 3 "module": "nodenext", 5 4 "target": "esnext", 6 - "lib": [ 7 - "esnext" 8 - ], 9 - "types": [ 10 - "node" 11 - ], 12 5 "esModuleInterop": true, 13 6 "sourceMap": true, 14 7 "noUncheckedIndexedAccess": true,
types.ts packages/database/types.ts