|
| 1 | +import CookiecordClient from 'cookiecord'; |
| 2 | +import { Channel, GuildMember, TextChannel, User } from 'discord.js'; |
| 3 | +import { inspect } from 'util'; |
| 4 | +import { logChannelId } from './env'; |
| 5 | + |
| 6 | +const logDebounceTime = 5000; |
| 7 | +const logMaxLength = 2000; |
| 8 | + |
| 9 | +export async function hookLog(client: CookiecordClient) { |
| 10 | + const guild = client.guilds.cache.get( |
| 11 | + (await client.guilds.fetch()).first()!.id, |
| 12 | + )!; |
| 13 | + const channel = (await guild.channels.fetch(logChannelId)) as TextChannel; |
| 14 | + let curLogText = ''; |
| 15 | + let timeout: NodeJS.Timeout | null = null; |
| 16 | + const origLog = console.log; |
| 17 | + console.log = (...args) => { |
| 18 | + origLog(...args); |
| 19 | + postLog(args); |
| 20 | + }; |
| 21 | + const origError = console.error; |
| 22 | + console.error = (...args) => { |
| 23 | + origError(...args); |
| 24 | + postLog(['[ERROR]', ...args]); |
| 25 | + }; |
| 26 | + console.log('Writing logs to', channel); |
| 27 | + function argToString(arg: unknown) { |
| 28 | + if (typeof arg === 'string') return arg; |
| 29 | + return inspect(arg); |
| 30 | + } |
| 31 | + function postLog(args: unknown[]) { |
| 32 | + curLogText += `[${new Date().toISOString()}] ${args |
| 33 | + .map(argToString) |
| 34 | + .join(' ')}\n`; |
| 35 | + if (timeout) clearTimeout(timeout); |
| 36 | + while (curLogText.length > logMaxLength) { |
| 37 | + const initial = |
| 38 | + curLogText.match(/^[^]{0,2000}\n/g)?.[0] ?? |
| 39 | + curLogText.slice(0, 2000); |
| 40 | + curLogText = curLogText.slice(initial.length); |
| 41 | + postCodeblock(initial); |
| 42 | + } |
| 43 | + if (curLogText.trim().length) |
| 44 | + timeout = setTimeout(() => { |
| 45 | + postCodeblock(curLogText); |
| 46 | + curLogText = ''; |
| 47 | + }, logDebounceTime); |
| 48 | + } |
| 49 | + async function postCodeblock(content: string) { |
| 50 | + channel.send(`\`\`\`ts\n${content}\n\`\`\``); |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +function defineCustomUtilInspect<T>( |
| 55 | + Cls: { new (...args: any): T; prototype: T }, |
| 56 | + cb: (value: T) => string, |
| 57 | +) { |
| 58 | + // @ts-ignore |
| 59 | + Cls.prototype[inspect.custom] = function () { |
| 60 | + return cb(this); |
| 61 | + }; |
| 62 | +} |
| 63 | + |
| 64 | +const inspectUser = (user: User) => |
| 65 | + `@${user.username}#${user.discriminator}/${user.id}`; |
| 66 | +defineCustomUtilInspect(User, inspectUser); |
| 67 | +defineCustomUtilInspect(GuildMember, member => inspectUser(member.user)); |
| 68 | + |
| 69 | +defineCustomUtilInspect(Channel, channel => |
| 70 | + 'name' in channel |
| 71 | + ? `#${(channel as any).name}/${(channel as Channel).id}` |
| 72 | + : `#${channel.id}`, |
| 73 | +); |
0 commit comments