1
0
Fork 0
conan-discord/src/bot/readline.ts

102 lines
2.5 KiB
TypeScript

import { doesExist } from '@apextoaster/js-utils';
import { stdin as input, stdout as output } from 'node:process';
import { createInterface } from 'node:readline/promises';
import { Author, Command, CommandContext, Message } from '../command/index.js';
import { catchAndLog } from '../utils/async.js';
import { LOCAL_DISCRIMINATOR, matchCommands } from '../utils/command.js';
import { Bot, BotContext } from './index.js';
/**
* Readline-only methods.
*
* @public
*/
export interface ReadlineBot extends Bot {
destroy(): void;
}
export const READLINE_AUTHOR: Author = {
discriminator: LOCAL_DISCRIMINATOR,
username: 'readline',
};
/**
* Start the readline bot interface.
*
* This reads input from stdin and send replies through the logging system, and is meant for interactive
* debugging.
*
* @public
*/
export async function readlineConnect(botContext: BotContext, commands: ReadonlyArray<Command>): Promise<ReadlineBot> {
const { args, logger } = botContext;
const rl = createInterface({ input, output });
async function onLine(line: string) {
logger.debug({ line }, 'message received');
const matchingCommands = matchCommands(line, commands, args.commandPrefix);
for (const command of matchingCommands) {
logger.debug({
command: command.name,
}, 'message matched command');
const context: CommandContext = {
...botContext,
bot,
command,
};
if (doesExist(command.auth)) {
const allowed = await command.auth(READLINE_AUTHOR, context);
logger.debug({ allowed, author: READLINE_AUTHOR, command }, 'command auth check');
if (allowed === false) {
logger.warn({ author: READLINE_AUTHOR, command }, 'author attempted to use unauthorized command');
continue;
}
}
const message: Message = {
author: READLINE_AUTHOR,
content: line,
async reply(reply: string) {
logger.info({ reply }, 'reply from command');
return this;
},
};
try {
await command.run(message, context);
} catch (err) {
logger.error(err, 'error executing command');
} finally {
rl.prompt();
}
}
}
rl.on('line', (line) => {
catchAndLog(onLine(line), logger, 'error handling line');
});
rl.setPrompt('> ');
const bot: Bot = {
async connect() {
rl.prompt();
return true;
},
destroy() {
rl.close();
},
async getUser() {
return READLINE_AUTHOR;
},
};
return bot;
}