fancy logging

This commit is contained in:
Jill 2023-11-11 03:54:11 +03:00
parent 726586f377
commit afcd63039c
Signed by: oat
GPG Key ID: 33489AA58A955108
9 changed files with 125 additions and 22 deletions

View File

@ -12,11 +12,13 @@
"author": "oatmealine",
"license": "AGPL-3.0",
"dependencies": {
"chalk": "^4.1.2",
"d3-array": "^2.12.1",
"discord.js": "^14.13.0",
"got": "^11.8.6",
"knex": "^3.0.1",
"parse-color": "^1.0.0",
"pretty-bytes": "^5.6.0",
"random-seed": "^0.3.0",
"sqlite3": "^5.1.6"
},

View File

@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false
dependencies:
chalk:
specifier: ^4.1.2
version: 4.1.2
d3-array:
specifier: ^2.12.1
version: 2.12.1
@ -20,6 +23,9 @@ dependencies:
parse-color:
specifier: ^1.0.0
version: 1.0.0
pretty-bytes:
specifier: ^5.6.0
version: 5.6.0
random-seed:
specifier: ^0.3.0
version: 0.3.0
@ -537,7 +543,6 @@ packages:
engines: {node: '>=8'}
dependencies:
color-convert: 2.0.1
dev: true
/aproba@2.0.0:
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
@ -650,7 +655,6 @@ packages:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
dev: true
/chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
@ -679,11 +683,9 @@ packages:
engines: {node: '>=7.0.0'}
dependencies:
color-name: 1.1.4
dev: true
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
/color-support@1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
@ -799,6 +801,7 @@ packages:
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
requiresBuild: true
dev: false
/encoding@0.1.13:
@ -1135,7 +1138,6 @@ packages:
/has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
dev: true
/has-unicode@2.0.1:
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
@ -1239,6 +1241,7 @@ packages:
/inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
requiresBuild: true
/internmap@1.0.1:
resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
@ -1269,6 +1272,7 @@ packages:
/is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
requiresBuild: true
dev: false
/is-glob@4.0.3:
@ -1739,6 +1743,11 @@ packages:
engines: {node: '>= 0.8.0'}
dev: true
/pretty-bytes@5.6.0:
resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
engines: {node: '>=6'}
dev: false
/promise-inflight@1.0.1:
resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
requiresBuild: true
@ -1859,6 +1868,7 @@ packages:
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
requiresBuild: true
dev: false
/safer-buffer@2.1.2:
@ -1977,6 +1987,7 @@ packages:
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
requiresBuild: true
dependencies:
safe-buffer: 5.2.1
dev: false
@ -1997,7 +2008,6 @@ packages:
engines: {node: '>=8'}
dependencies:
has-flag: 4.0.0
dev: true
/supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
@ -2111,6 +2121,7 @@ packages:
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
requiresBuild: true
dev: false
/webidl-conversions@3.0.1:

View File

@ -2,6 +2,7 @@ import { RoleCreateOptions, GuildMember, Interaction, EmbedBuilder, TextChannel,
import { default as parseColor, Color } from 'parse-color';
import { isColorRole, COLOR_ROLE_SEPERATOR } from '../lib/assignableRoles';
import { knownServers } from '../lib/knownServers';
import * as log from '../lib/log';
const PREVIEW_DURATION = 1000 * 60;
@ -17,7 +18,7 @@ async function applyColor(member: GuildMember, color: Color) {
await removeAllColorRoles(member);
const colorRoleSeperator = await member.guild.roles.fetch(COLOR_ROLE_SEPERATOR);
if (!colorRoleSeperator) console.error('no color role seperator found?!?!');
if (!colorRoleSeperator) log.error('no color role seperator found?!?!');
const roleSettings: RoleCreateOptions = {
name: color.hex,

View File

@ -1,6 +1,7 @@
import { RoleCreateOptions, GuildMember, Interaction, SlashCommandBuilder } from 'discord.js';
import { pronouns, PRONOUN_ROLE_SEPERATOR } from '../lib/assignableRoles';
import { knownServers } from '../lib/knownServers';
import * as log from '../lib/log';
function extendOption(t: string) {
return {name: t, value: t};
@ -30,7 +31,7 @@ module.exports = {
const pronoun = interaction.options.getString('pronoun', true);
const pronounRoleSeperator = await member.guild.roles.fetch(PRONOUN_ROLE_SEPERATOR);
if (!pronounRoleSeperator) console.error('no pronoun role seperator found?!?!');
if (!pronounRoleSeperator) log.error('no pronoun role seperator found?!?!');
const roleSettings: RoleCreateOptions = {
name: pronoun,

View File

@ -1,9 +1,12 @@
import { Client, GatewayIntentBits, Events, Collection, TextChannel } from 'discord.js';
import { Client, GatewayIntentBits, Events, Collection } from 'discord.js';
import * as fs from 'fs';
const { token } = JSON.parse(fs.readFileSync('./config.json', 'utf8'));
import * as path from 'path';
import { initializeAnnouncements } from './lib/subscriptions';
import { initTables } from './lib/db';
import * as log from './lib/log';
import chalk from 'chalk';
import prettyBytes from 'pretty-bytes';
const bot = new Client({
intents: [
@ -17,7 +20,26 @@ const bot = new Client({
],
});
async function init() {
log.nonsense('booting chip...');
await initTables();
log.nonsense('setting up connection...');
try {
await bot.login(token);
} catch (err) {
log.error('error: network hardware broken?', err);
log.error(`${chalk.bold('emergency mode could not be established.')} shutting down.`);
}
}
bot.on(Events.ClientReady, async () => {
log.info('jillo online');
log.nonsense('finishing launch');
initializeAnnouncements(bot);
bot.commands = new Collection();
@ -28,7 +50,11 @@ bot.on(Events.ClientReady, async () => {
if (cmd.onClientReady) cmd.onClientReady(bot);
}
console.log('jillo online');
log.info('jillo firmware up and running');
log.nonsense(`| running on ${process.platform} ${process.config.variables.host_arch}`);
log.nonsense(`| node ${process.version} V8 v${process.versions.v8}`);
const memory = process.memoryUsage();
log.nonsense(`| ${prettyBytes(memory.rss)} memory usage, ${prettyBytes(memory.heapUsed)} / ${prettyBytes(memory.heapTotal)} heap usage`);
});
bot.on(Events.InteractionCreate, async (interaction) => {
@ -41,7 +67,7 @@ bot.on(Events.InteractionCreate, async (interaction) => {
} catch (error) {
if (interaction.isRepliable() && !interaction.replied && !interaction.deferred) interaction.reply({ content: '`ERROR`', ephemeral: true });
if (interaction.deferred) interaction.followUp('`ERROR`');
console.error(error);
log.error(error);
}
} else if (interaction.isAutocomplete()) {
const command = interaction.client.commands.get(interaction.commandName);
@ -50,18 +76,13 @@ bot.on(Events.InteractionCreate, async (interaction) => {
try {
await command.autocomplete(interaction);
} catch (error) {
console.error(error);
log.error(error);
}
}
});
bot.on(Events.MessageDelete, (msg) => {
console.log(`${msg.author?.username}#${msg.author?.discriminator} in #${msg.channel instanceof TextChannel ? msg.channel.name : '?'} at ${msg.createdAt.toISOString()}`);
console.log(`${msg.content}`);
});
process.on('uncaughtException', err => {
console.error(err);
log.error(err);
});
initTables().then(() => bot.login(token));
init();

View File

@ -1,11 +1,18 @@
import knex from 'knex';
import * as log from './log';
export const db = knex({
client: 'sqlite3',
connection: {
filename: './jillo.sqlite'
},
useNullAsDefault: true
useNullAsDefault: true,
log: {
warn: log.warn,
error: log.error,
deprecate: log.warn,
debug: log.info
}
});
export interface ScheduledSubscription {

View File

@ -1,4 +1,5 @@
import { Interaction, Message, TextBasedChannel, User } from 'discord.js';
import * as log from '../lib/log';
export const RANDOM_WORDS = [
'tarsorado', 'aboba', 'robtop', 'viprin', 'milk', 'milking station', 'radiation', 'extreme', 'glogging', 'glogged',
@ -102,7 +103,7 @@ export async function startGame(interaction: Interaction, startingUser: User, na
collector.on('end', async (_, reason) => {
clearInterval(updateInterval);
await m.reactions.removeAll().catch(error => console.error(error));
await m.reactions.removeAll().catch(error => log.error(error));
if (reason === 'cancelled') {
m.edit(formatMessage(participants, 0, name, false, true));

58
src/lib/log.ts Normal file
View File

@ -0,0 +1,58 @@
import chalk from 'chalk';
import * as util from 'util';
enum Severity {
Info,
Warn,
Error,
Nonsense
}
function severityChar(severity: Severity) {
switch (severity) {
case Severity.Info:
return ' ';
case Severity.Warn:
return chalk.yellow('!');
case Severity.Error:
return chalk.red('!');
case Severity.Nonsense:
return chalk.grey('.');
}
}
const inspectOptions: util.InspectOptions = {
colors: true
};
function format(thing: unknown): string {
if (typeof thing === 'string') {
return thing;
} else if (thing instanceof Error) {
return thing.stack || thing.toString();
} else {
return util.inspect(thing, inspectOptions);
}
}
function log(severity: Severity, ...message: unknown[]) {
const formatted = message
.map(m => format(m))
.reduce((l, r) => l.includes('\n') || r.includes('\n') ? (l + '\n' + r) : (l + ' ' + r), '')
.trim();
const prefix = severityChar(severity) + ' ';
process.stdout.write(`${prefix}${formatted.split('\n').join('\n' + prefix)}\n`);
}
export function info(...message: unknown[]) {
log(Severity.Info, ...message);
}
export function warn(...message: unknown[]) {
log(Severity.Warn, ...message);
}
export function error(...message: unknown[]) {
log(Severity.Error, ...message);
}
export function nonsense(...message: unknown[]) {
log(Severity.Nonsense, ...message);
}

View File

@ -1,5 +1,6 @@
import { Client, TextChannel } from 'discord.js';
import { ScheduledSubscription, Subscription, db } from './db';
import * as log from '../lib/log';
interface AnnouncementType {
hour: number;
@ -217,7 +218,7 @@ export function initializeAnnouncements(bot: Client) {
`${announcement.messagesPrefix ? announcement.messagesPrefix : ''} ${announcement.messages[Math.floor(Math.random() * announcement.messages.length)]}`
)
)
.catch(err => console.error(`failed to send ${k} announcement to ${channel}: ${err}`));
.catch(err => log.error(`failed to send ${k} announcement to ${channel}:`, err));
}
}
}, 1000);