Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions apps/discord-bot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,27 @@
},
"scripts": {
"check": "tsc --noEmit",
"prebuild": "sayable extract && sayable compile",
"build": "pnpm tsup"
},
"dependencies": {
"@buape/carbon": "^0.9.0",
"@buape/carbon": "^0.12.0",
"@evaluate/execute": "workspace:^",
"@evaluate/logger": "workspace:^",
"@evaluate/runtimes": "workspace:^",
"@sayable/carbon": "0.0.0-alpha.4",
"@sayable/react": "0.0.0-alpha.6",
"@t3-oss/env-core": "^0.13.8",
"date-fns": "^4.1.0",
"es-toolkit": "^1.39.7",
"posthog-node": "^5.5.1",
"sayable": "0.0.0-alpha.6",
"zod": "^4.0.5"
},
"devDependencies": {
"tsup": "^8.5.0"
"@sayable/config": "0.0.0",
"@sayable/format-po": "0.0.0",
"tsup": "^8.5.0",
"unplugin-sayable": "0.0.0-alpha.4"
}
}
12 changes: 12 additions & 0 deletions apps/discord-bot/sayable.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from '@sayable/config';

export default defineConfig({
sourceLocale: 'en',
locales: ['en'],
catalogues: [
{
include: ['src/**/*.ts'],
output: 'src/locales/{locale}/messages.{extension}',
},
],
});
89 changes: 48 additions & 41 deletions apps/discord-bot/src/commands/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,49 @@ import {
type AutocompleteInteraction,
Command,
type CommandInteraction,
type CommandOptions,
} from '@buape/carbon';
import { fetchAllRuntimes, searchForRuntimes } from '@evaluate/runtimes';
import { sayable } from '@sayable/carbon';
import type { Sayable } from 'sayable';
import { EvaluateModal } from '~/components/evaluate-modal';
import { handleEvaluating } from '~/handlers/evaluate';
import { getInteractionContext } from '~/helpers/session-context';
import { captureEvent } from '~/services/posthog';

export class EvaluateCommand extends Command {
name = 'evaluate';
description =
'Evaluate any piece of code in any runtime with optional input and command line arguments.';
options: CommandOptions = [
{
type: ApplicationCommandOptionType.String,
name: 'runtime',
description: 'The runtime in which the code is written.',
required: false,
autocomplete: true,
},
{
type: ApplicationCommandOptionType.String,
name: 'code',
description: 'The source code to evaluate.',
required: false,
},
{
type: ApplicationCommandOptionType.String,
name: 'input',
description: 'The STDIN input to provide to the program.',
required: false,
},
{
type: ApplicationCommandOptionType.String,
name: 'arguments',
description: 'Additional command line arguments to pass to the program.',
required: false,
},
];
export class EvaluateCommand extends sayable(Command) {
constructor(say: Sayable) {
super(say, (say) => ({
name: say`evaluate`,
description: say`Evaluate any piece of code in any runtime with optional input and command line arguments.`,
options: [
{
type: ApplicationCommandOptionType.String,
name: say`runtime`,
description: say`The runtime in which the code is written.`,
required: false,
autocomplete: true,
},
{
type: ApplicationCommandOptionType.String,
name: say`code`,
description: say`The source code to evaluate.`,
required: false,
},
{
type: ApplicationCommandOptionType.String,
name: say`input`,
description: say`The STDIN input to provide to the program.`,
required: false,
},
{
type: ApplicationCommandOptionType.String,
name: say`arguments`,
description: say`Additional command line arguments to pass to the program.`,
required: false,
},
],
}));
}

async autocomplete(interaction: AutocompleteInteraction) {
const runtime = interaction.options.getString('runtime');
Expand All @@ -59,21 +63,24 @@ export class EvaluateCommand extends Command {
}
}

async run(use: CommandInteraction) {
captureEvent(getInteractionContext(use.rawData), 'used_command', {
async run(interaction: CommandInteraction) {
captureEvent(getInteractionContext(interaction.rawData), 'used_command', {
command_name: this.name,
});

const runtime = use.options.getString('runtime');
const code = use.options.getString('code');
const input = use.options.getString('input');
const args = use.options.getString('arguments');
const runtime = interaction.options.getString('runtime');
const code = interaction.options.getString('code');
const input = interaction.options.getString('input');
const args = interaction.options.getString('arguments');

if (runtime && code) {
return handleEvaluating(use, runtime, { code, args, input });
return handleEvaluating(interaction, runtime, { code, args, input });
} else {
const modal = new EvaluateModal({ runtime, code, args, input });
return use.showModal(modal);
const modal = new EvaluateModal(
interaction.say, //
{ runtime, code, args, input },
);
return interaction.showModal(modal);
}
}
}
22 changes: 15 additions & 7 deletions apps/discord-bot/src/components/edit-evaluation-button.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
import { Button, type ButtonInteraction, ButtonStyle } from '@buape/carbon';
import { sayable } from '@sayable/carbon';
import type { Sayable } from 'sayable';
import { getEvaluateOptions } from '~/helpers/evaluate-helpers';
import { resolveEmoji } from '~/helpers/resolve-emoji';
import { getInteractionContext } from '~/helpers/session-context';
import { captureEvent } from '~/services/posthog';
import { EvaluateModalEdit } from './evaluate-modal';

export class EditEvaluationButton extends Button {
export class EditEvaluationButton extends sayable(Button) {
customId = 'evaluate,edit';
constructor(say: Sayable) {
super({
label: say`Edit`,
});
}
style = ButtonStyle.Success;
label = 'Edit';
emoji = resolveEmoji('pencil', true);

async run(click: ButtonInteraction) {
captureEvent(getInteractionContext(click.rawData), 'clicked_button', {
async run(interaction: ButtonInteraction) {
captureEvent(getInteractionContext(interaction.rawData), 'clicked_button', {
button_id: this.customId,
});

const embed = click.embeds?.[0]!;
const options = getEvaluateOptions(embed);
return click.showModal(new EvaluateModalEdit(options));
const embed = interaction.embeds?.[0];
const options = embed && getEvaluateOptions(embed);
return interaction.showModal(
new EvaluateModalEdit(interaction.say, options),
);
}
}
95 changes: 64 additions & 31 deletions apps/discord-bot/src/components/evaluate-modal.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
import {
Label,
Modal,
type ModalInteraction,
Row,
TextInput,
TextInputStyle,
} from '@buape/carbon';
import { sayable } from '@sayable/carbon';
import type { Sayable } from 'sayable';
import { handleEvaluating } from '~/handlers/evaluate';
import { getInteractionContext } from '~/helpers/session-context';
import { captureEvent } from '~/services/posthog';

export class EvaluateModal extends Modal {
title = 'Evaluate';
export class EvaluateModal extends sayable(Modal) {
customId = 'evaluate,new';

components = [
new Row([new RuntimeInput()]),
new Row([new CodeInput()]),
new Row([new ArgumentsInput()]),
new Row([new InputInput()]),
];
constructor(defaultValues: Record<string, string | undefined> = {}) {
super();

const components = this.components.flatMap((row) => row.components);
for (const component of components) {
const value = defaultValues[component.customId];
if (value) component.value = value;
}
constructor(say: Sayable, values?: Record<string, string | undefined>) {
super({
title: say`Evaluate`,
components: [
new RuntimeLabel(say, values?.runtime),
new CodeLabel(say, values?.code),
new ArgumentsLabel(say, values?.args),
new InputLabel(say, values?.input),
],
});
}

async run(submit: ModalInteraction) {
Expand All @@ -45,45 +41,82 @@ export class EvaluateModal extends Modal {

export class EvaluateModalEdit extends EvaluateModal {
customId = 'evaluate,edit';
title = 'Edit Evaluation';
constructor(say: Sayable, values?: Record<string, string | undefined>) {
super(say, values);
Reflect.set(this, 'title', say`Edit Evaluation`);
}
}

class RuntimeInput extends TextInput {
class RuntimeTextInput extends sayable(TextInput) {
customId = 'runtime';
label = 'Runtime';
placeholder = 'The runtime in which the code is written.';
constructor(say: Sayable, value?: string) {
super({ placeholder: say`The runtime in which the code is written.` });
if (value) this.value = value;
}
style = TextInputStyle.Short;
minLength = 1;
maxLength = 100;
required = true;
}

class CodeInput extends TextInput {
class RuntimeLabel extends sayable(Label) {
constructor(say: Sayable, value?: string) {
super({ label: say`Runtime` }, new RuntimeTextInput(say, value));
}
}

class CodeTextInput extends sayable(TextInput) {
customId = 'code';
label = 'Code';
placeholder = 'The source code to evaluate.';
constructor(say: Sayable, value?: string) {
super({ placeholder: say`The source code to evaluate.` });
if (value) this.value = value;
}
style = TextInputStyle.Paragraph;
minLength = 1;
maxLength = 4000;
required = true;
}

class ArgumentsInput extends TextInput {
class CodeLabel extends sayable(Label) {
constructor(say: Sayable, value?: string) {
super({ label: say`Code` }, new CodeTextInput(say, value));
}
}

class ArgumentsTextInput extends sayable(TextInput) {
customId = 'args';
label = 'Arguments';
placeholder = 'Additional command line arguments to pass to the program.';
constructor(say: Sayable, value?: string) {
super({
placeholder: say`Additional command line arguments to pass to the program.`,
});
if (value) this.value = value;
}
style = TextInputStyle.Short;
minLength = 0;
maxLength = 500;
required = false;
}

class InputInput extends TextInput {
class ArgumentsLabel extends sayable(Label) {
constructor(say: Sayable, value?: string) {
super({ label: say`CLI Arguments` }, new ArgumentsTextInput(say, value));
}
}

class InputTextInput extends sayable(TextInput) {
customId = 'input';
label = 'Input';
placeholder = 'The STDIN input to provide to the program.';
constructor(say: Sayable, value?: string) {
super({ placeholder: say`The STDIN input to provide to the program.` });
if (value) this.value = value;
}
style = TextInputStyle.Short;
minLength = 0;
maxLength = 500;
required = false;
}

class InputLabel extends sayable(Label) {
constructor(say: Sayable, value?: string) {
super({ label: say`STDIN` }, new InputTextInput(say, value));
}
}
14 changes: 7 additions & 7 deletions apps/discord-bot/src/components/open-evaluation-button.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { LinkButton } from '@buape/carbon';
import { sayable } from '@sayable/carbon';
import type { Sayable } from 'sayable';
import env from '~/env';
import { resolveEmoji } from '~/helpers/resolve-emoji';

export class OpenEvaluationButton extends LinkButton {
label = 'Open Evaluation';
export class OpenEvaluationButton extends sayable(LinkButton) {
public constructor(say: Sayable, url?: string) {
super({ label: say`Open Evaluation` });
if (url) this.url = url;
}
url = `${env.WEBSITE_URL}`;
emoji = resolveEmoji('globe', true);

public constructor(url: string) {
super();
this.url = url;
}
}
5 changes: 1 addition & 4 deletions apps/discord-bot/src/events/application-authorised.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import {
ApplicationAuthorizedListener,
type ApplicationWebhookEventType,
type ListenerEventData,
} from '@buape/carbon';
import { getUserContext } from '~/helpers/session-context';
import { captureEvent } from '~/services/posthog';

export class ApplicationAuthorisedListener extends ApplicationAuthorizedListener {
async handle(
data: ListenerEventData[ApplicationWebhookEventType.ApplicationAuthorized],
) {
async handle(data: ListenerEventData[this['type']]) {
if (data.guild)
captureEvent(getUserContext(data.user), 'installed_app', {
install_type: 'guild',
Expand Down
Loading