diff --git a/adapters/discord/src/actions/shutdown.ts b/adapters/discord/src/actions/shutdown.ts new file mode 100644 index 0000000..27c4225 --- /dev/null +++ b/adapters/discord/src/actions/shutdown.ts @@ -0,0 +1,7 @@ +import { logChannelService } from "features/log-channel/log-channel.service"; +import { logger } from "lib/common-logger"; + +export const handleShutdown = async () => { + logger.info("Bot is shutting down..."); + await logChannelService.sendLogMessage("Bot is shutting down..."); +}; diff --git a/adapters/discord/src/discord.controller.ts b/adapters/discord/src/discord.controller.ts index ca50800..a91903b 100644 --- a/adapters/discord/src/discord.controller.ts +++ b/adapters/discord/src/discord.controller.ts @@ -12,25 +12,6 @@ export default class DiscordController extends EventEmitter { super(); let channelListeners = new Map(); - // log when running - client.once("ready", async () => { - const channels = client.channels; - const logChannel = channels.cache.get(config.discord.channelIdLog); - - if (logChannel?.isTextBased() && logChannel?.isSendable()) { - try { - console.log("bot is online"); - await logChannel.send("wieder online!!!"); - } catch (error) { - console.error("failed to send online message:", error); - } - } else { - console.error("log channel is not valid or sendable."); - } - await this.setActivity(100); - console.log("ready"); - }); - process.on("exit", async () => { const channels = client.channels; const logChannel = channels.cache.get(config.discord.channelIdLog); @@ -144,24 +125,6 @@ export default class DiscordController extends EventEmitter { } } - async handleShutdown(logChannel: Channel | undefined) { - if (logChannel?.isTextBased() && logChannel?.isSendable()) { - try { - await logChannel.send("bot is going offline..."); - } catch (error) { - console.error("failed to send offline message:", error); - } - } else { - console.error("log channel is not valid or sendable."); - } - await this.setActivity(0); - console.log("bot is offline."); - } - - async init() { - await this.discordService.init(); - } - async handleInteraction(interaction: Interaction) { if (interaction.isModalSubmit()) { await this.handleModalSubmit(interaction); diff --git a/adapters/discord/src/features/activity/activity.schema.ts b/adapters/discord/src/features/activity/activity.schema.ts new file mode 100644 index 0000000..4539551 --- /dev/null +++ b/adapters/discord/src/features/activity/activity.schema.ts @@ -0,0 +1,19 @@ +import z from "zod"; + +export const Activities = z.enum([ + "playing", + "streaming", + "listening", + "watching", + "competing", + "invisible", +]); + +export const ActivityLocales: Record, string> = { + playing: "spielt sudoku", + streaming: "streamt sudoku", + listening: "hört sudoku", + watching: "schaut sudoku", + competing: "wettstreitet sudoku", + invisible: "versteckt sudoku", +}; diff --git a/adapters/discord/src/features/activity/activity.service.ts b/adapters/discord/src/features/activity/activity.service.ts new file mode 100644 index 0000000..985593a --- /dev/null +++ b/adapters/discord/src/features/activity/activity.service.ts @@ -0,0 +1,27 @@ +import client from "lib/client"; +import type z from "zod"; +import { type Activities, ActivityLocales } from "./activity.schema"; + +/** + * Set the activity of the bot. This can be used to show that the bot is playing a game, listening to music, etc. + */ +export class ActivityService { + async set(activity: z.output) { + if (activity === "invisible") { + client.user?.setPresence({ + status: "invisible", + }); + client.user?.setActivity(" ", { type: 0 }); + return; + } + + client.user?.setActivity(ActivityLocales[activity], { + type: 0, + }); + client.user?.setPresence({ + status: "online", + }); + } +} + +export const activityService = new ActivityService(); diff --git a/adapters/discord/src/features/log-channel/log-channel.service.ts b/adapters/discord/src/features/log-channel/log-channel.service.ts new file mode 100644 index 0000000..f241146 --- /dev/null +++ b/adapters/discord/src/features/log-channel/log-channel.service.ts @@ -0,0 +1,26 @@ +import { config } from "config"; +import client from "lib/client"; +import { logger } from "lib/common-logger"; + +export class LogChannelService { + private logChannelId = config.channelMapping.text.bot; + + async getLogChannel() { + const logChannel = await client.channels.fetch(this.logChannelId); + + if (logChannel?.isTextBased() && logChannel.isSendable()) { + return logChannel; + } else { + logger.fatal("Log channel not found or is not text-based."); + throw new Error("Log channel not found or is not text-based"); + } + } + + async sendLogMessage(message: string) { + const logChannel = await this.getLogChannel(); + + await logChannel.send(message); + } +} + +export const logChannelService = new LogChannelService(); diff --git a/adapters/discord/src/lib/common-logger.ts b/adapters/discord/src/lib/common-logger.ts new file mode 100644 index 0000000..95fdca2 --- /dev/null +++ b/adapters/discord/src/lib/common-logger.ts @@ -0,0 +1,3 @@ +import { createLogger } from "@avocadi/bot-core/lib/logger"; + +export const logger = createLogger("DiscordAdapter"); diff --git a/adapters/discord/src/listeners/guild-member/guild-member.listener.ts b/adapters/discord/src/listeners/guild-members/guild-members.listener.ts similarity index 100% rename from adapters/discord/src/listeners/guild-member/guild-member.listener.ts rename to adapters/discord/src/listeners/guild-members/guild-members.listener.ts diff --git a/adapters/discord/src/listeners/index.ts b/adapters/discord/src/listeners/index.ts new file mode 100644 index 0000000..6ba5aef --- /dev/null +++ b/adapters/discord/src/listeners/index.ts @@ -0,0 +1,5 @@ +import "./ready.listener"; +import "./guild-member-add.listener"; +import "./voice-state/voice-state.listener"; +import "./message-reaction-add.listener"; +import "./message-reaction-remove.listener"; diff --git a/adapters/discord/src/listeners/interactions/handle-button.ts b/adapters/discord/src/listeners/interactions/handle-button.ts new file mode 100644 index 0000000..71a037b --- /dev/null +++ b/adapters/discord/src/listeners/interactions/handle-button.ts @@ -0,0 +1,5 @@ +import type { ButtonInteraction } from "discord.js"; + +export const handleButtonInteraction = async ( + interaction: ButtonInteraction, +) => {}; diff --git a/adapters/discord/src/listeners/interactions/handle-chat-input-command.ts b/adapters/discord/src/listeners/interactions/handle-chat-input-command.ts new file mode 100644 index 0000000..38b666a --- /dev/null +++ b/adapters/discord/src/listeners/interactions/handle-chat-input-command.ts @@ -0,0 +1,5 @@ +import type { ChatInputCommandInteraction } from "discord.js"; + +export const handleChatInputCommandInteraction = async ( + interaction: ChatInputCommandInteraction, +) => {}; diff --git a/adapters/discord/src/listeners/interactions/handle-modal-submit.ts b/adapters/discord/src/listeners/interactions/handle-modal-submit.ts new file mode 100644 index 0000000..dcdb405 --- /dev/null +++ b/adapters/discord/src/listeners/interactions/handle-modal-submit.ts @@ -0,0 +1,5 @@ +import type { ModalSubmitInteraction } from "discord.js"; + +export const handleModalSubmitInteraction = async ( + interaction: ModalSubmitInteraction, +) => {}; diff --git a/adapters/discord/src/listeners/interactions/interactions.listener.ts b/adapters/discord/src/listeners/interactions/interactions.listener.ts new file mode 100644 index 0000000..2c5ad14 --- /dev/null +++ b/adapters/discord/src/listeners/interactions/interactions.listener.ts @@ -0,0 +1,20 @@ +import client from "lib/client"; +import { handleButtonInteraction } from "./handle-button"; +import { handleChatInputCommandInteraction } from "./handle-chat-input-command"; +import { handleModalSubmitInteraction } from "./handle-modal-submit"; + +client.on("interactionCreate", async (interaction) => { + if (interaction.isModalSubmit()) { + await handleModalSubmitInteraction(interaction); + return; + } + + if (interaction.isChatInputCommand()) { + await handleChatInputCommandInteraction(interaction); + return; + } + if (interaction.isButton()) { + await handleButtonInteraction(interaction); + return; + } +}); diff --git a/adapters/discord/src/listeners/ready.listener.ts b/adapters/discord/src/listeners/ready.listener.ts new file mode 100644 index 0000000..629c99e --- /dev/null +++ b/adapters/discord/src/listeners/ready.listener.ts @@ -0,0 +1,22 @@ +import { config } from "config"; +import { activityService } from "features/activity/activity.service"; +import client from "lib/client"; +import { logger } from "lib/common-logger"; + +client.once("ready", async () => { + const channels = client.channels; + const logChannel = channels.cache.get(config.channelMapping.text.bot); + + if (logChannel?.isTextBased() && logChannel?.isSendable()) { + try { + logger.info("bot is online"); + await logChannel.send("wieder online!!!"); + } catch (error) { + logger.error("failed to send online message:", error); + } + } else { + logger.error("log channel is not valid or sendable."); + } + await activityService.set("playing"); + logger.info("ready"); +}); diff --git a/adapters/discord/src/listeners/stop.listener.ts b/adapters/discord/src/listeners/stop.listener.ts new file mode 100644 index 0000000..52ffc7a --- /dev/null +++ b/adapters/discord/src/listeners/stop.listener.ts @@ -0,0 +1,15 @@ +import { handleShutdown } from "actions/shutdown"; + +process.on("exit", async () => { + await handleShutdown(); +}); + +process.on("SIGINT", async () => { + await handleShutdown(); + process.exit(0); +}); + +process.on("SIGTERM", async () => { + await handleShutdown(); + process.exit(0); +});