Skip to content
Open
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
910 changes: 910 additions & 0 deletions bun.lock

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "penguinbot",
"version": "1.0.0",
"description": "",
"main": "index.js",
"main": "src/index.js",
"scripts": {
"test": "node permrun test",
"start": "node permrun"
Expand All @@ -21,9 +21,10 @@
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"@frostzzone/discord-sync-commands": "^0.3.0",
"@jimp/plugin-circle": "^1.6.0",
"@napi-rs/canvas": "^1.0.0",
"@resvg/resvg-js": "^2.6.2",
"axios": "^1.6.2",
"body-parser": "^2.2.0",
"canvas": "^3.2.3",
"chess.js": "^1.0.0-beta.8",
"cors": "^2.8.5",
"discord.js": "^13.14.0",
Expand All @@ -33,8 +34,8 @@
"gifencoder": "^2.0.1",
"glob": "^11.0.3",
"jimp": "^1.6.0",
"jsdom": "^26.1.0",
"jszip": "^3.10.1",
"linkedom": "^0.18.12",
"mathjs": "^15.2.0",
"moment": "^2.29.4",
"moment-duration-format": "^2.3.2",
Expand Down
2 changes: 1 addition & 1 deletion src/basecommands/gifmodifier.worker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const fs = require('fs');
const path = require('path');

const Canvas = require('canvas');
const Canvas = require('@napi-rs/canvas')
const GIFEncoder = require('gifencoder');
const decodeGif = require("../util/decode-gif");

Expand Down
2 changes: 1 addition & 1 deletion src/basecommands/videomodifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const uuid = require('uuid');
const isGif = require('../util/is-gif');
const runNewThread = require('../util/multi-thread');

const Canvas = require('canvas');
const Canvas = require('@napi-rs/canvas')
const ffmpeg = require('fluent-ffmpeg');
const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');

Expand Down
2 changes: 1 addition & 1 deletion src/basecommands/videomodifier.worker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const fs = require('fs');
const path = require('path');

const Canvas = require('canvas');
const Canvas = require('@napi-rs/canvas')

const processFrames = async (data) => {
const { workerSrc, commandSrc, tempDir, imagePath, frameCount, frameRate, width, height, args, serializableData } = data;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/blackjack.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { MessageActionRow, MessageButton, MessageEmbed } = require('discord.js');
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const axios = require('axios');
const fetch = require('node-fetch');

Expand Down
108 changes: 47 additions & 61 deletions src/commands/block.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
const discord = require("discord.js");
const canvas = require('canvas');
const { JSDOM } = require("jsdom");
const { Resvg } = require('@resvg/resvg-js');
const { GlobalFonts } = require('@napi-rs/canvas');
const { parseHTML } = require('linkedom');
const path = require('path');
const OptionType = require('../util/optiontype');
let scratchblocks;

GlobalFonts.registerFromPath(path.join(__dirname, '../../assets/fonts/HelveticaNeue-Medium.otf'), 'Helvetica Neue'); // we need this bcs @resvg/resvg-js doesnt like fonts pls dont remove

let scratchblocks;
(async () => {
scratchblocks = await import("../modules/scratchblocks/index.js");
scratchblocks = scratchblocks.default;
})();

const xmlEscape = function (unsafe) {
return unsafe.replace(/[<>&'"]/g, c => {
switch (c) {
case '<': return '&lt;';
case '>': return '&gt;';
case '&': return '&amp;';
case '\'': return '&apos;';
case '"': return '&quot;';
}
});
};
const wait = (ms) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
};
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));

class Command {
constructor(_, state) {
Expand All @@ -41,11 +27,11 @@ class Command {
admin: false,
lockedToCommands: false,
unlockedChannels: [
"1090809014343974972", // help channel
"1181103736685350983", // tutorials
"1038248289830711406", // bug reports
"1038249236552237167", // suggestions
"1181097377730400287", // spaces
"1090809014343974972",
"1181103736685350983",
"1038248289830711406",
"1038249236552237167",
"1181097377730400287",
],
};
this.slash = {
Expand All @@ -66,17 +52,21 @@ class Command {
async invoke(message, args) {
if (!scratchblocks) await wait(500);
if (!args[0]) return message.reply('Please provide blocks written in [scratchblocks syntax](https://en.scratch-wiki.info/wiki/Block_Plugin/Syntax).');
const window = new JSDOM(`<pre class='blocks'>${xmlEscape(args.join(' '))}</pre>`);
const scratchBlocksInstance = scratchblocks(window.window);
scratchBlocksInstance.appendStyles();
scratchBlocksInstance.renderMatching("pre.blocks", {

const { window } = parseHTML(`<!DOCTYPE html><html><head></head><body></body></html>`);

const sb = scratchblocks(window);

const doc = sb.parse(args.join(' '), {
style: "scratch3",
languages: ["en"]
});
const scratchBlocksDiv = window.window.document.querySelector("div.scratchblocks");
const svgElement = scratchBlocksDiv.getElementsByTagName('svg').item(0);
let newWidth = Math.ceil(Number(svgElement.getAttribute('width')) * 2);
let newHeight = Math.ceil(Number(svgElement.getAttribute('height')) * 2);
const view = sb.newView(doc, { style: "scratch3", scale: 1 });
const svgEl = view.render();

let newWidth = view.width;
let newHeight = view.height;

if (newWidth < 1 || newHeight < 1) {
return message.reply('The resulting image is blank.\nPlease provide blocks written in [scratchblocks syntax](https://en.scratch-wiki.info/wiki/Block_Plugin/Syntax).');
}
Expand All @@ -90,33 +80,29 @@ class Command {
newWidth = Math.ceil(newWidth / divisor);
newHeight = Math.ceil(newHeight / divisor);
}
svgElement.setAttribute('width', String(newWidth));
svgElement.setAttribute('height', String(newHeight));
svgElement.setAttribute('viewbox', `0 0 ${newWidth} ${newHeight}`);
// add extra style tags & stuff
const styleTag1 = svgElement.appendChild(window.window.document.createElement('style'));
styleTag1.innerHTML = `.sb3-comment-label {
fill: black !important;
}
* {
font: 500 12pt "Helvetica Neue", "Helvetica 65 Medium", Helvetica Neue, Helvetica, sans-serif;
}`;
const styleTag2 = svgElement.appendChild(window.window.document.createElement('style'));
styleTag2.innerHTML = window.window.document.head.innerHTML;
const styleTag3 = svgElement.appendChild(window.window.document.createElement('style'));
styleTag3.innerHTML = scratchBlocksInstance.scratch3.stylee.cssContent;
// get svg data
const svgData = scratchBlocksDiv.innerHTML;
const uri = 'data:image/svg+xml;base64,' + Buffer.from(svgData, 'utf8').toString('base64url');
const image = await canvas.loadImage(uri);
const drawingCanvas = canvas.createCanvas(image.width, image.height);
const ctx = drawingCanvas.getContext('2d');
ctx.drawImage(image, 0, 0, image.width, image.height);
message.reply({
files: [drawingCanvas.toBuffer("image/png")]

const styleEl = sb.scratch3.makeStyle();
view.defs.appendChild(styleEl);
let svgData = svgEl.outerHTML;
view.defs.removeChild(styleEl);

if (!svgData.includes('xmlns=')) {
svgData = svgData.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" ');
}

// console.log(svgData)

const resvg = new Resvg(svgData, {
fitTo: { mode: 'width', value: newWidth * 2 },
font: {
fontFiles: [path.join(__dirname, '../../assets/fonts/HelveticaNeue-Medium.otf')],
loadSystemFonts: false,
},
});
const pngBuffer = resvg.render().asPng();

message.reply({ files: [pngBuffer] });
}
}

// needs to do new Command() in index.js because typing static every time STINKS!
module.exports = Command;
module.exports = Command;
2 changes: 1 addition & 1 deletion src/commands/fight.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { MessageEmbed } = require("discord.js");
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const crypto = require("crypto");
const OptionType = require('../util/optiontype');

Expand Down
2 changes: 1 addition & 1 deletion src/commands/gifmodifier/thinkyoure.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class Command extends GifModifierCommand {
encoder.setRepeat(0); // 0 means "to repeat"
encoder.setDelay(Math.round(1000 / 24)); // 24 FPS?

ctx.textAlign = "top";
ctx.textBaseline = "top";
ctx.textDrawingMode = "glyph";
ctx.strokeStyle = "#223";

Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/8ball.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { MessageEmbed, MessageAttachment } = require('discord.js');
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const OptionType = require('../../util/optiontype');

class Command {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/chomp.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const Discord = require("discord.js");
const OptionType = require('../../util/optiontype');

Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/license.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const Discord = require("discord.js");

class LicenseCommand {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/minion.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const Discord = require("discord.js");

const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')

const templateText = [
[7, 16, 260],
Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/pmlogo.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const OptionType = require('../../util/optiontype');
const Discord = require('discord.js');

Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/quote.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { MessageAttachment, Message } = require('discord.js');
const Canvas = require('canvas');
const Canvas = require('@napi-rs/canvas')

const OptionType = require('../../util/optiontype');
const env = require("../../util/env-util");
Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/rip.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { MessageAttachment, MessageEmbed } = require('discord.js');
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const OptionType = require('../../util/optiontype');
const { Jimp: jimp, JimpMime } = require('jimp');

Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/rng.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { MessageAttachment, Message } = require('discord.js');
const Canvas = require('canvas');
const Canvas = require('@napi-rs/canvas')
const OptionType = require('../../util/optiontype');

const rarities = [
Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/sigma.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const Discord = require("discord.js");

const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')

class SigmaCommand {
constructor() {
Expand Down
21 changes: 2 additions & 19 deletions src/commands/imagesimple/toast.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,24 @@ class Command {
}

async invoke(message) {
// Load background image
const background = await jimp.read('./assets/toast.png');

// Load user's avatar
const userAvatar = await jimp.read(message.author.displayAvatarURL({ format: 'png' }));

// Resize and roundify user's avatar
userAvatar.resize({ w: 320, h: 320 }).circle();

// Apply grayscale filter to user's avatar
userAvatar.color([{ apply: "greyscale" }]);

// Composite user's avatar onto the background
background.composite(userAvatar, (background.width / 2) - 160, (background.height / 2) - 160, {
mode: BlendMode.MULTIPLY
});

// Convert Jimp image to buffer for Discord.js
const buffer = await background.getBuffer(JimpMime.png);

// Create a Discord.js attachment from the buffer
const attachment = new Discord.MessageAttachment(buffer, 'toast.png');

// Create an embed with an image attachment
const embed = new Discord.MessageEmbed()
.setTitle('Toast')
.setImage('attachment://toast.png');

// Send the embed with the image
const messageOptions = {
embeds: [embed],
message.reply({
files: [attachment]
};

message.reply(messageOptions);
});
}
}

Expand Down
10 changes: 1 addition & 9 deletions src/commands/imagesimple/wanted.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { MessageAttachment, MessageEmbed } = require('discord.js');
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const OptionType = require('../../util/optiontype');

class Command {
Expand Down Expand Up @@ -46,15 +46,7 @@ class Command {

const attachment = new MessageAttachment(canvas.toBuffer("image/png"), 'wanted-poster.png');

const title = userMention ? `${userMention.username}'s Wanted Poster` : "Your Wanted Poster";

const embed = new MessageEmbed()
.setTitle(title)
.setImage('attachment://wanted-poster.png');


const messageOptions = {
embeds: [embed],
files: [attachment]
};

Expand Down
2 changes: 1 addition & 1 deletion src/commands/imagesimple/wheel.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const OptionType = require('../../util/optiontype');
const discord = require("discord.js");

Expand Down
2 changes: 1 addition & 1 deletion src/commands/projectsapi/analyze.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { createCanvas, loadImage } = require('canvas'); // best when dealing with text & shapes
const { createCanvas, loadImage } = require('@napi-rs/canvas') // best when dealing with text & shapes
const { Jimp: jimp, JimpMime } = require('jimp'); // best for adding effects to images & dealing with transparency
const axios = require('axios');
const JSZip = require('jszip');
Expand Down
2 changes: 1 addition & 1 deletion src/commands/standoff.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const discord = require("discord.js");
const crypto = require("crypto");
const { createCanvas, loadImage } = require('canvas');
const { createCanvas, loadImage } = require('@napi-rs/canvas')
const OptionType = require('../util/optiontype');

const delay = (ms) => {
Expand Down
3 changes: 2 additions & 1 deletion src/modules/scratchblocks/scratch3/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import SVG from "./draw.js"
import { LabelView } from "./blocks.js"
import style from "./style.js"
import { createCanvas } from "@napi-rs/canvas"

export function init(window) {
SVG.init(window)

LabelView.measuring = SVG.makeCanvas().getContext("2d")
LabelView.measuring = createCanvas(1, 1).getContext("2d")
}

export const makeStyle = style.makeStyle
Expand Down
Loading