Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7f330fe
Begin setting up golang environment
crackheadakira Apr 20, 2025
0d7c9d4
Set up build script
crackheadakira Apr 20, 2025
f3bfbfe
Add a package.json script to build go modules
crackheadakira Apr 20, 2025
b02aa48
Add in way to call go module
crackheadakira Apr 20, 2025
006e500
Successfully pass data to go
crackheadakira Apr 20, 2025
6704fbb
Pass image buffer from go to node
crackheadakira Apr 20, 2025
171bc9d
Make image buffer fixed size to avoid size issues
crackheadakira Apr 20, 2025
4abaa59
Draws the covers in a 3x3 grid with the text
crackheadakira Apr 20, 2025
35fcbf4
Add a modules structure
crackheadakira Apr 20, 2025
1c20b58
Migrate go module over to Rust due to issues
crackheadakira Apr 21, 2025
eacdb00
Add error handling to Rust FFI
crackheadakira Apr 21, 2025
fd096a0
Remove defer from more middleware
crackheadakira Apr 21, 2025
54bdc8f
Include compiled library
crackheadakira Apr 21, 2025
5aa5889
Decrease module size
crackheadakira Apr 21, 2025
79ff5fc
Add a way to free up memory after using module
crackheadakira Apr 25, 2025
f302067
Add a test to FFI module
crackheadakira Apr 25, 2025
2e118db
Use the Zod env schema everywhere in program now
crackheadakira Apr 27, 2025
810fac7
Fetch images asynchronously in module
crackheadakira Apr 27, 2025
29dd240
Stop including the modules
crackheadakira May 9, 2025
280e9dd
Add a check to verify that FFI module exists
crackheadakira May 9, 2025
c9393ce
Update RSA class to use more static properties
crackheadakira May 10, 2025
9437af8
Update Winston logger to use more default features
crackheadakira May 10, 2025
f40bed8
Remove patches from Dockerfile
crackheadakira May 10, 2025
7e902e8
feat: implement working pixelation
crackheadakira Sep 20, 2025
6017ab7
wip: get MVP working for buttons on pixel jumble
crackheadakira Sep 20, 2025
82c2c10
wip: add forfeit to pixel jumble
crackheadakira Sep 20, 2025
aa033df
wip: add hints to pixel jumble
crackheadakira Sep 20, 2025
46ef488
feat: fully working pixel jumble game
crackheadakira Sep 21, 2025
4614094
feat: fix character name showing null in PJ
crackheadakira Sep 21, 2025
9137411
fix: fix Dockerfile failing to build
crackheadakira Sep 21, 2025
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
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
TOKEN=YOUR_TOKEN_GOES_HERE
TRUSTED_USERS=["YOUR_TRUSTED_USERS_GO_HERE"]
TRUSTED_USERS=TRUSTED_USER1,TRUSTED_USER2
RSS_LIMIT=5
ANILIST_API=https://graphql.anilist.co
CLIENT_ID=YOUR_CLIENT_ID_GOES_HERE
Expand Down
11 changes: 8 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,16 @@ dist
.idea/

# Added by Yuuko Devs
/Local
/temp
commit.hash
db.sqlite-*

src/Local


## Database
src/database/sqlite
src/database/migration
src/database/migration

## FFI Modules
src/modules/target
src/modules/compiled
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"rust-analyzer.linkedProjects": [
"src/modules/Cargo.toml"
]
}

39 changes: 25 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
#FROM oven/bun:1.1.4 as base
FROM node:20 AS base
RUN useradd -m -u 1001 -s /bin/bash bun

# install bun from here curl -fsSL https://bun.sh/install | bash
RUN curl -fsSL https://bun.sh/install | bash && \
ln -s $HOME/.bun/bin/bun /usr/local/bin/bun

WORKDIR /usr/src/Yuuko

# Stage: Install dependencies
FROM base AS install
RUN mkdir -p /temp/prod
COPY package.json bun.lock /temp/prod/
# Copy bun patches folder so we can install the patches
COPY patches /temp/prod/patches
RUN cd /temp/prod && bun install --frozen-lockfile --production
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production

# Stage: Prerelease (prepare database & dev steps)
FROM base AS prerelease
WORKDIR /usr/src/Yuuko

COPY package.json bun.lock ./

# Install dev dependencies to run drizzle-kit
RUN bun install --frozen-lockfile

COPY . .
ENV NODE_ENV=docker

# Run DB migrations if SQLite database doesn't exist
RUN if [ ! -f ./src/database/sqlite/*.sqlite ]; then bun db:push; fi

FROM base AS release
COPY --from=install /temp/prod/node_modules node_modules
WORKDIR /usr/src/Yuuko

# Copy only production node_modules
COPY --from=install /usr/src/Yuuko/node_modules node_modules

# Copy source code
COPY --from=prerelease /usr/src/Yuuko/src ./src

COPY --from=prerelease /usr/src/Yuuko/entrypoint.sh .
# Copy config and entrypoint
COPY --from=prerelease /usr/src/Yuuko/package.json .
COPY --from=prerelease /usr/src/Yuuko/tsconfig.json .
COPY --from=prerelease /usr/src/Yuuko/drizzle.config.ts ..
COPY --from=prerelease /usr/src/Yuuko/drizzle.config.ts .
COPY --from=prerelease /usr/src/Yuuko/entrypoint.sh .

EXPOSE 3030/tcp
VOLUME "/usr/src/Yuuko/database/sqlite"

VOLUME "usr/src/Yuuko/database/sqlite"

ENTRYPOINT [ "sh", "entrypoint.sh" ]
CMD bun start:prod
165 changes: 13 additions & 152 deletions bun.lock

Large diffs are not rendered by default.

18 changes: 6 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"format": "prettier --write .",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"module:build": "sh ./src/modules/build.sh",
"gen:gql": "graphql-codegen",
"gen:queries": "bun run ./src/scripts/genQueries.ts",
"db:push": "bun drizzle-kit push",
Expand All @@ -30,27 +31,23 @@
"dependencies": {
"@discordjs/rest": "^2.4.3",
"@elysiajs/cors": "^1.2.0",
"@libsql/client": "^0.14.0",
"@types/bun": "^1.2.2",
"@types/dotenv-flow": "^3.3.3",
"@types/ms": "^2.1.0",
"@types/node-rsa": "^1.1.4",
"@upstash/redis": "^1.34.4",
"bun-types": "^1.2.2",
"colors": "^1.4.0",
"discord.js": "^14.18.0",
"dotenv-flow": "^4.1.0",
"drizzle-kit": "^0.30.4",
"drizzle-orm": "^0.39.3",
"elysia": "^1.2.12",
"jimp": "^1.6.0",
"ms": "^2.1.3",
"redis": "^4.7.0",
"winston": "^3.17.0",
"zod": "^3.24.2"
},
"devDependencies": {
"@antfu/eslint-config": "^4.2.1",
"drizzle-kit": "^0.30.4",
"@types/bun": "^1.2.12",
"@types/dotenv-flow": "^3.3.3",
"@types/ms": "^2.1.0",
"@types/node-rsa": "^1.1.4",
"@graphql-codegen/cli": "^5.0.5",
"@graphql-codegen/typescript": "^4.1.3",
"@graphql-codegen/typescript-operations": "^4.4.1",
Expand All @@ -60,8 +57,5 @@
"prettier": "^3.5.1",
"prettier-eslint": "^16.3.0",
"typescript": "^5.7.3"
},
"patchedDependencies": {
"@jimp/plugin-print@1.6.0": "patches/@jimp%2Fplugin-print@1.6.0.patch"
}
}
13 changes: 0 additions & 13 deletions patches/@jimp%2Fplugin-print@1.6.0.patch

This file was deleted.

21 changes: 0 additions & 21 deletions src/api/config/index.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/api/controllers/global.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { triggerController } from "./trigger.controller";
import { publicController } from "./public.controller";
import { infoController } from "./info.controller";
import { Elysia } from "elysia";
import { env } from "#env";

export const api = new Elysia({
prefix: "/api/v1",
name: "api:root"
})
.guard({
beforeHandle({ set, headers }) {
if (!headers.authorization || !process.env.TRUSTED_USERS.includes(headers.authorization)) {
if (!headers.authorization || !env().TRUSTED_USERS.includes(headers.authorization)) {
set.status = 401;
return { message: "Unauthorized" };
}
Expand Down
6 changes: 3 additions & 3 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { api } from '#api/controllers/global.controller'
import { RSA } from '#utils/rsaEncryption';
import { cors } from '@elysiajs/cors'
import { Elysia } from 'elysia'
import { env } from "#env";

export async function startApi() {
const port = process.env.PORT;
const rsa = new RSA();
await rsa.loadKeys();
const port = env().API_PORT;
await RSA.loadKeys();

new Elysia().onError(({ error }) => {
const err = new Response(error.toString());
Expand Down
51 changes: 11 additions & 40 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,26 @@ import dotenvFlow from "dotenv-flow";
import { db, sqlite, tables } from "#database/db";
import { Client } from "#structures/index";
import { GatewayIntentBits } from "discord.js";
import { registerEvents, updateBotStats } from "#utils/index";
import { registerEvents, RSA, updateBotStats } from "#utils/index";
import { runChecks } from "#checks/run";
import path from "path";
import fs from "fs";
import { syncAnilistUsers, type WorkerResponseUnion } from "#workers/index";
import { subtle } from "crypto";

process.on("SIGINT", () => {
sqlite.close();
process.exit();
})
import { env } from "#env";

dotenvFlow.config({ silent: true });

export const client = new Client({ intents: [GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.GuildExpressions, GatewayIntentBits.DirectMessages, GatewayIntentBits.Guilds], allowedMentions: { repliedUser: false } });
const workerManager = new Worker("#workers/manager.ts");

process.on("SIGINT", () => {
client.modules.closeAllModules();
sqlite.close();
process.exit();
})

async function start(token: string | undefined) {
await client.rsa.loadKeys();
await RSA.loadKeys();
await registerEvents(client);
await runChecks(client);

Expand All @@ -41,34 +42,7 @@ async function start(token: string | undefined) {
info: "Initialized log!"
}]));

process.env.UPTIME = Date.now();
}

async function makeRSAPair() {
const RSAdirectory = path.join(import.meta.dir, 'RSA');
if (fs.existsSync(path.join(RSAdirectory, 'id_rsa'))) return;

const keyPair = await subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 4096,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // Value taken from https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams#publicexponent
hash: "SHA-256",
}, true, ['encrypt', 'decrypt'])

const publicKey = await subtle.exportKey('spki', keyPair.publicKey);
const privateKey = await subtle.exportKey('pkcs8', keyPair.privateKey)

const exportedPublicKey = '-----BEGIN PUBLIC KEY-----\n' +
btoa(String.fromCharCode.apply(null, [...new Uint8Array(publicKey)])).replace(/.{64}/g, '$&\n') + '\n-----END PUBLIC KEY-----';
const exportedPrivateKey = '-----BEGIN PRIVATE KEY-----\n' + // Inserts a newline every 64 characters
btoa(String.fromCharCode.apply(null, [...new Uint8Array(privateKey)])).replace(/.{64}/g, '$&\n') + '\n-----END PRIVATE KEY-----';

if (!fs.existsSync(RSAdirectory)) fs.mkdirSync(RSAdirectory);

fs.writeFileSync(path.join(RSAdirectory, 'id_rsa'), exportedPrivateKey);
fs.writeFileSync(path.join(RSAdirectory, 'id_rsa.pub'), exportedPublicKey);

client.log("Successfully generated the RSA key pair!", "RSA")
env().UPTIME = Date.now();
}

async function initializeWorkerDB() {
Expand All @@ -83,10 +57,7 @@ async function initializeWorkerDB() {
};
}


// await makeRSAPair();
await start(process.env.TOKEN);

await start(env().TOKEN);
await initializeWorkerDB();

// tell the worker to start a check loop
Expand Down
5 changes: 3 additions & 2 deletions src/caching/redis.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createClient } from "redis";
import { client } from "app";
import { env } from "#env";

const host = process.env.NODE_ENV === "docker" ? "dragonfly" : "localhost";
const host = env().NODE_ENV === "docker" ? "dragonfly" : "localhost";

export const redis = createClient({
socket: {
Expand All @@ -15,7 +16,7 @@ redis.on("error", (err) => {
});

redis.on("connect", () => {
client.log(`Connected to ${host}!`, "Redis");
client.log(`Connected to ${host}!`, "info");
redis.set("test", "test");
});

Expand Down
10 changes: 5 additions & 5 deletions src/checks/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ export async function runChecks(client: Client) {
}),
)
).flat()
client.log(`Running ${checks.length} checks...`, "CheckRunner")
client.log(`Running ${checks.length} checks...`, "info")

for (const check of checks) {
try {
check.run()
client.log(`[✅] Check "${check.name}" passed.`, "CheckRunner")
client.log(`[✅] Check "${check.name}" passed.`, "info")
}
catch (e) {
if (check.optional === true) {
console.warn(`[⚠️] Optional check "${check.name}" failed. This may or may not cause problems in the future.
client.log(`[⚠️] Optional check "${check.name}" failed. This may or may not cause problems in the future.
> Purpose: ${check.description}
> Why: ${e}
`)
`, "warn")
}
else {
throw new Error(`[❌] FATAL: Critical check "${check.name}" failed. Cannot continue.
Expand All @@ -38,5 +38,5 @@ export async function runChecks(client: Client) {
}
}

client.log(`Checks passed!`, "CheckRunner")
client.log(`Checks passed!`, "info")
}
18 changes: 18 additions & 0 deletions src/checks/verifyModules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import fs from 'fs'
import path from 'path'
import { Check } from '#structures/index'
import { suffix } from "bun:ffi";

const getLibPath = (name: string) => path.join(import.meta.dir, "../modules", `compiled/lib${name}.${suffix}`);

const defaultModule = new Check({
name: 'FFI default module check',
description: 'Ensure that the FFI module exists',
optional: false,
run: () => {
if (!fs.existsSync(getLibPath("modules")))
throw new Error("Default module is not compiled, please run `bun run module:build`")
},
})

export default [defaultModule]
Loading