Skip to content

Commit 5426f42

Browse files
committed
feat: Support /eval on Discord
1 parent d6d0cea commit 5426f42

11 files changed

Lines changed: 56 additions & 24 deletions

File tree

src/cache/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { TranslatedText } from '@/i18n/types';
33
import type { Games } from '@/ps/games';
44
import type { CommonGame } from '@/ps/games/game';
55
import type { PSCronJobManager } from '@/ps/handlers/cron';
6-
import type { DiscCommand, PSCommand } from '@/types/chat';
6+
import type { DiscordCommand, PSCommand } from '@/types/chat';
77
import type { Perms } from '@/types/perms';
88
import type { PSRoomConfig } from '@/types/ps';
99
import type { Timer } from '@/utils/timer';
@@ -34,5 +34,5 @@ export const PSPointsNonce: Partial<{ [key: string]: Record<string, Record<strin
3434
export const PSGames: { [key in keyof Games]?: Record<string, CommonGame> } = {};
3535

3636
// Discord
37-
export const DiscCommands: { [key: string]: DiscCommand & { path: string; isAlias?: boolean; slash: SlashCommandBuilder } } = {};
37+
export const DiscCommands: { [key: string]: DiscordCommand & { path: string; isAlias?: boolean; slash: SlashCommandBuilder } } = {};
3838
export const DiscGames: { judgement: Record<string, JudgementGame> } = { judgement: {} };

src/discord/commands/bonk.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { DiscCommand } from '@/types/chat';
1+
import type { DiscordCommand } from '@/types/chat';
22

3-
export const command: DiscCommand = {
3+
export const command: DiscordCommand = {
44
name: 'bonk',
55
desc: 'Bonks!',
66
servers: ['226909807548825600', '776284091813068830', '719076445699440700'],

src/discord/commands/eval.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { evaluate } from '@/eval';
2+
import { i18n } from '@/i18n';
3+
4+
import type { DiscordCommand } from '@/types/chat';
5+
6+
// With a 100-character buffer
7+
const DISCORD_MAX_MSG_LENGTH = 1900;
8+
9+
export const command: DiscordCommand = {
10+
name: 'eval',
11+
aliases: ['run', 'exec'],
12+
desc: 'Evaluates code. Only for the administrator of the Bot.',
13+
perms: 'admin',
14+
args: slash => slash.addStringOption(builder => builder.setName('code').setDescription('The code to evaluate.').setRequired(true)),
15+
async run(message) {
16+
const $T = i18n();
17+
18+
const code = message.options.getString('code')!;
19+
const shortOutput = message.commandName === 'exec';
20+
const res = await evaluate(code, shortOutput ? 'ABBR_OUTPUT' : 'COLOR_OUTPUT_ANSI', { message, context: null });
21+
if (shortOutput) {
22+
message.reply(res.success ? $T('COMMANDS.EVAL.SUCCESS') : $T('COMMANDS.EVAL.ERROR', { error: res.output }));
23+
} else {
24+
const output = res.output.length > DISCORD_MAX_MSG_LENGTH ? res.output.slice(0, DISCORD_MAX_MSG_LENGTH) + ' ...' : res.output;
25+
message.reply({ content: `\`\`\`ansi\n${output}\n\`\`\``, ephemeral: message.commandName !== 'run' });
26+
}
27+
},
28+
};

src/discord/commands/godb.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { updatePokemonGOCache } from '@/cache/pokemonGo';
22
import { BOT_CHANNEL_ID, SERVER_ID } from '@/discord/constants/servers/pokemongo';
33

4-
import type { DiscCommand } from '@/types/chat';
4+
import type { DiscordCommand } from '@/types/chat';
55

6-
export const command: DiscCommand = {
6+
export const command: DiscordCommand = {
77
name: 'godb',
88
desc: 'Updates the Pokémon GO database on PartBot.',
99
servers: [SERVER_ID],

src/discord/commands/judgement.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { clientId } from '@/config/discord';
55
import { ChatError } from '@/utils/chatError';
66

77
import type { NoTranslate } from '@/i18n/types';
8-
import type { DiscCommand } from '@/types/chat';
8+
import type { DiscordCommand } from '@/types/chat';
99

1010
type History = {
1111
round: number;
@@ -35,7 +35,7 @@ const Judgement = DiscGames.judgement;
3535

3636
const userMenu = new UserSelectMenuBuilder().setCustomId('judgment_user_selector').setMinValues(4).setMaxValues(5);
3737

38-
export const command: DiscCommand[] = [
38+
export const command: DiscordCommand[] = [
3939
{
4040
name: 'judgement',
4141
desc: 'Creates a game',

src/discord/commands/parselb.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import { toId } from '@/tools';
66
import { ChatError } from '@/utils/chatError';
77

88
import type { NoTranslate } from '@/i18n/types';
9-
import type { DiscCommand } from '@/types/chat';
9+
import type { DiscordCommand } from '@/types/chat';
1010

1111
const ROOM = 'hindi';
1212

13-
export const command: DiscCommand = {
13+
export const command: DiscordCommand = {
1414
name: 'parselb',
1515
desc: 'Updates tour points for Hindi via JSON.',
1616
servers: [SERVER_ID],

src/discord/commands/test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { DiscCommand } from '@/types/chat';
1+
import type { DiscordCommand } from '@/types/chat';
22

3-
export const command: DiscCommand = {
3+
export const command: DiscordCommand = {
44
name: 'test',
55
desc: 'Test command',
66
run(interaction) {

src/discord/loaders/commands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { cachebust } from '@/utils/cachebust';
88
import { fsPath } from '@/utils/fsPath';
99
import { Logger } from '@/utils/logger';
1010

11-
import type { DiscCommand } from '@/types/chat';
11+
import type { DiscordCommand } from '@/types/chat';
1212

1313
const restClient = new REST().setToken(token);
1414

@@ -41,7 +41,7 @@ export async function loadCommands(): Promise<void> {
4141
await Promise.all(
4242
commands.map(async commandFile => {
4343
const requirePath = fsPath('discord', 'commands', commandFile);
44-
const { command: commandEntries }: { command: DiscCommand | DiscCommand[] } = await import(requirePath);
44+
const { command: commandEntries }: { command: DiscordCommand | DiscordCommand[] } = await import(requirePath);
4545
if (!commandEntries) return;
4646
const commands = Array.isArray(commandEntries) ? commandEntries : [commandEntries];
4747
commands.forEach(command =>

src/eval.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { Logger } from '@/utils/logger';
1717

1818
import type { PSCommandContext } from '@/types/chat';
1919
import type { PSMessage } from '@/types/ps';
20+
import type { Interaction } from 'discord.js';
2021

2122
// Exporting into side variables for eval lookup; this gets garbage-collected otherwise
2223
const cache = _cache;
@@ -37,20 +38,21 @@ const E: Record<string, unknown> = {};
3738
// Storing in context for eval()
3839
const _evalContext = [cache, cachebust, fs, fsSync, fsPath, path, Tools, $, Sentinel, E, jsxToHTML, paste];
3940

40-
export type EvalModes = 'COLOR_OUTPUT' | 'FULL_OUTPUT' | 'ABBR_OUTPUT' | 'NO_OUTPUT';
41+
export type EvalModes = 'COLOR_OUTPUT_HTML' | 'COLOR_OUTPUT_ANSI' | 'FULL_OUTPUT' | 'ABBR_OUTPUT' | 'NO_OUTPUT';
4142
export type EvalOutput = {
4243
success: boolean;
4344
output: string;
4445
};
4546

4647
export function formatValue(value: unknown, mode: EvalModes): string {
4748
switch (mode) {
48-
case 'COLOR_OUTPUT':
49+
case 'COLOR_OUTPUT_ANSI':
50+
case 'COLOR_OUTPUT_HTML':
4951
case 'FULL_OUTPUT': {
50-
const color = mode === 'COLOR_OUTPUT';
52+
const color = mode === 'COLOR_OUTPUT_HTML' || mode === 'COLOR_OUTPUT_ANSI';
5153
// TODO Stringify functions and render with syntax highlighting
5254
const inspection = inspect(value, { depth: 2, colors: color, numericSeparator: true });
53-
return color
55+
return mode === 'COLOR_OUTPUT_HTML'
5456
? ansiToHtml(inspection)
5557
.replace(/\t/g, '&nbsp;'.repeat(4)) // Fill out tabs
5658
.replace(/ (?= |$)/g, '&nbsp;') // Fill out multi-spaces
@@ -104,10 +106,12 @@ export function formatValue(value: unknown, mode: EvalModes): string {
104106
export async function evaluate(
105107
code: string,
106108
mode: EvalModes,
107-
passedContext: {
108-
message: PSMessage;
109-
context: PSCommandContext;
110-
} // Add Discord case here, eventually
109+
passedContext:
110+
| {
111+
message: PSMessage;
112+
context: PSCommandContext;
113+
}
114+
| { message: Interaction; context: null }
111115
): Promise<EvalOutput> {
112116
let success: boolean, value: unknown;
113117
try {

src/ps/commands/eval.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const command: PSCommand = {
1818
originalCommand: [originalCommand],
1919
$T,
2020
} = context;
21-
const res = await evaluate(arg, originalCommand === 'exec' ? 'ABBR_OUTPUT' : 'COLOR_OUTPUT', { message, context });
21+
const res = await evaluate(arg, originalCommand === 'exec' ? 'ABBR_OUTPUT' : 'COLOR_OUTPUT_HTML', { message, context });
2222
if (originalCommand !== 'exec') {
2323
let outputHTML = res.output;
2424
if (outputHTML.length > MAX_CHAT_HTML_LENGTH) {

0 commit comments

Comments
 (0)