+1
-1
.oxfmtrc.json
+1
-1
.oxfmtrc.json
+6
-2
PLAN.md
+6
-2
PLAN.md
···
1
# rough notes on how I think this should work
2
3
- we start of with no accounts
4
-
- `/teal auth` sends user a link to log in with atproto account
5
- after auth success, we take did and send http request to tap instance to start backfilling for repo
6
- user can now use bot commands
7
8
## planned commands
9
10
-
- `teal auth` sends user a link to log in with atproto account
11
- `top <artist>`: top 10 listeners for artist (total amount of plays across all songs / albums)
12
- `recent`: most recent play
13
14
## web interface for account management...maybe..?
15
16
- link/unlink account (unlinking would basically delete an account and all their plays)
···
1
# rough notes on how I think this should work
2
3
- we start of with no accounts
4
+
- `/auth <did or handle>` sends user a link to log in with atproto account
5
- after auth success, we take did and send http request to tap instance to start backfilling for repo
6
- user can now use bot commands
7
8
## planned commands
9
10
+
- `auth <did or handle>` sends user a link to log in with atproto account
11
- `top <artist>`: top 10 listeners for artist (total amount of plays across all songs / albums)
12
- `recent`: most recent play
13
14
## web interface for account management...maybe..?
15
16
- link/unlink account (unlinking would basically delete an account and all their plays)
17
+
18
+
## authentication flow
19
+
20
+
1.
+13
-7
apps/bot/commands/auth.ts
+13
-7
apps/bot/commands/auth.ts
···
1
-
import { CommandInteraction, SlashCommandBuilder } from "discord.js";
2
import { logger } from "@tealfmbot/common/logger.ts";
3
4
export default {
5
data: new SlashCommandBuilder()
6
.setName("auth")
7
-
.setDescription(
8
-
"Authenticate your account with the teal.fm bot to start tracking your listens",
9
-
),
10
-
async execute(interaction: CommandInteraction) {
11
-
await interaction.reply("placeholder");
12
-
logger.info("auth command sent");
13
},
14
};
···
1
import { logger } from "@tealfmbot/common/logger.ts";
2
+
import { ChatInputCommandInteraction, InteractionContextType, SlashCommandBuilder } from "discord.js";
3
4
export default {
5
data: new SlashCommandBuilder()
6
.setName("auth")
7
+
.setDescription("Authenticate your account with the teal.fm bot to start tracking your listens")
8
+
.addStringOption((option) =>
9
+
option
10
+
.setName("identifier")
11
+
.setDescription("e.g. 'handle.bsky.social or did:plc...'")
12
+
.setRequired(true)
13
+
.setMinLength(1),
14
+
).setContexts(InteractionContextType.Guild),
15
+
async execute(interaction: ChatInputCommandInteraction) {
16
+
const identifier = interaction.options.getString("identifier")
17
+
await interaction.reply(`hello ${identifier}`)
18
+
logger.info(`starting authentication process for ${identifier} at ${new Date().toJSON()}`);
19
},
20
};
+1
-1
apps/bot/commands/ping.ts
+1
-1
apps/bot/commands/ping.ts
+1
-1
apps/bot/commands/top.ts
+1
-1
apps/bot/commands/top.ts
+3
-3
apps/bot/deploy-commands.ts
+3
-3
apps/bot/deploy-commands.ts
···
1
-
import { REST, Routes } from "discord.js";
2
-
import fs from "node:fs";
3
-
import path from "node:path";
4
import {
5
DISCORD_APPLICATION_ID,
6
DISCORD_BOT_TOKEN,
7
DISCORD_GUILD_ID,
8
} from "@tealfmbot/common/constants.ts";
9
10
const commands = [];
11
const commandPaths = fs.globSync("commands/**/*.ts");
···
1
import {
2
DISCORD_APPLICATION_ID,
3
DISCORD_BOT_TOKEN,
4
DISCORD_GUILD_ID,
5
} from "@tealfmbot/common/constants.ts";
6
+
import { REST, Routes } from "discord.js";
7
+
import fs from "node:fs";
8
+
import path from "node:path";
9
10
const commands = [];
11
const commandPaths = fs.globSync("commands/**/*.ts");
+2
-2
apps/bot/main.ts
+2
-2
apps/bot/main.ts
···
1
import { Client, Collection, Events, GatewayIntentBits, MessageFlags } from "discord.js";
2
import fs from "node:fs";
3
import path from "node:path";
4
-
import { DISCORD_BOT_TOKEN } from "@tealfmbot/common/constants.ts";
5
-
import { logger } from "@tealfmbot/common/logger.ts";
6
7
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
8
···
1
+
import { DISCORD_BOT_TOKEN } from "@tealfmbot/common/constants.ts";
2
+
import { logger } from "@tealfmbot/common/logger.ts";
3
import { Client, Collection, Events, GatewayIntentBits, MessageFlags } from "discord.js";
4
import fs from "node:fs";
5
import path from "node:path";
6
7
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
8
+19
-2
apps/web/index.ts
+19
-2
apps/web/index.ts
···
1
+
import { serve, type HttpBindings } from "@hono/node-server";
2
+
import { logger } from "@tealfmbot/common/logger.js";
3
import { Hono } from "hono";
4
+
import pinoHttpLogger from "pino-http"
5
+
6
+
type Variables = {
7
+
logger: typeof logger
8
+
}
9
+
10
+
const app = new Hono<{ Bindings: HttpBindings, Variables: Variables }>();
11
+
12
+
app.use(async (c, next) => {
13
+
await new Promise<void>((resolve) => pinoHttpLogger({
14
+
autoLogging: false,
15
+
})(c.env.incoming, c.env.outgoing, () => resolve()));
16
17
+
c.set("logger", c.env.incoming.log)
18
+
19
+
await next();
20
+
})
21
22
app.get("/", (c) => {
23
+
c.var.logger.info("test log")
24
return c.text("yo!!");
25
});
26
+2
-1
apps/web/package.json
+2
-1
apps/web/package.json
+1
-1
packages/common/constants.ts
+1
-1
packages/common/constants.ts
+3
-2
packages/database/db.ts
+3
-2
packages/database/db.ts
+5
-4
packages/database/migrator.ts
+5
-4
packages/database/migrator.ts
···
1
import path from "node:path";
2
import { Pool } from "pg";
3
-
import fs from "node:fs/promises";
4
-
import type { DB } from "kysely-codegen";
5
-
import { Kysely, Migrator, PostgresDialect, FileMigrationProvider } from "kysely";
6
-
import { DATABASE_URL } from "@tealfmbot/common/constants.ts";
7
8
async function migrateToLatest() {
9
const db = new Kysely<DB>({
···
1
+
import type { DB } from "kysely-codegen";
2
+
3
+
import { DATABASE_URL } from "@tealfmbot/common/constants.ts";
4
+
import { Kysely, Migrator, PostgresDialect, FileMigrationProvider } from "kysely";
5
+
import fs from "node:fs/promises";
6
import path from "node:path";
7
import { Pool } from "pg";
8
9
async function migrateToLatest() {
10
const db = new Kysely<DB>({
+19
pnpm-lock.yaml
+19
pnpm-lock.yaml
···
79
hono:
80
specifier: ^4.11.3
81
version: 4.11.3
82
devDependencies:
83
'@types/node':
84
specifier: ^25.0.3
···
636
function-bind@1.1.2:
637
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
638
639
get-tsconfig@4.13.0:
640
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
641
···
920
921
pino-abstract-transport@3.0.0:
922
resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==}
923
924
pino-pretty@13.1.3:
925
resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==}
···
1567
1568
function-bind@1.1.2: {}
1569
1570
get-tsconfig@4.13.0:
1571
dependencies:
1572
resolve-pkg-maps: 1.0.0
···
1817
pino-abstract-transport@3.0.0:
1818
dependencies:
1819
split2: 4.2.0
1820
1821
pino-pretty@13.1.3:
1822
dependencies:
···
79
hono:
80
specifier: ^4.11.3
81
version: 4.11.3
82
+
pino-http:
83
+
specifier: ^11.0.0
84
+
version: 11.0.0
85
devDependencies:
86
'@types/node':
87
specifier: ^25.0.3
···
639
function-bind@1.1.2:
640
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
641
642
+
get-caller-file@2.0.5:
643
+
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
644
+
engines: {node: 6.* || 8.* || >= 10.*}
645
+
646
get-tsconfig@4.13.0:
647
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
648
···
927
928
pino-abstract-transport@3.0.0:
929
resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==}
930
+
931
+
pino-http@11.0.0:
932
+
resolution: {integrity: sha512-wqg5XIAGRRIWtTk8qPGxkbrfiwEWz1lgedVLvhLALudKXvg1/L2lTFgTGPJ4Z2e3qcRmxoFxDuSdMdMGNM6I1g==}
933
934
pino-pretty@13.1.3:
935
resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==}
···
1577
1578
function-bind@1.1.2: {}
1579
1580
+
get-caller-file@2.0.5: {}
1581
+
1582
get-tsconfig@4.13.0:
1583
dependencies:
1584
resolve-pkg-maps: 1.0.0
···
1829
pino-abstract-transport@3.0.0:
1830
dependencies:
1831
split2: 4.2.0
1832
+
1833
+
pino-http@11.0.0:
1834
+
dependencies:
1835
+
get-caller-file: 2.0.5
1836
+
pino: 10.1.0
1837
+
pino-std-serializers: 7.0.0
1838
+
process-warning: 5.0.0
1839
1840
pino-pretty@13.1.3:
1841
dependencies: