diff --git a/exe/bot.ts b/exe/bot.ts deleted file mode 100644 index daaaddd..0000000 --- a/exe/bot.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Client, Message } from "discord.js"; -import type { Analecta } from "../src/bot/exp/analecta"; -import type { CommandProcessor } from "../src/bot/abst/connector"; -import { DiscordMessage } from "../src/bot/skin/discord-message"; -import { FaunaDB } from "../src/bot/skin/fauna-db"; -import { GitHubApi } from "../src/bot/skin/github-api"; -import { PlainDB } from "../src/bot/skin/plain-db"; -import { SubscriptionNotifier } from "../src/bot/skin/notifier"; -import { TomlLoader } from "../src/bot/skin/toml-loader"; -import dotenv from "dotenv"; -import { procs } from "../src/bot/skin/procs"; - -dotenv.config(); - -const messageHandler = ( - analecta: Analecta, - builtProcs: CommandProcessor, -) => async (msg: Message) => { - if (msg.author.bot) { - return; - } - if (msg.content.startsWith("/gh?")) { - const dm = await msg.author.createDM(); - dm.send(analecta.HelpMessage); - return; - } - const discordMessage = new DiscordMessage(msg); - await builtProcs(analecta, discordMessage); -}; - -(async () => { - const loader = new TomlLoader( - process.env.TOML_PATH || "./analecta/laffey.toml", - ); - const db = new FaunaDB(process.env.FAUNA_SECRET || "UNSET"); - const analecta = await loader.load(); - - const client = new Client(); - const notifier = new SubscriptionNotifier(analecta, client.users, db); - db.onUpdate(notifier); - const query = new GitHubApi(); - - const builtProcs = procs(analecta, db, query); - - client.on("ready", () => { - console.log("I got ready."); - }); - - client.on("message", messageHandler(analecta, builtProcs)); - - client.login(process.env.DISCORD_TOKEN); -})().catch((err) => console.error(err)); diff --git a/package.json b/package.json index bee0161..c63eb72 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "license": "Apache-2.0", "private": true, "scripts": { - "dev:bot": "ts-node ./exe/bot.ts", - "build:bot": "esbuild --outfile=dist/bundle.js --sourcemap --bundle exe/bot.ts --platform=node --target=es2019 --external:discord.js --external:node-fetch", + "dev:bot": "ts-node src/bot/run/release.ts", + "build:bot": "esbuild --outfile=dist/bundle.js --sourcemap --bundle src/bot/run/release.ts --platform=node --target=es2019 --external:discord.js --external:node-fetch", "start:bot": "node dist/bundle.js", "dev:web": "next ./src/web", "build:web": "next build ./src/web", diff --git a/src/bot/exp/omit.ts b/src/bot/exp/omit.ts index 6ba4d4d..abcd924 100644 --- a/src/bot/exp/omit.ts +++ b/src/bot/exp/omit.ts @@ -4,5 +4,6 @@ export const omitBody = (body: string): string => { if (body.length < bodyWidth) { return body; } - return `${body.slice(0, bodyWidth)}...`; + const omitted = body.slice(0, bodyWidth); + return `${omitted}...`; }; diff --git a/src/bot/play/bring/branch.test.ts b/src/bot/play/bring/branch.test.ts index aaa0216..c2a172c 100644 --- a/src/bot/play/bring/branch.test.ts +++ b/src/bot/play/bring/branch.test.ts @@ -1,5 +1,5 @@ import { MessageEmbed } from "discord.js"; -import { MockMessage } from "../../skin/mock-message"; +import { MockMessage } from "../../skin/mock/message"; import { analectaForTest } from "../../skin/test-analecta"; import { bringBranch } from "./branch"; import { colorFromState } from "../../exp/state-color"; diff --git a/src/bot/play/bring/issue.test.ts b/src/bot/play/bring/issue.test.ts index aacf8e0..d16ccbf 100644 --- a/src/bot/play/bring/issue.test.ts +++ b/src/bot/play/bring/issue.test.ts @@ -1,5 +1,5 @@ import { MessageEmbed } from "discord.js"; -import { MockMessage } from "../../skin/mock-message"; +import { MockMessage } from "../../skin/mock/message"; import { analectaForTest } from "../../skin/test-analecta"; import { bringIssue } from "./issue"; import { colorFromState } from "../../exp/state-color"; diff --git a/src/bot/play/bring/pr.test.ts b/src/bot/play/bring/pr.test.ts index 9cbda42..859db87 100644 --- a/src/bot/play/bring/pr.test.ts +++ b/src/bot/play/bring/pr.test.ts @@ -1,5 +1,5 @@ import { MessageEmbed } from "discord.js"; -import { MockMessage } from "../../skin/mock-message"; +import { MockMessage } from "../../skin/mock/message"; import { analectaForTest } from "../../skin/test-analecta"; import { bringPR } from "./pr"; import { colorFromState } from "../../exp/state-color"; diff --git a/src/bot/play/bring/repo.test.ts b/src/bot/play/bring/repo.test.ts index 7c3122a..521029d 100644 --- a/src/bot/play/bring/repo.test.ts +++ b/src/bot/play/bring/repo.test.ts @@ -1,5 +1,5 @@ import { MessageEmbed } from "discord.js"; -import { MockMessage } from "../../skin/mock-message"; +import { MockMessage } from "../../skin/mock/message"; import { analectaForTest } from "../../skin/test-analecta"; import { bringRepo } from "./repo"; diff --git a/src/bot/play/error.test.ts b/src/bot/play/error.test.ts index 7cf4211..8499ba2 100644 --- a/src/bot/play/error.test.ts +++ b/src/bot/play/error.test.ts @@ -1,4 +1,4 @@ -import { MockMessage } from "../skin/mock-message"; +import { MockMessage } from "../skin/mock/message"; import { analectaForTest } from "../skin/test-analecta"; import { error } from "./error"; diff --git a/src/bot/play/flavor.test.ts b/src/bot/play/flavor.test.ts index 85b78af..78eb947 100644 --- a/src/bot/play/flavor.test.ts +++ b/src/bot/play/flavor.test.ts @@ -1,4 +1,4 @@ -import { MockMessage } from "../skin/mock-message"; +import { MockMessage } from "../skin/mock/message"; import { analectaForTest } from "../skin/test-analecta"; import { flavor } from "./flavor"; diff --git a/src/bot/play/subscribe/mark-as-read.test.ts b/src/bot/play/notify/mark-as-read.test.ts similarity index 86% rename from src/bot/play/subscribe/mark-as-read.test.ts rename to src/bot/play/notify/mark-as-read.test.ts index f79e7ae..f07c46a 100644 --- a/src/bot/play/subscribe/mark-as-read.test.ts +++ b/src/bot/play/notify/mark-as-read.test.ts @@ -1,14 +1,14 @@ import { DiscordId } from "../../exp/discord-id"; import { GitHubUser } from "../../exp/github-user"; -import { MockDB } from "../../skin/mock-db"; -import { MockMessage } from "../../skin/mock-message"; +import { MockMessage } from "../../skin/mock/message"; +import { MockUserDB } from "../../skin/mock/user-db"; import { NotificationId } from "../../exp/github-notification"; import { analectaForTest } from "../../skin/test-analecta"; import { markAsRead } from "./mark-as-read"; test("mark a notification as read", async () => { const analecta = await analectaForTest(); - const db = new MockDB({ + const db = new MockUserDB({ userName: "Alice", notificationToken: "TEST_TOKEN", currentNotificationIds: ["0123456789" as NotificationId], diff --git a/src/bot/play/subscribe/mark-as-read.ts b/src/bot/play/notify/mark-as-read.ts similarity index 100% rename from src/bot/play/subscribe/mark-as-read.ts rename to src/bot/play/notify/mark-as-read.ts diff --git a/src/bot/play/subscribe/subscribe-notification.test.ts b/src/bot/play/notify/subscribe.test.ts similarity index 84% rename from src/bot/play/subscribe/subscribe-notification.test.ts rename to src/bot/play/notify/subscribe.test.ts index b5b79f3..7f4617c 100644 --- a/src/bot/play/subscribe/subscribe-notification.test.ts +++ b/src/bot/play/notify/subscribe.test.ts @@ -1,14 +1,14 @@ -import { MockDB, placeholder } from "../../skin/mock-db"; +import { MockUserDB, placeholder } from "../../skin/mock/user-db"; import { DiscordId } from "../../exp/discord-id"; import { GitHubUser } from "../../exp/github-user"; -import { MockMessage } from "../../skin/mock-message"; +import { MockMessage } from "../../skin/mock/message"; import { NotificationId } from "../../exp/github-notification"; import { analectaForTest } from "../../skin/test-analecta"; -import { subscribeNotification } from "./subscribe-notification"; +import { subscribeNotification } from "./subscribe"; test("subscribe a member", async () => { const analecta = await analectaForTest(); - const db = new MockDB({ + const db = new MockUserDB({ userName: "Alice", notificationToken: "TEST_TOKEN", currentNotificationIds: [] as NotificationId[], diff --git a/src/bot/play/subscribe/subscribe-notification.ts b/src/bot/play/notify/subscribe.ts similarity index 100% rename from src/bot/play/subscribe/subscribe-notification.ts rename to src/bot/play/notify/subscribe.ts diff --git a/src/bot/play/subscribe/unsubscribe-notification.test.ts b/src/bot/play/notify/unsubscribe.test.ts similarity index 74% rename from src/bot/play/subscribe/unsubscribe-notification.test.ts rename to src/bot/play/notify/unsubscribe.test.ts index 4f3cdd9..1070195 100644 --- a/src/bot/play/subscribe/unsubscribe-notification.test.ts +++ b/src/bot/play/notify/unsubscribe.test.ts @@ -1,12 +1,12 @@ -import { MockDB, placeholder } from "../../skin/mock-db"; +import { MockUserDB, placeholder } from "../../skin/mock/user-db"; import { DiscordId } from "../../exp/discord-id"; -import { MockMessage } from "../../skin/mock-message"; +import { MockMessage } from "../../skin/mock/message"; import { analectaForTest } from "../../skin/test-analecta"; -import { unsubNotification } from "./unsubscribe-notification"; +import { unsubNotification } from "./unsubscribe"; test("subscribe a member", async () => { const analecta = await analectaForTest(); - const db = new MockDB(); + const db = new MockUserDB(); const unregisterDone = new Promise((resolve) => { db.onUnregister.on(placeholder, (id) => { expect(id).toStrictEqual("alice_discord"); diff --git a/src/bot/play/subscribe/unsubscribe-notification.ts b/src/bot/play/notify/unsubscribe.ts similarity index 100% rename from src/bot/play/subscribe/unsubscribe-notification.ts rename to src/bot/play/notify/unsubscribe.ts diff --git a/src/bot/skin/procs.ts b/src/bot/run/procs.ts similarity index 56% rename from src/bot/skin/procs.ts rename to src/bot/run/procs.ts index 1cce238..8104935 100644 --- a/src/bot/skin/procs.ts +++ b/src/bot/run/procs.ts @@ -1,6 +1,8 @@ import { CommandProcessor, connectProcessors } from "../abst/connector"; import type { AllApi } from "../abst/api"; import type { Analecta } from "../exp/analecta"; +import { DiscordMessage } from "../skin/discord-message"; +import type { Message } from "discord.js"; import type { UserDatabase } from "../abst/user-database"; import { bringBranch } from "../play/bring/branch"; import { bringIssue } from "../play/bring/issue"; @@ -8,10 +10,9 @@ import { bringPR } from "../play/bring/pr"; import { bringRepo } from "../play/bring/repo"; import { error } from "../play/error"; import { flavor } from "../play/flavor"; -import { markAsRead } from "../play/subscribe/mark-as-read"; -// eslint-disable-next-line max-len -import { subscribeNotification } from "../play/subscribe/subscribe-notification"; -import { unsubNotification } from "../play/subscribe/unsubscribe-notification"; +import { markAsRead } from "../play/notify/mark-as-read"; +import { subscribeNotification } from "../play/notify/subscribe"; +import { unsubNotification } from "../play/notify/unsubscribe"; export const procs = ( analecta: Analecta, @@ -32,3 +33,18 @@ export const procs = ( markAsRead(db, query), error, ]); + +export const messageHandler = + (analecta: Analecta, builtProcs: CommandProcessor) => + async (msg: Message): Promise => { + if (msg.author.bot) { + return; + } + if (msg.content.startsWith("/gh?")) { + const dm = await msg.author.createDM(); + dm.send(analecta.HelpMessage); + return; + } + const discordMessage = new DiscordMessage(msg); + await builtProcs(analecta, discordMessage); + }; diff --git a/src/bot/run/release.ts b/src/bot/run/release.ts new file mode 100644 index 0000000..89d1e68 --- /dev/null +++ b/src/bot/run/release.ts @@ -0,0 +1,32 @@ +import { messageHandler, procs } from "./procs"; +import { Client } from "discord.js"; +import { FaunaDB } from "../skin/fauna-db"; +import { GitHubApi } from "../skin/github-api"; +import { SubscriptionNotifier } from "../skin/notifier"; +import { TomlLoader } from "../skin/toml-loader"; +import dotenv from "dotenv"; + +dotenv.config(); + +(async () => { + const loader = new TomlLoader( + process.env.TOML_PATH || "./analecta/laffey.toml", + ); + const db = new FaunaDB(process.env.FAUNA_SECRET || "UNSET"); + const analecta = await loader.load(); + + const client = new Client(); + const notifier = new SubscriptionNotifier(analecta, client.users, db); + db.onUpdate(notifier); + const query = new GitHubApi(); + + const builtProcs = procs(analecta, db, query); + + client.on("ready", () => { + console.log("I got ready."); + }); + + client.on("message", messageHandler(analecta, builtProcs)); + + client.login(process.env.DISCORD_TOKEN); +})().catch((err) => console.error(err)); diff --git a/src/bot/skin/github-api.ts b/src/bot/skin/github-api.ts index bf0d6d3..ae6d484 100644 --- a/src/bot/skin/github-api.ts +++ b/src/bot/skin/github-api.ts @@ -23,7 +23,7 @@ export class GitHubApi const repoInfoApiUrl = `${apiRoot}/repos/${owner}/${repoName}`; const infoRes: unknown = await (await fetch(repoInfoApiUrl)).json(); if (checkNotFound(infoRes)) { - throw new Error("not found the repositpory"); + throw new Error("not found the repository"); } return infoRes as Repository; } @@ -32,7 +32,7 @@ export class GitHubApi const apiUrl = `${apiRoot}/repos/${owner}/${repoName}/issues`; const res: unknown = await (await fetch(apiUrl)).json(); if (checkNotFound(res)) { - throw new Error("not found the repositpory"); + throw new Error("not found the repository"); } return res as PartialIssue[]; } @@ -57,7 +57,7 @@ export class GitHubApi const apiUrl = `${apiRoot}/repos/${owner}/${repoName}/pulls`; const res: unknown = await (await fetch(apiUrl)).json(); if (checkNotFound(res)) { - throw new Error("not found the repositpory"); + throw new Error("not found the repository"); } return res as PartialPullRequest[]; } diff --git a/src/bot/skin/github-notification-query.ts b/src/bot/skin/github-notification-query.ts index 36c28c3..97e2208 100644 --- a/src/bot/skin/github-notification-query.ts +++ b/src/bot/skin/github-notification-query.ts @@ -8,11 +8,12 @@ export const notificationQuery: Query = { userName, notificationToken, }: GitHubUser): Promise { + const base64 = Buffer.from(`${userName}:${notificationToken}`).toString( + "base64", + ); const rawRes = await fetch("https://api.github.com/notifications", { headers: { - Authorization: `Basic ${Buffer.from( - `${userName}:${notificationToken}`, - ).toString("base64")}`, + Authorization: `Basic ${base64}`, }, }); if (!rawRes.ok) { diff --git a/src/bot/skin/mock-message.ts b/src/bot/skin/mock/message.ts similarity index 90% rename from src/bot/skin/mock-message.ts rename to src/bot/skin/mock/message.ts index a581f48..cd6a031 100644 --- a/src/bot/skin/mock-message.ts +++ b/src/bot/skin/mock/message.ts @@ -1,6 +1,6 @@ -import type { DiscordId } from "../exp/discord-id"; +import type { DiscordId } from "../../exp/discord-id"; import { EventEmitter } from "events"; -import type { Message } from "../abst/message"; +import type { Message } from "../../abst/message"; import type { MessageEmbed } from "discord.js"; export class MockMessage implements Message { diff --git a/src/bot/skin/mock-db.ts b/src/bot/skin/mock/user-db.ts similarity index 74% rename from src/bot/skin/mock-db.ts rename to src/bot/skin/mock/user-db.ts index d3023aa..79118f8 100644 --- a/src/bot/skin/mock-db.ts +++ b/src/bot/skin/mock/user-db.ts @@ -1,11 +1,11 @@ -import type { DiscordId } from "../exp/discord-id"; +import type { DiscordId } from "../../exp/discord-id"; import { EventEmitter } from "events"; -import type { GitHubUser } from "../exp/github-user"; -import type { UserDatabase } from "../abst/user-database"; +import type { GitHubUser } from "../../exp/github-user"; +import type { UserDatabase } from "../../abst/user-database"; export const placeholder = Symbol("placeholder for MockDB"); -export class MockDB implements UserDatabase { +export class MockUserDB implements UserDatabase { constructor(private readonly passed?: GitHubUser) {} readonly onRegister = new EventEmitter(); diff --git a/src/bot/skin/toml-loader.ts b/src/bot/skin/toml-loader.ts index 92893a2..824f531 100644 --- a/src/bot/skin/toml-loader.ts +++ b/src/bot/skin/toml-loader.ts @@ -17,7 +17,7 @@ export class TomlLoader implements SayingLoader { const analecta: unknown = toml.parse(tomlStr); if (!validateAnalecta(analecta)) { - console.log({ analecta }); + console.dir(analecta); throw new Error("invalid toml"); } return analecta;