Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: merge into stage #33

Merged
merged 6 commits into from
Mar 24, 2024
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 .github/workflows/deploy-docker.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish Docker Image
name: Publish Docker Image and update bot's server

on:
pull_request:
Expand Down Expand Up @@ -35,4 +35,11 @@ jobs:
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
labels: ${{ steps.meta.outputs.labels }}

- name: Update container in bot's server
if: success()
run: |
curl -X POST -H "Content-Type: application/json" -d '{"image": "alexcrav/saasbot"}' $WEBHOOK_SERVER
env:
WEBHOOK_SERVER: ${{ secrets.WEBHOOK_SERVER }}
31 changes: 31 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

136 changes: 136 additions & 0 deletions src/commands/code-interpreter/slash/RunCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import {SlashCommand} from "../../../modules/handlers/HandlerBuilders.js";
import {Attachment, EmbedBuilder, SlashCommandBuilder} from "discord.js";
import {CreateRunnerResponse, GetRunnerDetails} from "../utils/ApiTypes.js";
import {checkRunnerDetails, checkRunnerStatus, getResultEmbed, extensions} from "../utils/RunnerUtils.js";

export default new SlashCommand({
builder: new SlashCommandBuilder()
.setName("run-code")
.setDescription("Code running category")
.addSubcommand(c => c
.setName("from-file")
.setDescription("Run code from a file you upload")
.addAttachmentOption(o => o
.setName("file")
.setDescription("The file to run the code from, the language will be inferred from the file extension")
.setRequired(true)
)
.addStringOption(o => o
.setName("stdin")
.setDescription("Pass input to the code interpreter.")
)
)
.addSubcommand(c => c
.setName("from-input")
.setDescription("Run code directly from your command input")
.addStringOption(o => o
.setName("code")
.setDescription("The script to run.")
.setRequired(true)
)
.addStringOption(o => o
.setName("language")
.setDescription("The coding language the script is written on")
.setRequired(true)
.addChoices(
{name: "C | C17/clang10", value: "c"},
{name: "C++ | C++17/clang10", value: "cpp"},
{name: "Java | OpenJDK18", value: "java"},
{name: "Kotlin | 1.7.10/JRE18", value: "kotlin"},
{name: "Swift | 5.6.2", value: "swift"},
{name: "C# | Mono 6", value: "csharp"},
{name: "Go | 1.19", value: "go"},
{name: "Python 3 | 3.8.10", value: "python3"},
{name: "Ruby | 3.1.2p20", value: "ruby"},
{name: "PHP | 8.1.9 cli", value: "php"},
{name: "BASH | 5.0.17", value: "bash"},
{name: "RLang | 3.6.3", value: "r"},
{name: "JavaScript | NodeJS 16.17.0", value: "javascript"},
{name: "Visual Basic | Mono 6", value: "vb"},
{name: "BrainFuck", value: "brainfuck"},
{name: "Cobol | cobc 2.2.0", value: "cobol"},
{name: "F# | F# Interactive 4.0", value: "fsharp"},
{name: "Elixir | 1.12.3", value: "elixir"},
{name: "Rust | rustc 1.59.0", value: "rust"},
{name: "TypeScript | 4.8.2", value: "typescript"}
)
)
.addStringOption(o => o
.setName("stdin")
.setDescription("Pass input to the code interpreter.")
)
),

async handler(): Promise<void> {
let code: string;
let language: string;
const stdin: string | null = this.context.options.getString("stdin");

if (this.context.options.getSubcommand() === "from-file") {
const file: Attachment = this.context.options.getAttachment("file")!;
code = await fetch(file.url).then(f => f.text());

const splitName: string[] = file.name.split('.');

if (splitName.length < 2
|| !Object.keys(extensions).includes(splitName[splitName.length - 1])) {

const unknownLangEmbed: EmbedBuilder = new EmbedBuilder()
.setTitle("Invalid language")
.setDescription("Inferred language is not valid, valid languages include:\n\n```" +
Object.keys(extensions).map(e => `*.${e}`).join(", ") +
"```"
)
.setColor("#FF0000");

await this.context.reply({
embeds: [unknownLangEmbed],
ephemeral: true
});
return;
}

language = extensions[splitName[splitName.length - 1]];
} else {
code = this.context.options.getString("code")!;
language = this.context.options.getString("language")!;
}

await this.context.deferReply();

const createRunnerResult: CreateRunnerResponse = await fetch("http://api.paiza.io/runners/create?"
+ `source_code=${encodeURIComponent(code)}&`
+ `language=${encodeURIComponent(language)}&`
+ "api_key=guest&"
+ (stdin !== null ? `input=${encodeURIComponent(stdin)}` : ""),
{ method: "POST" }
)
.then(r => r.json());

setTimeout(async (): Promise<void> => {
const checkRunnerResult: CreateRunnerResponse = await checkRunnerStatus(createRunnerResult.id);

if (checkRunnerResult.status === "running") {
const timeoutEmbed: EmbedBuilder = new EmbedBuilder()
.setTitle("Long running session!")
.setDescription(
"Looks like this session is going to take some time," +
"but don't worry you can still use the </run-code get-output:1221193746209701920>" +
"command and check the output for this session once it's finished!"
)
.addFields(
{name: "Session ID", value: checkRunnerResult.id, inline: true},
{name: "Status", value: checkRunnerResult.status, inline: true}
)
.setColor("#FFA500");

await this.context.editReply({embeds: [timeoutEmbed]});
return;
}

const getRunnerResult: GetRunnerDetails = await checkRunnerDetails(createRunnerResult.id);

await this.context.editReply({embeds: [getResultEmbed(getRunnerResult)]});
}, 3000);
}
});
49 changes: 49 additions & 0 deletions src/commands/code-interpreter/slash/SessionResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {SlashCommand} from "../../../modules/handlers/HandlerBuilders.js";
import {EmbedBuilder, SlashCommandBuilder} from "discord.js";
import {CreateRunnerResponse} from "../utils/ApiTypes.js";
import {checkRunnerDetails, checkRunnerStatus, getResultEmbed} from "../utils/RunnerUtils.js";

export default new SlashCommand({
builder: new SlashCommandBuilder()
.setName("code-session")
.setDescription("Get data from code sessions")
.addSubcommand(c => c
.setName("get-result")
.setDescription("Get result from a long running session")
.addStringOption(o => o
.setName("id")
.setDescription("The session ID")
.setRequired(true)
)
),

async handler(): Promise<void> {
await this.context.deferReply();
const sessionId: string = this.context.options.getString("id")!;

const status: CreateRunnerResponse = await checkRunnerStatus(sessionId);

if (status.status === "running") {
const timeoutEmbed: EmbedBuilder = new EmbedBuilder()
.setTitle("On it!")
.setDescription(
"This runner is still running," +
"please, wait and run this command again in a few seconds"
)
.addFields(
{name: "Session ID", value: status.id, inline: true},
{name: "Status", value: status.status, inline: true}
)
.setColor("#FFA500")

await this.context.editReply({
embeds: [timeoutEmbed]
});
return;
}

await this.context.editReply({
embeds: [getResultEmbed(await checkRunnerDetails(sessionId))]
});
}
})
19 changes: 19 additions & 0 deletions src/commands/code-interpreter/utils/ApiTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

export interface CreateRunnerResponse {
id: string;
status: "running" | "completed";
}

export interface GetRunnerDetails {
id: string;

build_stderr: string | null;
build_stdout: string | null;
build_exit_code: number;
build_result: "success" | "failure" | "error";

stdout: string | null;
stderr: string | null;
result: "success" | "failure" | "error";
exit_code: number;
}
59 changes: 59 additions & 0 deletions src/commands/code-interpreter/utils/RunnerUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {CreateRunnerResponse, GetRunnerDetails} from "./ApiTypes.js";
import {EmbedBuilder} from "discord.js";

export const extensions: Record<string, string> = {
"c": "c",
"cpp": "cpp",
"java": "java",
"kt": "kotlin",
"swift": "swift",
"cs": "csharp",
"go": "go",
"py": "python3",
"rb": "ruby",
"php": "php",
"sh": "bash",
"r": "r",
"js": "javascript",
"vb": "vb",
"bf": "brainfuck",
"cob": "cobol",
"fs": "fsharp",
"ex": "elixir",
"rs": "rust",
"ts": "typescript"
}

export async function checkRunnerDetails(id: string): Promise<GetRunnerDetails> {
return await fetch(`http://api.paiza.io/runners/get_details?id=${encodeURIComponent(id)}&api_key=guest`)
.then(r => r.json());
}

export async function checkRunnerStatus(id: string): Promise<CreateRunnerResponse> {
return await fetch(`http://api.paiza.io/runners/get_status?id=${encodeURIComponent(id)}&api_key=guest`)
.then(r => r.json());
}

export function getResultEmbed(details: GetRunnerDetails): EmbedBuilder {
if (details.build_result !== "success" && details.build_result !== null) {
return new EmbedBuilder()
.setTitle("Build error")
.setDescription(`Session ID: \`${details.id}\`\n\`\`\`${details.build_stderr ?? details.build_stdout}\`\`\``)
.setColor("#FF0000")
.setFooter({text: `Exited with code: ${details.build_exit_code}`});
}

if (details.result !== "success") {
return new EmbedBuilder()
.setTitle("Runtime error")
.setDescription(`Session ID: \`${details.id}\`\n\`\`\`${details.stderr ?? details.stdout}\`\`\``)
.setColor("#FF0000")
.setFooter({text: `Exited with code: ${details.exit_code}`});
}

return new EmbedBuilder()
.setTitle("Success")
.setDescription(`Session ID: \`${details.id}\`\n\`\`\`${details.stdout}\`\`\``)
.setColor("#00B000")
.setFooter({text: `Exited with code: ${details.exit_code}`});
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default new SlashCommand({
}

const totalMembers = guild.memberCount
await guild.members.fetch();
const totalUsers = guild.members.cache.filter(member => !member.user.bot).size
const totalBots = guild.members.cache.filter(member => member.user.bot).size

Expand Down Expand Up @@ -80,6 +81,6 @@ export default new SlashCommand({
]
})

await this.context.reply(`Canal de estadísticas creado en la categoría ${category}`)
await this.context.reply(`Canal de estadísticas creado correctamente`)
}
});
File renamed without changes.
Loading
Loading