diff --git a/lib/bot.ts b/lib/bot.ts index a319c9a..beef087 100644 --- a/lib/bot.ts +++ b/lib/bot.ts @@ -128,11 +128,10 @@ export default class Bot { this.attachIrcListeners(); - // IRC handlers wil - this.logger.info('Connecting to Discord'); // Begin connection to remote servers - await this.discord.connect(); await this.ircClient.connect(this.config.server, this.config.port, this.config.tls); + this.logger.info('Connecting to Discord'); + await this.discord.connect(); // Extract id and token from Webhook urls and connect. this.channelMapping = await ChannelMapper.CreateAsync(this.config, this, this.discord); diff --git a/lib/channelMapping.ts b/lib/channelMapping.ts index d6cf00d..426c909 100644 --- a/lib/channelMapping.ts +++ b/lib/channelMapping.ts @@ -1,6 +1,6 @@ import Bot from './bot.ts'; import { Config } from './config.ts'; -import { Client, GuildTextChannel, Webhook } from './deps.ts'; +import { CommandClient, GuildTextChannel, Webhook } from './deps.ts'; type Hook = { id: string; @@ -20,7 +20,7 @@ export class ChannelMapper { discordIdToMapping: Map = new Map(); ircNameToMapping: Map = new Map(); - public static CreateAsync = async (config: Config, bot: Bot, discord: Client) => { + public static CreateAsync = async (config: Config, bot: Bot, discord: CommandClient) => { const me = new ChannelMapper(); for (const [discordChannelNameOrId, ircChannelNameAndOrPassword] of Object.entries(config.channelMapping)) { @@ -82,7 +82,7 @@ export class ChannelMapper { } } - private static async findDiscordChannel(discordChannelName: string, discord: Client) { + private static async findDiscordChannel(discordChannelName: string, discord: CommandClient) { const discordChannel = await discord.channels.get(discordChannelName); if (!discordChannel && discordChannelName.startsWith('#')) { diff --git a/lib/deps.ts b/lib/deps.ts index 7ea9898..8dfb6a2 100644 --- a/lib/deps.ts +++ b/lib/deps.ts @@ -17,7 +17,8 @@ export type { AnyRawCommand } from 'https://deno.land/x/irc@v0.15.0/core/protoco // Harmony/Discord exports export { AllowedMentionType, - Client, + Command, + CommandClient, DiscordAPIError, event, GatewayIntents, @@ -27,6 +28,7 @@ export { User, Webhook, } from 'https://raw.githubusercontent.com/harmonyland/harmony/main/mod.ts'; +export type { CommandContext } from 'https://raw.githubusercontent.com/harmonyland/harmony/main/mod.ts'; export type { AllWebhookMessageOptions } from 'https://raw.githubusercontent.com/harmonyland/harmony/main/src/structures/webhook.ts'; // std exports export { resolve as resolvePath } from 'https://deno.land/std@0.203.0/path/mod.ts'; diff --git a/lib/discordClient.ts b/lib/discordClient.ts index 14cf78c..5956a5d 100644 --- a/lib/discordClient.ts +++ b/lib/discordClient.ts @@ -1,11 +1,44 @@ import Bot from './bot.ts'; import { escapeMarkdown } from './helpers.ts'; -import { Client, event, GatewayIntents, Message } from './deps.ts'; +import { Command, CommandClient, CommandContext, event, GatewayIntents, Message } from './deps.ts'; -export class DiscordClient extends Client { +class Names extends Command { + name = 'names'; + private bot: Bot; + + constructor(bot: Bot) { + super(); + this.bot = bot; + } + + async execute(ctx: CommandContext): Promise { + const ircChannel = this.bot?.channelMapping?.discordIdToMapping.get(ctx.channel.id)?.ircChannel; + // return early if message was in channel we don't post to + if (!ircChannel) return; + const users = this.bot?.channelUsers[ircChannel]; + if (users && users.length > 0) { + const ircNamesArr = new Array(...users); + await ctx.message.reply( + `Users in ${ircChannel}\n> ${ + ircNamesArr + .map(escapeMarkdown) + .join(', ') + }`, + ); + } else { + this.bot.logger.warn( + `No channelUsers found for ${ircChannel} when /names requested`, + ); + } + } +} + +export class DiscordClient extends CommandClient { private bot: Bot; constructor(bot: Bot) { super({ + prefix: '/', + caseSensitive: false, intents: [ GatewayIntents.GUILDS, GatewayIntents.GUILD_MEMBERS, @@ -16,7 +49,8 @@ export class DiscordClient extends Client { }); this.bot = bot; // Reconnect event has to be hooked manually due to naming conflict - this.on('reconnect', (shardId) => this.bot.logger.info(`Reconnected to Discord (shard ID ${shardId})`)); + this.on('reconnect', (shardId) => this.bot?.logger.info(`Reconnected to Discord (shard ID ${shardId})`)); + this.commands.add(new Names(bot)); } @event() @@ -32,37 +66,16 @@ export class DiscordClient extends Client { @event() async messageCreate(message: Message): Promise { - // Show the IRC channel's /names list when asked for in Discord - if (message.content.toLowerCase() === '/names') { - if (!message.channel.isGuildText()) return; - // return early if message was in channel we don't post to - if (!(this.bot.channelMapping?.discordIdToMapping.get(message.channel.id))) { - return; - } - const ircChannel = this.bot.channelMapping?.discordIdToMapping.get(message.channel.id)?.ircChannel; - if (!ircChannel) return; - if (this.bot.channelUsers[ircChannel]) { - const ircNames = this.bot.channelUsers[ircChannel].values(); - const ircNamesArr = new Array(...ircNames); - await this.bot.sendExactToDiscord( - ircChannel, - `Users in ${ircChannel}\n> ${ - ircNamesArr - .map(escapeMarkdown) //TODO: Switch to discord.js escape markdown - .join(', ') - }`, - ); - } else { - this.bot.logger.warn( - `No channelUsers found for ${ircChannel} when /names requested`, - ); - // Pass the command through if channelUsers is empty - await this.bot.sendToIRC(message); - } - } else { - // Ignore this.bot messages and people leaving/joining - await this.bot.sendToIRC(message); + if (message.content.trim() === '/names') return; + if (!message.channel.isGuildText()) return; + // return early if message was in channel we don't post to + if (!(this.bot.channelMapping?.discordIdToMapping.get(message.channel.id))) { + return; } + const ircChannel = this.bot?.channelMapping?.discordIdToMapping.get(message.channel.id)?.ircChannel; + if (!ircChannel) return; + // Ignore this.bot? messages and people leaving/joining + await this.bot.sendToIRC(message); } @event()