ANSI color codeblocks, ephemeral message flag update, etc.
This commit is contained in:
parent
b497423ba7
commit
4f5a90b3bb
119
_opt/ansi.js
Normal file
119
_opt/ansi.js
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import { SlashCommandBuilder, PermissionFlagsBits } from 'discord.js';
|
||||||
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
|
import { CODES } from '../_src/ansiColors.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combined ANSI utilities module
|
||||||
|
* - /ansi: preview nested [tag]…[/] ANSI coloring
|
||||||
|
* - /ansitheme: display full BG×FG theme chart
|
||||||
|
* Both commands are Admin-only.
|
||||||
|
*/
|
||||||
|
export const commands = [
|
||||||
|
// Preview arbitrary ANSI tags
|
||||||
|
{
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('ansi')
|
||||||
|
.setDescription('Preview an ANSI-colored code block')
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||||
|
.setDMPermission(false)
|
||||||
|
.addStringOption(opt =>
|
||||||
|
opt
|
||||||
|
.setName('text')
|
||||||
|
.setDescription('Use [red]…[/], [bold,blue]…[/], escape \\[/]')
|
||||||
|
.setRequired(true)
|
||||||
|
)
|
||||||
|
.addBooleanOption(opt =>
|
||||||
|
opt
|
||||||
|
.setName('ephemeral')
|
||||||
|
.setDescription('Reply ephemerally?')
|
||||||
|
.setRequired(false)
|
||||||
|
),
|
||||||
|
async execute(interaction, client) {
|
||||||
|
const raw = interaction.options.getString('text', true);
|
||||||
|
const ephemeral = interaction.options.getBoolean('ephemeral') ?? true;
|
||||||
|
const colored = client.ansi`${raw}`;
|
||||||
|
const block = client.wrapAnsi(colored);
|
||||||
|
const opts = { content: block };
|
||||||
|
if (ephemeral) opts.flags = MessageFlags.Ephemeral;
|
||||||
|
await interaction.reply(opts);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Show complete ANSI theme chart
|
||||||
|
{
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('ansitheme')
|
||||||
|
.setDescription('Show ANSI color theme chart')
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
||||||
|
.setDMPermission(false)
|
||||||
|
.addBooleanOption(opt =>
|
||||||
|
opt
|
||||||
|
.setName('ephemeral')
|
||||||
|
.setDescription('Reply ephemerally?')
|
||||||
|
.setRequired(false)
|
||||||
|
),
|
||||||
|
async execute(interaction, client) {
|
||||||
|
const fgs = ['gray', 'red', 'green', 'yellow', 'blue', 'pink', 'cyan', 'white'];
|
||||||
|
const bgs = ['bgGray', 'bgOrange', 'bgBlue', 'bgTurquoise', 'bgFirefly', 'bgIndigo', 'bgLightGray', 'bgWhite'];
|
||||||
|
const pad = 8;
|
||||||
|
// Column header with padded labels (no colors) - shifted right by 1
|
||||||
|
const header = ' ' + fgs.map(f => f.padEnd(pad, ' ')).join('');
|
||||||
|
// Sample row with no background (padded cells)
|
||||||
|
let defaultRow = '';
|
||||||
|
for (const fg of fgs) {
|
||||||
|
const fgCode = CODES[fg];
|
||||||
|
const openNormal = `\u001b[${fgCode}m`;
|
||||||
|
const openBold = `\u001b[${fgCode};${CODES.bold}m`;
|
||||||
|
const openUnder = `\u001b[${fgCode};${CODES.underline}m`;
|
||||||
|
const cell = `${openNormal}sa\u001b[0m${openBold}mp\u001b[0m${openUnder}le\u001b[0m`;
|
||||||
|
defaultRow += ' ' + cell + ' ';
|
||||||
|
}
|
||||||
|
// Append default row label after one pad
|
||||||
|
defaultRow += 'default';
|
||||||
|
// Colored rows per background
|
||||||
|
const rows = [];
|
||||||
|
for (const bg of bgs) {
|
||||||
|
let row = '';
|
||||||
|
const bgCode = CODES[bg];
|
||||||
|
for (const fg of fgs) {
|
||||||
|
const fgCode = CODES[fg];
|
||||||
|
const openNormal = `\u001b[${bgCode};${fgCode}m`;
|
||||||
|
const openBold = `\u001b[${bgCode};${fgCode};${CODES.bold}m`;
|
||||||
|
const openUnder = `\u001b[${bgCode};${fgCode};${CODES.underline}m`;
|
||||||
|
const cell = `${openNormal}sa\u001b[0m${openBold}mp\u001b[0m${openUnder}le\u001b[0m`;
|
||||||
|
row += ' ' + cell + ' ';
|
||||||
|
}
|
||||||
|
// Append uncolored row label immediately after cell padding
|
||||||
|
row += bg;
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
// Determine ephemeral setting
|
||||||
|
const ephemeral = interaction.options.getBoolean('ephemeral') ?? true;
|
||||||
|
// Initial sample table (header + default row)
|
||||||
|
const sampleContent = [header, defaultRow].join('\n');
|
||||||
|
const optsSample = { content: client.wrapAnsi(sampleContent) };
|
||||||
|
if (ephemeral) optsSample.flags = MessageFlags.Ephemeral;
|
||||||
|
await interaction.reply(optsSample);
|
||||||
|
// Split colored rows into two tables
|
||||||
|
const half = Math.ceil(rows.length / 2);
|
||||||
|
const firstRows = rows.slice(0, half);
|
||||||
|
const secondRows = rows.slice(half);
|
||||||
|
// First colored table
|
||||||
|
const table1 = [header, ...firstRows].join('\n');
|
||||||
|
const opts1 = { content: client.wrapAnsi(table1) };
|
||||||
|
if (ephemeral) opts1.flags = MessageFlags.Ephemeral;
|
||||||
|
await interaction.followUp(opts1);
|
||||||
|
// Second colored table
|
||||||
|
if (secondRows.length > 0) {
|
||||||
|
const table2 = [header, ...secondRows].join('\n');
|
||||||
|
const opts2 = { content: client.wrapAnsi(table2) };
|
||||||
|
if (ephemeral) opts2.flags = MessageFlags.Ephemeral;
|
||||||
|
await interaction.followUp(opts2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function init(client) {
|
||||||
|
client.logger.info('[module:ansi] Loaded ANSI utilities');
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } from 'discord.js';
|
import { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } from 'discord.js';
|
||||||
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* botUtils module - provides administrative bot control commands
|
* botUtils module - provides administrative bot control commands
|
||||||
@ -28,16 +29,16 @@ export const commands = [
|
|||||||
const ownerId = client.config.owner;
|
const ownerId = client.config.owner;
|
||||||
// Check invoking user is the bot owner
|
// Check invoking user is the bot owner
|
||||||
if (interaction.user.id !== String(ownerId)) {
|
if (interaction.user.id !== String(ownerId)) {
|
||||||
return interaction.reply({ content: 'Only the bot owner can shutdown the bot.', ephemeral: true });
|
return interaction.reply({ content: 'Only the bot owner can shutdown the bot.', flags: MessageFlags.Ephemeral });
|
||||||
}
|
}
|
||||||
// Determine desired exit code (default 0)
|
// Determine desired exit code (default 0)
|
||||||
const exitCode = interaction.options.getInteger('code') ?? 0;
|
const exitCode = interaction.options.getInteger('code') ?? 0;
|
||||||
// Validate exit code bounds
|
// Validate exit code bounds
|
||||||
if (exitCode < 0 || exitCode > 254) {
|
if (exitCode < 0 || exitCode > 254) {
|
||||||
return interaction.reply({ content: 'Exit code must be between 0 and 254 inclusive.', ephemeral: true });
|
return interaction.reply({ content: 'Exit code must be between 0 and 254 inclusive.', flags: MessageFlags.Ephemeral });
|
||||||
}
|
}
|
||||||
// Acknowledge before shutting down
|
// Acknowledge before shutting down
|
||||||
await interaction.reply({ content: `Shutting down with exit code ${exitCode}...`, ephemeral: true });
|
await interaction.reply({ content: `Shutting down with exit code ${exitCode}...`, flags: MessageFlags.Ephemeral });
|
||||||
client.logger.info(
|
client.logger.info(
|
||||||
`[cmd:exit] Shutdown initiated by owner ${interaction.user.tag} (${interaction.user.id}), exit code ${exitCode}`
|
`[cmd:exit] Shutdown initiated by owner ${interaction.user.tag} (${interaction.user.id}), exit code ${exitCode}`
|
||||||
);
|
);
|
||||||
@ -72,9 +73,8 @@ export const commands = [
|
|||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
),
|
),
|
||||||
async execute(interaction, client) {
|
async execute(interaction, client) {
|
||||||
// Determine if response should be ephemeral (default true)
|
|
||||||
const ephemeral = interaction.options.getBoolean('ephemeral') ?? true;
|
const ephemeral = interaction.options.getBoolean('ephemeral') ?? true;
|
||||||
await interaction.deferReply({ ephemeral });
|
await interaction.deferReply({ flags: ephemeral ? MessageFlags.Ephemeral : undefined });
|
||||||
// Process metrics
|
// Process metrics
|
||||||
const uptimeSec = process.uptime();
|
const uptimeSec = process.uptime();
|
||||||
const hours = Math.floor(uptimeSec / 3600);
|
const hours = Math.floor(uptimeSec / 3600);
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
@ -72,12 +73,12 @@ export const commands = [
|
|||||||
async execute(interaction, client) {
|
async execute(interaction, client) {
|
||||||
const ownerId = client.config.owner;
|
const ownerId = client.config.owner;
|
||||||
if (interaction.user.id !== ownerId) {
|
if (interaction.user.id !== ownerId) {
|
||||||
return interaction.reply({ content: 'Only the bot owner can run git commands.', ephemeral: true });
|
return interaction.reply({ content: 'Only the bot owner can run git commands.', flags: MessageFlags.Ephemeral });
|
||||||
}
|
}
|
||||||
const raw = interaction.options.getString('args');
|
const raw = interaction.options.getString('args');
|
||||||
// Disallow semicolons to prevent command chaining
|
// Disallow semicolons to prevent command chaining
|
||||||
if (raw.includes(';')) {
|
if (raw.includes(';')) {
|
||||||
return interaction.reply({ content: 'Semicolons are not allowed in git arguments.', ephemeral: true });
|
return interaction.reply({ content: 'Semicolons are not allowed in git arguments.', flags: MessageFlags.Ephemeral });
|
||||||
}
|
}
|
||||||
const ephemeral = interaction.options.getBoolean('ephemeral') ?? true;
|
const ephemeral = interaction.options.getBoolean('ephemeral') ?? true;
|
||||||
const args = raw.match(/(?:[^\s"]+|"[^"]*")+/g)
|
const args = raw.match(/(?:[^\s"]+|"[^"]*")+/g)
|
||||||
@ -98,14 +99,18 @@ export const commands = [
|
|||||||
const outputChunks = chunkString(output, firstChunkSize);
|
const outputChunks = chunkString(output, firstChunkSize);
|
||||||
// Send first block with header + first output chunk
|
// Send first block with header + first output chunk
|
||||||
const firstBlock = header + (outputChunks[0] || '');
|
const firstBlock = header + (outputChunks[0] || '');
|
||||||
await interaction.reply({ content: formatCodeBlock(firstBlock), ephemeral });
|
const replyOpts = { content: formatCodeBlock(firstBlock) };
|
||||||
|
if (ephemeral) replyOpts.flags = MessageFlags.Ephemeral;
|
||||||
|
await interaction.reply(replyOpts);
|
||||||
// Send any remaining blocks without the header
|
// Send any remaining blocks without the header
|
||||||
for (let i = 1; i < outputChunks.length; i++) {
|
for (let i = 1; i < outputChunks.length; i++) {
|
||||||
await interaction.followUp({ content: formatCodeBlock(outputChunks[i]), ephemeral });
|
const fuOpts = { content: formatCodeBlock(outputChunks[i]) };
|
||||||
|
if (ephemeral) fuOpts.flags = MessageFlags.Ephemeral;
|
||||||
|
await interaction.followUp(fuOpts);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err instanceof GitError ? err.message : String(err);
|
const msg = err instanceof GitError ? err.message : String(err);
|
||||||
await interaction.reply({ content: `Error: ${msg}`, ephemeral: true });
|
await interaction.reply({ content: `Error: ${msg}`, flags: MessageFlags.Ephemeral });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
import { SlashCommandBuilder, PermissionFlagsBits, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } from 'discord.js';
|
import { SlashCommandBuilder, PermissionFlagsBits, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } from 'discord.js';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -30,10 +31,10 @@ export const commands = [
|
|||||||
// URL-based update
|
// URL-based update
|
||||||
if (url) {
|
if (url) {
|
||||||
client.logger.info(`[cmd:prompt] URL update requested for client ${clientId}: ${url}`);
|
client.logger.info(`[cmd:prompt] URL update requested for client ${clientId}: ${url}`);
|
||||||
await interaction.deferReply({ ephemeral: true });
|
await interaction.deferReply({ flags: MessageFlags.Ephemeral});
|
||||||
if (!url.toLowerCase().endsWith('.txt')) {
|
if (!url.toLowerCase().endsWith('.txt')) {
|
||||||
client.logger.warn(`[cmd:prompt] Invalid URL extension, must end .txt: ${url}`);
|
client.logger.warn(`[cmd:prompt] Invalid URL extension, must end .txt: ${url}`);
|
||||||
return interaction.editReply({ content: 'URL must point to a .txt file.', ephemeral: true });
|
return interaction.editReply({ content: 'URL must point to a .txt file.', flags: MessageFlags.Ephemeral});
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
@ -48,7 +49,7 @@ export const commands = [
|
|||||||
updatedBy: interaction.user.id
|
updatedBy: interaction.user.id
|
||||||
});
|
});
|
||||||
client.responsesPrompt = text;
|
client.responsesPrompt = text;
|
||||||
return interaction.editReply({ content: 'Prompt updated from URL.', ephemeral: true });
|
return interaction.editReply({ content: 'Prompt updated from URL.', flags: MessageFlags.Ephemeral});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
client.logger.error(`[cmd:prompt] URL update failed: ${err.message}`);
|
client.logger.error(`[cmd:prompt] URL update failed: ${err.message}`);
|
||||||
return interaction.editReply({ content: `Error fetching URL: ${err.message}`, ephemeral: true });
|
return interaction.editReply({ content: `Error fetching URL: ${err.message}`, ephemeral: true });
|
||||||
@ -151,7 +152,7 @@ export async function init(client, clientConfig) {
|
|||||||
updatedBy: interaction.user.id
|
updatedBy: interaction.user.id
|
||||||
});
|
});
|
||||||
client.responsesPrompt = newPrompt;
|
client.responsesPrompt = newPrompt;
|
||||||
await interaction.reply({ content: 'Prompt updated!', ephemeral: true });
|
await interaction.reply({ content: 'Prompt updated!', flags: MessageFlags.Ephemeral});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
client.logger.error(`responsesPrompt modal submit error: ${err.message}`);
|
client.logger.error(`responsesPrompt modal submit error: ${err.message}`);
|
||||||
await interaction.reply({ content: `Error saving prompt: ${err.message}`, ephemeral: true });
|
await interaction.reply({ content: `Error saving prompt: ${err.message}`, ephemeral: true });
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
/**
|
/**
|
||||||
* Slash command module for '/query'.
|
* Slash command module for '/query'.
|
||||||
* Defines and handles the /query command via the OpenAI Responses API,
|
* Defines and handles the /query command via the OpenAI Responses API,
|
||||||
@ -182,7 +183,7 @@ export const commands = [
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
client.logger.error(`[cmd:query] Error checking score: ${err.message}`);
|
client.logger.error(`[cmd:query] Error checking score: ${err.message}`);
|
||||||
return interaction.reply({ content: 'Error verifying your score. Please try again later.', ephemeral: true });
|
return interaction.reply({ content: 'Error verifying your score. Please try again later.', flags: MessageFlags.Ephemeral});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const prompt = interaction.options.getString('prompt');
|
const prompt = interaction.options.getString('prompt');
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
// _opt/schangar.js
|
// _opt/schangar.js
|
||||||
import { SlashCommandBuilder } from 'discord.js';
|
import { SlashCommandBuilder } from 'discord.js';
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { MessageFlags } from 'discord-api-types/v10';
|
||||||
// opt/scorekeeper.js
|
// opt/scorekeeper.js
|
||||||
import cron from 'node-cron';
|
import cron from 'node-cron';
|
||||||
import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } from 'discord.js';
|
import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } from 'discord.js';
|
||||||
@ -846,7 +847,7 @@ export const commands = [
|
|||||||
await interaction.reply({ content: `Category '${name}' created.`, ephemeral: true });
|
await interaction.reply({ content: `Category '${name}' created.`, ephemeral: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
client.logger.error(`Error in addcategory: ${err.message}`);
|
client.logger.error(`Error in addcategory: ${err.message}`);
|
||||||
await interaction.reply({ content: 'Failed to create category.', ephemeral: true });
|
await interaction.reply({ content: 'Failed to create category.', flags: MessageFlags.Ephemeral});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -875,7 +876,7 @@ export const commands = [
|
|||||||
await interaction.reply({ content: `Category '${name}' removed.`, ephemeral: true });
|
await interaction.reply({ content: `Category '${name}' removed.`, ephemeral: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
client.logger.error(`Error in removecategory: ${err.message}`);
|
client.logger.error(`Error in removecategory: ${err.message}`);
|
||||||
await interaction.reply({ content: 'Failed to remove category.', ephemeral: true });
|
await interaction.reply({ content: 'Failed to remove category.', flags: MessageFlags.Ephemeral});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
90
_src/ansiColors.js
Normal file
90
_src/ansiColors.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// ANSI Colors helper - provides nested [tag]…[/] parsing and code-block wrapping.
|
||||||
|
|
||||||
|
// ANSI color/style codes
|
||||||
|
const CODES = {
|
||||||
|
// text colors
|
||||||
|
gray: 30, red: 31, green: 32, yellow: 33,
|
||||||
|
blue: 34, pink: 35, cyan: 36, white: 37,
|
||||||
|
// background colors
|
||||||
|
bgGray: 40, bgOrange: 41, bgBlue: 42,
|
||||||
|
bgTurquoise: 43, bgFirefly: 44, bgIndigo: 45,
|
||||||
|
bgLightGray: 46, bgWhite: 47,
|
||||||
|
// styles
|
||||||
|
bold: 1, underline: 4,
|
||||||
|
// reset
|
||||||
|
reset: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape literal brackets so users can write \[ and \] without triggering tags.
|
||||||
|
*/
|
||||||
|
export function escapeBrackets(str) {
|
||||||
|
return str
|
||||||
|
.replace(/\\\[/g, '__ESC_LB__')
|
||||||
|
.replace(/\\\]/g, '__ESC_RB__');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Restore any escaped brackets after formatting. */
|
||||||
|
export function restoreBrackets(str) {
|
||||||
|
return str
|
||||||
|
.replace(/__ESC_LB__/g, '[')
|
||||||
|
.replace(/__ESC_RB__/g, ']');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse nested [tag1,tag2]…[/] patterns into ANSI codes (stack-based).
|
||||||
|
*/
|
||||||
|
export function formatAnsi(input) {
|
||||||
|
const stack = [];
|
||||||
|
let output = '';
|
||||||
|
const pattern = /\[\/\]|\[([^\]]+)\]/g;
|
||||||
|
let lastIndex = 0;
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = pattern.exec(input)) !== null) {
|
||||||
|
output += input.slice(lastIndex, match.index);
|
||||||
|
|
||||||
|
if (match[0] === '[/]') {
|
||||||
|
if (stack.length) stack.pop();
|
||||||
|
output += `\u001b[${CODES.reset}m`;
|
||||||
|
for (const tag of stack) {
|
||||||
|
const code = CODES[tag] ?? CODES.gray;
|
||||||
|
output += `\u001b[${code}m`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const tags = match[1].split(/[,;\s]+/).filter(Boolean);
|
||||||
|
for (const tag of tags) {
|
||||||
|
stack.push(tag);
|
||||||
|
const code = CODES[tag] ?? CODES.gray;
|
||||||
|
output += `\u001b[${code}m`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndex = pattern.lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
output += input.slice(lastIndex);
|
||||||
|
if (stack.length) output += `\u001b[${CODES.reset}m`;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template-tag: ansi`[red]…[/] text`
|
||||||
|
* Escapes brackets, parses ANSI, and restores literals.
|
||||||
|
*/
|
||||||
|
export function ansi(strings, ...values) {
|
||||||
|
let built = '';
|
||||||
|
for (let i = 0; i < strings.length; i++) {
|
||||||
|
built += strings[i];
|
||||||
|
if (i < values.length) built += values[i];
|
||||||
|
}
|
||||||
|
return restoreBrackets(formatAnsi(escapeBrackets(built)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wrap text in a ```ansi code block for Discord. */
|
||||||
|
export function wrapAnsi(text) {
|
||||||
|
return '```ansi\n' + text + '\n```';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export raw codes for advanced use (e.g., ansitheme module)
|
||||||
|
export { CODES };
|
||||||
@ -66,6 +66,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
modules: [
|
modules: [
|
||||||
|
'ansi',
|
||||||
'botUtils',
|
'botUtils',
|
||||||
'pbUtils',
|
'pbUtils',
|
||||||
'gitUtils',
|
'gitUtils',
|
||||||
@ -194,6 +195,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
modules: [
|
modules: [
|
||||||
|
'ansi',
|
||||||
'botUtils',
|
'botUtils',
|
||||||
'pbUtils',
|
'pbUtils',
|
||||||
'gitUtils',
|
'gitUtils',
|
||||||
|
|||||||
10
index.js
10
index.js
@ -3,6 +3,8 @@ import { createLogger } from './_src/logger.js';
|
|||||||
import { initializePocketbase } from './_src/pocketbase.js';
|
import { initializePocketbase } from './_src/pocketbase.js';
|
||||||
import { loadModules } from './_src/loader.js';
|
import { loadModules } from './_src/loader.js';
|
||||||
import config from './config.js';
|
import config from './config.js';
|
||||||
|
// For deprecated ephemeral option: convert to flags
|
||||||
|
import { ansi, wrapAnsi } from './_src/ansiColors.js';
|
||||||
|
|
||||||
// Initialize Discord client
|
// Initialize Discord client
|
||||||
const initializeClient = async (clientConfig) => {
|
const initializeClient = async (clientConfig) => {
|
||||||
@ -27,8 +29,11 @@ const initializeClient = async (clientConfig) => {
|
|||||||
// Set up Pocketbase
|
// Set up Pocketbase
|
||||||
client.pb = await initializePocketbase(clientConfig, client.logger);
|
client.pb = await initializePocketbase(clientConfig, client.logger);
|
||||||
|
|
||||||
// Commands collection
|
// Commands collection
|
||||||
client.commands = new Collection();
|
client.commands = new Collection();
|
||||||
|
// ANSI helper attached to client
|
||||||
|
client.ansi = ansi;
|
||||||
|
client.wrapAnsi = wrapAnsi;
|
||||||
|
|
||||||
// Load optional modules
|
// Load optional modules
|
||||||
await loadModules(clientConfig, client);
|
await loadModules(clientConfig, client);
|
||||||
@ -39,6 +44,7 @@ const initializeClient = async (clientConfig) => {
|
|||||||
client.on('interactionCreate', async (interaction) => {
|
client.on('interactionCreate', async (interaction) => {
|
||||||
if (!interaction.isChatInputCommand()) return;
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
|
|
||||||
const commandName = interaction.commandName;
|
const commandName = interaction.commandName;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user