From fef546b23f6bf2217cd596cc2ddbe4a4789aca23 Mon Sep 17 00:00:00 2001 From: koneko <67551503+koneko@users.noreply.github.com> Date: Tue, 29 Apr 2025 00:41:55 +0200 Subject: [PATCH] management --- .gitea/workflows/deploy.yml | 35 +++++++ Dockerfile | 26 ++++++ commands/howlucky.js | 43 +++++++++ commands/howmanyruns.js | 65 +++++++++++++ deploy.js | 49 ++++++++++ docker-compose.yml | 11 +++ index.js | 57 ++++++++++++ package.json | 9 ++ yarn.lock | 178 ++++++++++++++++++++++++++++++++++++ 9 files changed, 473 insertions(+) create mode 100644 .gitea/workflows/deploy.yml create mode 100644 Dockerfile create mode 100644 commands/howlucky.js create mode 100644 commands/howmanyruns.js create mode 100644 deploy.js create mode 100644 docker-compose.yml create mode 100644 index.js create mode 100644 package.json create mode 100644 yarn.lock diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..1a23791 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,35 @@ +name: Deploy bot + +on: + push: + branches: + - main + +jobs: + build-and-deploy: + env: + TOKEN: ${{ secrets.TOKEN }} + GUILDID: ${{ secrets.GUILDID }} + CLIENTID: ${{ secrets.CLIENTID }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + + # use this as template if you need to build + #- name: Build project + # run: | + # echo "Running build..." + # ./build.sh + + - name: Store artifact + run: | + echo "Storing artifact..." + mkdir -p /opt/artifacts/koneko-static-site + cp -r . /opt/artifacts/koneko-static-site/ + + - name: Deploy with Docker Compose + run: | + echo "Deploying with docker-compose..." + cd /opt/artifacts/koneko-static-site + docker compose down + docker compose up -d --build diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..690b07d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# syntax=docker/dockerfile:1 + +# Comments are provided throughout this file to help you get started. +# If you need more help, visit the Dockerfile reference guide at +# https://docs.docker.com/go/dockerfile-reference/ + +# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 + +ARG NODE_VERSION=18.0.0 + +FROM node:${NODE_VERSION}-alpine + +WORKDIR /usr/src/app + +COPY package.json . +# Download dependencies. +RUN npm install + +# Run the application as a non-root user. +USER node + +# Copy the rest of the source files into the image. +COPY . . + +# Run the application. +CMD node index.js diff --git a/commands/howlucky.js b/commands/howlucky.js new file mode 100644 index 0000000..abecfbf --- /dev/null +++ b/commands/howlucky.js @@ -0,0 +1,43 @@ +const { SlashCommandBuilder, SlashCommandNumberOption } = require("discord.js"); + +let tries = (module.exports = { + data: new SlashCommandBuilder() + .setName("howlucky") + .setDescription("How lucky are you?") + .addNumberOption( + new SlashCommandNumberOption() + .setName("tries") + .setDescription("how many tries you have") + .setRequired(true) + .setMinValue(1) + ) + .addStringOption((option) => + option + .setName("probability") + .setDescription("you can guess") + .setRequired(true) + ), + async execute(interaction) { + let probabilitylowernum; + if (interaction.options.getString("probability").split("/")[1] == null) { + probabilitylowernum = parseInt( + interaction.options.getString("probability") + ); + } else { + probabilitylowernum = parseInt( + interaction.options.getString("probability").split("/")[1] + ); + } + let result = + (interaction.options.getNumber("tries") / probabilitylowernum) * 100; + await interaction.reply( + `With a drop chance of ${interaction.options.getString( + "probability" + )}, getting a drop within ${interaction.options.getNumber( + "tries" + )} tries has a **${ + Math.round(result * 100) / 100 + }%** chance of happening.` + ); + }, +}); diff --git a/commands/howmanyruns.js b/commands/howmanyruns.js new file mode 100644 index 0000000..425f04d --- /dev/null +++ b/commands/howmanyruns.js @@ -0,0 +1,65 @@ +const { SlashCommandBuilder, SlashCommandNumberOption } = require("discord.js"); + +function calculate(desiredPercent, probability, itemsPerRun, minutesPerRun) { + let result = []; + //const numberOfRuns = Math.round( + // Math.log(1 - desiredPercent) / Math.log(1 - probability) + //); + const numberOfItems = Math.log(1 - desiredPercent) / Math.log(1 - probability); + const numberOfRuns = numberOfItems / itemsPerRun; + const hoursToGetPercent = (numberOfRuns * minutesPerRun) / 60; + result.push(`${Math.round(desiredPercent * 100)}%`.padEnd(7, " ")); // prob + result.push(`${Math.ceil(numberOfItems)}`.padEnd(7, " ")); // items + result.push(`${Math.ceil(numberOfRuns)}`.padEnd(7, " ")); // runs + result.push(`${Math.round(hoursToGetPercent)}`.padEnd(7, " ")); // hours + return result.join("\t") + "\n"; +} + +let tries = (module.exports = { + data: new SlashCommandBuilder() + .setName("howmanyruns") + .setDescription("How lucky are you?") + .addStringOption((option) => + option + .setName("probability") + .setDescription("you can guess") + .setRequired(true) + ) + .addNumberOption( + new SlashCommandNumberOption() + .setName("items-per-run") + .setDescription("how many items every run") + .setRequired(true) + .setMinValue(1) + ) + .addNumberOption((option) => + option + .setName("minutes-per-run") + .setDescription("how long each run takes") + .setRequired(true) + .setMinValue(1) + ), + async execute(interaction) { + let strprobability; + if (interaction.options.getString("probability").split("/")[1] == null) { + strprobability = parseInt(interaction.options.getString("probability")); + } else { + strprobability = parseInt( + interaction.options.getString("probability").split("/")[1] + ); + } + const items = interaction.options.getNumber("items-per-run"); + const probability = 1 / strprobability; + const minutes = interaction.options.getNumber("minutes-per-run"); + let result = ""; + result += calculate(0.1, probability, items, minutes); + result += calculate(0.25, probability, items, minutes); + result += calculate(0.5, probability, items, minutes); + result += calculate(0.75, probability, items, minutes); + result += calculate(0.9, probability, items, minutes); + result += calculate(0.95, probability, items, minutes); + await interaction.reply( + `Assuming you get ${items} item(s) per run.\nYou spend ${minutes} minute(s) per run and probability is 1/${strprobability}.\n\`\`\`${"Prob".padEnd(7, " ")}\t${"Items".padEnd(7, " ")}\t${"Runs".padEnd(7, " ")}\tHours\n${result}\`\`\`` + ); + }, +}); diff --git a/deploy.js b/deploy.js new file mode 100644 index 0000000..d205f26 --- /dev/null +++ b/deploy.js @@ -0,0 +1,49 @@ +// const { REST, Routes } = require("discord.js"); +// const { clientId, guildId, token } = require("./config.json"); +// // const clientId +// const fs = require("node:fs"); +// const path = require("node:path"); + +// const commands = []; +// const commandsPath = path.join(__dirname, "commands"); +// fs.readdirSync(commandsPath).forEach((file) => { +// if (file.endsWith(".js")) { +// const filePath = path.join(commandsPath, file); +// const command = require(filePath); +// if ("data" in command && "execute" in command) { +// console.log(command.data); +// commands.push(command.data.toJSON()); +// } else { +// console.log( +// `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.` +// ); +// } +// } +// }); + +// // Construct and prepare an instance of the REST module +// const rest = new REST().setToken(token); + +// // and deploy your commands! +// (async () => { +// try { +// console.log( +// `Started refreshing ${commands.length} application (/) commands.` +// ); + +// // The put method is used to fully refresh all commands in the guild with the current set +// const data = await rest.put( +// Routes.applicationGuildCommands(clientId, guildId), +// { +// body: commands, +// } +// ); + +// console.log( +// `Successfully reloaded ${data.length} application (/) commands.` +// ); +// } catch (error) { +// // And of course, make sure you catch and log any errors! +// console.error(error); +// } +// })(); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e3a38b5 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +services: + shiroprob: + container_name: shiroprob-discord-bot + restart: unless-stopped + build: + context: . + dockerfile: Dockerfile + environment: + - TOKEN=${TOKEN} + - GUILDID=${GUILDID} + - CLIENTID=${CLIENTID} diff --git a/index.js b/index.js new file mode 100644 index 0000000..505c0f7 --- /dev/null +++ b/index.js @@ -0,0 +1,57 @@ +const fs = require("node:fs"); +const path = require("node:path"); +const { Client, Collection, Events, GatewayIntentBits } = require("discord.js"); +const token = process.env.TOKEN; + +const client = new Client({ intents: [GatewayIntentBits.Guilds] }); + +client.commands = new Collection(); +const commandsPath = path.join(__dirname, "commands"); +fs.readdirSync(commandsPath).forEach((file) => { + if (file.endsWith(".js")) { + const filePath = path.join(commandsPath, file); + const command = require(filePath); + if ("data" in command && "execute" in command) { + client.commands.set(command.data.name, command); + } else { + console.log( + `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.` + ); + } + } +}); +client.once(Events.ClientReady, (readyClient) => { + console.log(`Ready! Logged in as ${readyClient.user.tag}`); +}); + +client.on(Events.InteractionCreate, async (interaction) => { + if (!interaction.isChatInputCommand()) return; + const command = interaction.client.commands.get(interaction.commandName); + + if (!command) { + console.error( + `No command matching ${interaction.commandName} was found.` + ); + return; + } + + try { + await command.execute(interaction); + } catch (error) { + console.error(error); + if (interaction.replied || interaction.deferred) { + await interaction.followUp({ + content: + "There was an error while executing this command! contact shiro or something", + ephemeral: true, + }); + } else { + await interaction.reply({ + content: + "There was an error while executing this command! contact shiro or something", + ephemeral: true, + }); + } + } +}); +client.login(token); diff --git a/package.json b/package.json new file mode 100644 index 0000000..867ab27 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "shiroprobability", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "discord.js": "^14.15.3" + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..71e7266 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,178 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@discordjs/builders@^1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.8.2.tgz#535d970331ee40f20dec9ef8079e43092f323ce9" + integrity sha512-6wvG3QaCjtMu0xnle4SoOIeFB4y6fKMN6WZfy3BMKJdQQtPLik8KGzDwBVL/+wTtcE/ZlFjgEk74GublyEVZ7g== + dependencies: + "@discordjs/formatters" "^0.4.0" + "@discordjs/util" "^1.1.0" + "@sapphire/shapeshift" "^3.9.7" + discord-api-types "0.37.83" + fast-deep-equal "^3.1.3" + ts-mixer "^6.0.4" + tslib "^2.6.2" + +"@discordjs/collection@1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.3.tgz#5a1250159ebfff9efa4f963cfa7e97f1b291be18" + integrity sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ== + +"@discordjs/collection@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-2.1.0.tgz#f327d944ab2dcf9a1f674470a481f78a120a5e3b" + integrity sha512-mLcTACtXUuVgutoznkh6hS3UFqYirDYAg5Dc1m8xn6OvPjetnUlf/xjtqnnc47OwWdaoCQnHmHh9KofhD6uRqw== + +"@discordjs/formatters@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.4.0.tgz#066a2c2163b26ac066e6f621f17445be9690c6a9" + integrity sha512-fJ06TLC1NiruF35470q3Nr1bi95BdvKFAF+T5bNfZJ4bNdqZ3VZ+Ttg6SThqTxm6qumSG3choxLBHMC69WXNXQ== + dependencies: + discord-api-types "0.37.83" + +"@discordjs/rest@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-2.3.0.tgz#06d37c7fb54a9be61134b5bbb201abd760343472" + integrity sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg== + dependencies: + "@discordjs/collection" "^2.1.0" + "@discordjs/util" "^1.1.0" + "@sapphire/async-queue" "^1.5.2" + "@sapphire/snowflake" "^3.5.3" + "@vladfrangu/async_event_emitter" "^2.2.4" + discord-api-types "0.37.83" + magic-bytes.js "^1.10.0" + tslib "^2.6.2" + undici "6.13.0" + +"@discordjs/util@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-1.1.0.tgz#dcffd2b61aab8eadd66bea67811bc34fc769bb2a" + integrity sha512-IndcI5hzlNZ7GS96RV3Xw1R2kaDuXEp7tRIy/KlhidpN/BQ1qh1NZt3377dMLTa44xDUNKT7hnXkA/oUAzD/lg== + +"@discordjs/ws@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-1.1.1.tgz#bffbfd46838258ab09054ed98ddef1a36f6507a3" + integrity sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA== + dependencies: + "@discordjs/collection" "^2.1.0" + "@discordjs/rest" "^2.3.0" + "@discordjs/util" "^1.1.0" + "@sapphire/async-queue" "^1.5.2" + "@types/ws" "^8.5.10" + "@vladfrangu/async_event_emitter" "^2.2.4" + discord-api-types "0.37.83" + tslib "^2.6.2" + ws "^8.16.0" + +"@sapphire/async-queue@^1.5.2": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.3.tgz#03cd2a2f3665068f314736bdc56eee2025352422" + integrity sha512-x7zadcfJGxFka1Q3f8gCts1F0xMwCKbZweM85xECGI0hBTeIZJGGCrHgLggihBoprlQ/hBmDR5LKfIPqnmHM3w== + +"@sapphire/shapeshift@^3.9.7": + version "3.9.7" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.7.tgz#43e23243cac8a0c046bf1e73baf3dbf407d33a0c" + integrity sha512-4It2mxPSr4OGn4HSQWGmhFMsNFGfFVhWeRPCRwbH972Ek2pzfGRZtb0pJ4Ze6oIzcyh2jw7nUDa6qGlWofgd9g== + dependencies: + fast-deep-equal "^3.1.3" + lodash "^4.17.21" + +"@sapphire/snowflake@3.5.3", "@sapphire/snowflake@^3.5.3": + version "3.5.3" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.3.tgz#0c102aa2ec5b34f806e9bc8625fc6a5e1d0a0c6a" + integrity sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ== + +"@types/node@*": + version "22.3.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.3.0.tgz#7f8da0e2b72c27c4f9bd3cb5ef805209d04d4f9e" + integrity sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g== + dependencies: + undici-types "~6.18.2" + +"@types/ws@^8.5.10": + version "8.5.12" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" + integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== + dependencies: + "@types/node" "*" + +"@vladfrangu/async_event_emitter@^2.2.4": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.5.tgz#7bc35026fdc3398a5e1aac801edd21b28cdf4cfa" + integrity sha512-J7T3gUr3Wz0l7Ni1f9upgBZ7+J22/Q1B7dl0X6fG+fTsD+H+31DIosMHj4Um1dWQwqbcQ3oQf+YS2foYkDc9cQ== + +discord-api-types@0.37.83: + version "0.37.83" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.83.tgz#a22a799729ceded8176ea747157837ddf4708b1f" + integrity sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA== + +discord.js@^14.15.3: + version "14.15.3" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.15.3.tgz#b2a67a1a4ef192be498fb8b6784224a42906f1be" + integrity sha512-/UJDQO10VuU6wQPglA4kz2bw2ngeeSbogiIPx/TsnctfzV/tNf+q+i1HlgtX1OGpeOBpJH9erZQNO5oRM2uAtQ== + dependencies: + "@discordjs/builders" "^1.8.2" + "@discordjs/collection" "1.5.3" + "@discordjs/formatters" "^0.4.0" + "@discordjs/rest" "^2.3.0" + "@discordjs/util" "^1.1.0" + "@discordjs/ws" "^1.1.1" + "@sapphire/snowflake" "3.5.3" + discord-api-types "0.37.83" + fast-deep-equal "3.1.3" + lodash.snakecase "4.1.1" + tslib "2.6.2" + undici "6.13.0" + +fast-deep-equal@3.1.3, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +lodash.snakecase@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +magic-bytes.js@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz#c41cf4bc2f802992b05e64962411c9dd44fdef92" + integrity sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ== + +ts-mixer@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.4.tgz#1da39ceabc09d947a82140d9f09db0f84919ca28" + integrity sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA== + +tslib@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tslib@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +undici-types@~6.18.2: + version "6.18.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.18.2.tgz#8b678cf939d4fc9ec56be3c68ed69c619dee28b0" + integrity sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ== + +undici@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-6.13.0.tgz#7edbf4b7f3aac5f8a681d515151bf55cb3589d72" + integrity sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw== + +ws@^8.16.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==