feat(events)!: use better api for events

This commit is contained in:
PalmDevs
2024-07-04 20:53:29 +07:00
parent af3759caf4
commit 0bfd03583d
11 changed files with 48 additions and 44 deletions

View File

@@ -1,8 +1,7 @@
import { on } from '$utils/api/events' import { on, withContext } from '$utils/api/events'
import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared' import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared'
import { api, logger } from 'src/context'
on('disconnect', (reason, msg) => { withContext(on, 'disconnect', ({ api, config, logger }, reason, msg) => {
if (reason === DisconnectReason.PlannedDisconnect && api.isStopping) return if (reason === DisconnectReason.PlannedDisconnect && api.isStopping) return
const ws = api.client.ws const ws = api.client.ws
@@ -16,8 +15,7 @@ on('disconnect', (reason, msg) => {
}`, }`,
) )
// TODO: move to config if (api.disconnectCount >= (config.api.disconnectLimit ?? 3)) {
if (api.disconnectCount >= 3) {
console.error('Disconnected from bot API too many times') console.error('Disconnected from bot API too many times')
// We don't want the process hanging // We don't want the process hanging
process.exit(1) process.exit(1)
@@ -26,5 +24,6 @@ on('disconnect', (reason, msg) => {
logger.info( logger.info(
`Disconnected from bot API ${++api.disconnectCount} times (this time because: ${reason}, ${msg}), reconnecting again...`, `Disconnected from bot API ${++api.disconnectCount} times (this time because: ${reason}, ${msg}), reconnecting again...`,
) )
setTimeout(() => api.client.connect(), 10000) setTimeout(() => api.client.connect(), 10000)
}) })

View File

@@ -1,6 +1,3 @@
import { on } from '$utils/api/events' import { on, withContext } from '$utils/api/events'
import { logger } from 'src/context'
on('ready', () => { withContext(on, 'ready', ({ logger }) => void logger.info('Connected to the bot API'))
logger.info('Connected to the bot API')
})

View File

@@ -1,18 +1,17 @@
import { on } from '$/utils/discord/events' import { on } from '$/utils/discord/events'
import { cureNickname } from '$/utils/discord/moderation' import { cureNickname } from '$/utils/discord/moderation'
on('guildMemberUpdate', async (_, oldMember, newMember) => { on('guildMemberUpdate', async (oldMember, newMember) => {
if (newMember.user.bot) return if (newMember.user.bot) return
if (oldMember.displayName !== newMember.displayName) await cureNickname(newMember) if (oldMember.displayName !== newMember.displayName) await cureNickname(newMember)
}) })
on('guildMemberAdd', (_, member) => { on('guildMemberAdd', member => {
if (member.user.bot) return if (member.user.bot) return
cureNickname(member) cureNickname(member)
}) })
on('messageCreate', async (_, msg) => { on('messageCreate', async msg => {
if (msg.author.bot) return if (msg.author.bot || !msg.member) return
if (!msg.member) return
await cureNickname(msg.member) await cureNickname(msg.member)
}) })

View File

@@ -1,9 +1,9 @@
import { appliedPresets } from '$/database/schemas' import { appliedPresets } from '$/database/schemas'
import { on } from '$/utils/discord/events' import { on, withContext } from '$/utils/discord/events'
import { applyRolesUsingPreset } from '$/utils/discord/rolePresets' import { applyRolesUsingPreset } from '$/utils/discord/rolePresets'
import { and, eq, gt } from 'drizzle-orm' import { and, eq, gt } from 'drizzle-orm'
on('guildMemberAdd', async ({ database }, member) => { withContext(on, 'guildMemberAdd', async ({ database }, member) => {
const applieds = await database.query.appliedPresets.findMany({ const applieds = await database.query.appliedPresets.findMany({
where: and( where: and(
eq(appliedPresets.memberId, member.id), eq(appliedPresets.memberId, member.id),

View File

@@ -1,8 +1,8 @@
import CommandError from '$/classes/CommandError' import CommandError from '$/classes/CommandError'
import { createErrorEmbed, createStackTraceEmbed } from '$utils/discord/embeds' import { createErrorEmbed, createStackTraceEmbed } from '$utils/discord/embeds'
import { on } from '$utils/discord/events' import { on, withContext } from '$utils/discord/events'
export default on('interactionCreate', async (context, interaction) => { withContext(on, 'interactionCreate', async (context, interaction) => {
if (!interaction.isChatInputCommand()) return if (!interaction.isChatInputCommand()) return
const { logger, discord, config } = context const { logger, discord, config } = context

View File

@@ -1,13 +1,13 @@
import { responses } from '$/database/schemas' import { responses } from '$/database/schemas'
import { handleUserResponseCorrection } from '$/utils/discord/messageScan' import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds' import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds'
import { on } from '$utils/discord/events' import { on, withContext } from '$utils/discord/events'
import type { ButtonInteraction, StringSelectMenuInteraction, TextBasedChannel } from 'discord.js' import type { ButtonInteraction, StringSelectMenuInteraction, TextBasedChannel } from 'discord.js'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
// No permission check required as it is already done when the user reacts to a bot response // No permission check required as it is already done when the user reacts to a bot response
export default on('interactionCreate', async (context, interaction) => { withContext(on, 'interactionCreate', async (context, interaction) => {
const { const {
logger, logger,
database: db, database: db,

View File

@@ -2,15 +2,15 @@ import { MessageScanLabeledResponseReactions } from '$/constants'
import { responses } from '$/database/schemas' import { responses } from '$/database/schemas'
import { getResponseFromText, shouldScanMessage } from '$/utils/discord/messageScan' import { getResponseFromText, shouldScanMessage } from '$/utils/discord/messageScan'
import { createMessageScanResponseEmbed } from '$utils/discord/embeds' import { createMessageScanResponseEmbed } from '$utils/discord/embeds'
import { on } from '$utils/discord/events' import { on, withContext } from '$utils/discord/events'
on('messageCreate', async (ctx, msg) => { withContext(on, 'messageCreate', async (context, msg) => {
const { const {
api, api,
config: { messageScan: config }, config: { messageScan: config },
database: db, database: db,
logger, logger,
} = ctx } = context
if (!config || !config.responses) return if (!config || !config.responses) return
@@ -21,7 +21,7 @@ on('messageCreate', async (ctx, msg) => {
try { try {
logger.debug(`Classifying message ${msg.id}`) logger.debug(`Classifying message ${msg.id}`)
const { response, label } = await getResponseFromText(msg.content, filteredResponses, ctx) const { response, label } = await getResponseFromText(msg.content, filteredResponses, context)
if (response) { if (response) {
logger.debug('Response found') logger.debug('Response found')
@@ -59,7 +59,7 @@ on('messageCreate', async (ctx, msg) => {
try { try {
const { text: content } = await api.client.parseImage(attachment.url) const { text: content } = await api.client.parseImage(attachment.url)
const { response } = await getResponseFromText(content, filteredResponses, ctx, true) const { response } = await getResponseFromText(content, filteredResponses, context, true)
if (response) { if (response) {
logger.debug(`Response found for attachment: ${attachment.url}`) logger.debug(`Response found for attachment: ${attachment.url}`)

View File

@@ -1,6 +1,6 @@
import { MessageScanLabeledResponseReactions as Reactions } from '$/constants' import { MessageScanLabeledResponseReactions as Reactions } from '$/constants'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$/utils/discord/embeds' import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$/utils/discord/embeds'
import { on } from '$/utils/discord/events' import { on, withContext } from '$/utils/discord/events'
import { import {
ActionRowBuilder, ActionRowBuilder,
@@ -10,14 +10,14 @@ import {
StringSelectMenuOptionBuilder, StringSelectMenuOptionBuilder,
} from 'discord.js' } from 'discord.js'
import type { ConfigMessageScanResponseLabelConfig } from '$/../config.schema'
import { responses } from '$/database/schemas' import { responses } from '$/database/schemas'
import { handleUserResponseCorrection } from '$/utils/discord/messageScan' import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
import type { ConfigMessageScanResponseLabelConfig } from 'config.schema'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
const PossibleReactions = Object.values(Reactions) as string[] const PossibleReactions = Object.values(Reactions) as string[]
on('messageReactionAdd', async (context, rct, user) => { withContext(on, 'messageReactionAdd', async (context, rct, user) => {
if (user.bot) return if (user.bot) return
const { database: db, logger, config } = context const { database: db, logger, config } = context

View File

@@ -3,9 +3,9 @@ import { appliedPresets } from '$/database/schemas'
import { removeRolePreset } from '$/utils/discord/rolePresets' import { removeRolePreset } from '$/utils/discord/rolePresets'
import type { Client } from 'discord.js' import type { Client } from 'discord.js'
import { lt } from 'drizzle-orm' import { lt } from 'drizzle-orm'
import { on } from 'src/utils/discord/events' import { on, withContext } from 'src/utils/discord/events'
export default on('ready', ({ config, logger }, client) => { export default withContext(on, 'ready', ({ config, logger }, client) => {
logger.info(`Connected to Discord API, logged in as ${client.user.displayName} (@${client.user.tag})!`) logger.info(`Connected to Discord API, logged in as ${client.user.displayName} (@${client.user.tag})!`)
logger.info( logger.info(
`Bot is in ${client.guilds.cache.size} guilds, if this is not expected, please run the /leave-unknowns command`, `Bot is in ${client.guilds.cache.size} guilds, if this is not expected, please run the /leave-unknowns command`,

View File

@@ -1,15 +1,20 @@
import type { ClientWebSocketEvents } from '@revanced/bot-api' import type { ClientWebSocketEvents } from '@revanced/bot-api'
import { api } from '../../context' import * as context from '../../context'
const { client } = api const { client } = context.api
export const on = <Event extends EventName>(event: Event, listener: ListenerOf<Event>) => { export const withContext = <Event extends EventName>(
client.on(event, listener) fn: typeof on | typeof once,
} event: Event,
listener: ListenerWithContextOf<Event>,
// @ts-expect-error: Not smart enough, sorry!
) => fn(event, (...args) => listener(context, ...args))
export const once = <Event extends EventName>(event: Event, listener: ListenerOf<Event>) => { export const on = <Event extends EventName>(event: Event, listener: ListenerOf<Event>) => client.on(event, listener)
client.once(event, listener) export const once = <Event extends EventName>(event: Event, listener: ListenerOf<Event>) => client.once(event, listener)
}
export type EventName = keyof ClientWebSocketEvents export type EventName = keyof ClientWebSocketEvents
export type ListenerOf<Event extends EventName> = ClientWebSocketEvents[Event] export type ListenerOf<Event extends EventName> = ClientWebSocketEvents[Event]
export type ListenerWithContextOf<Event extends EventName> = (
...args: [typeof import('../../context'), ...Parameters<ClientWebSocketEvents[Event]>]
) => void | Promise<void>

View File

@@ -3,17 +3,21 @@ import type { ClientEvents } from 'discord.js'
const { client } = context.discord const { client } = context.discord
export const on = <Event extends EventName>(event: Event, listener: ListenerOf<Event>) => export const withContext = <Event extends EventName>(
client.on(event, (...args) => listener(context, ...args)) fn: typeof on | typeof once,
event: Event,
listener: ListenerWithContextOf<Event>,
) => fn(event, (...args) => listener(context, ...args))
export const once = <Event extends EventName>(event: Event, listener: ListenerOf<Event>) => export const on = <Event extends EventName>(event: Event, listener: ListenerOf<Event>) => client.on(event, listener)
client.once(event, (...args) => listener(context, ...args)) export const once = <Event extends EventName>(event: Event, listener: ListenerOf<Event>) => client.once(event, listener)
export type EventName = keyof ClientEvents export type EventName = keyof ClientEvents
export type EventMap = { export type EventMap = {
[K in EventName]: ListenerOf<K> [K in EventName]: ListenerOf<K>
} }
type ListenerOf<Event extends EventName> = ( type ListenerOf<Event extends EventName> = (...args: ClientEvents[Event]) => void | Promise<void>
type ListenerWithContextOf<Event extends EventName> = (
...args: [typeof import('$/context'), ...ClientEvents[Event]] ...args: [typeof import('$/context'), ...ClientEvents[Event]]
) => void | Promise<void> ) => void | Promise<void>