diff --git a/bun.lock b/bun.lock index 90d8d88..5a7657f 100644 --- a/bun.lock +++ b/bun.lock @@ -7,8 +7,8 @@ "@napi-rs/canvas": "^0.1.72", "@prisma/client": "^6.10.1", "@sern/handler": "^4.0.0", - "@sern/publisher": "^1.1.1", - "discord.js": "latest", + "@sern/publisher": "1.1.2", + "discord.js": "^14.21.0", "dotenv": "^16.3.1", "mongodb": "^6.17.0", "sharp": "^0.34.2", @@ -185,7 +185,7 @@ "@sern/ioc": ["@sern/ioc@1.1.2", "", {}, "sha512-n84w7n5hB1dl8N6dfSbeYIo0QYORMS1bpG/P7J7GoMNTu8c28EYVZ8uGs3Md9GB09UseOKn3mfv1QBDtRsbb1g=="], - "@sern/publisher": ["@sern/publisher@1.1.4", "", {}, "sha512-2O3GmKTzSdZr2cZhrVRFXg1iB0VwZT3vKfuubtJYLneu7F0j1oU6Db4QjGqloiZvOE4HBdQM1AhLx37ej22lgA=="], + "@sern/publisher": ["@sern/publisher@1.1.2", "", {}, "sha512-1zh99JZykKUhqHhE75ZXfiLsBtf1WI+NnDCojv8UlpnGBEyzO8xyI1X7PNf6cPKRs4W9XqY3PqTJ+hrqzIsMkg=="], "@types/luxon": ["@types/luxon@3.4.2", "", {}, "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA=="], @@ -257,7 +257,7 @@ "discord.js": ["discord.js@14.21.0", "", { "dependencies": { "@discordjs/builders": "^1.11.2", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.1", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-U5w41cEmcnSfwKYlLv5RJjB8Joa+QJyRwIJz5i/eg+v2Qvv6EYpCRhN9I2Rlf0900LuqSDg8edakUATrDZQncQ=="], - "dotenv": ["dotenv@16.6.0", "", {}, "sha512-Omf1L8paOy2VJhILjyhrhqwLIdstqm1BvcDPKg4NGAlkwEu9ODyrFbvk8UymUOMCT+HXo31jg1lArIrVAAhuGA=="], + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], @@ -423,7 +423,7 @@ "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - "ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], "yocto-queue": ["yocto-queue@1.2.1", "", {}, "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg=="], diff --git a/package.json b/package.json index de11f34..0f4ea81 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "@napi-rs/canvas": "^0.1.72", "@prisma/client": "^6.10.1", "@sern/handler": "^4.0.0", - "@sern/publisher": "^1.1.1", - "discord.js": "latest", + "@sern/publisher": "1.1.2", + "discord.js": "^14.21.0", "dotenv": "^16.3.1", "mongodb": "^6.17.0", "sharp": "^0.34.2" diff --git a/src/commands/contextMenu/cursivify.ts b/src/commands/contextMenu/cursivify.ts new file mode 100644 index 0000000..bafb7ee --- /dev/null +++ b/src/commands/contextMenu/cursivify.ts @@ -0,0 +1,16 @@ +import { commandModule, CommandType } from '@sern/handler'; + +export default commandModule({ + name: 'cursivify', + type: CommandType.CtxMsg, + plugins: [], + execute: async (ctx) => { + await ctx.deferReply() + const trimmedstring = ctx.targetMessage.content.replaceAll('*', ''); + if (trimmedstring.length === 0) { + await ctx.editReply('No hay nada que cursivificar!'); + } else { + await ctx.editReply(`*${trimmedstring}*`); + } + }, +}); diff --git a/src/commands/contextMenu/image-classification.ts b/src/commands/contextMenu/image-classification.ts new file mode 100644 index 0000000..df5196d --- /dev/null +++ b/src/commands/contextMenu/image-classification.ts @@ -0,0 +1,63 @@ +import { commandModule, CommandType } from '@sern/handler'; +import { AttachmentBuilder, codeBlock } from 'discord.js'; +import { createCanvas, loadImage } from '@napi-rs/canvas'; +import sharp from 'sharp'; + +export default commandModule({ + name: 'Clasifica una imagen', + type: CommandType.CtxMsg, + plugins: [], + execute: async (ctx) => { + await ctx.deferReply(); + + if (ctx.targetMessage.attachments.size === 0) + return ctx.editReply('No hay ninguna imagen para clasificar!'); + const image = ctx.targetMessage.attachments.first()!; + if (!image.contentType!.startsWith('image/') && image.contentType !== 'image/gif') + return ctx.editReply('El archivo no es una imagen!'); + + const imageBuffer = await fetch(image.url).then(async (res) => await res.arrayBuffer()); + const compressed = sharp(imageBuffer) + .png({ quality: 70 }) + .jpeg({ quality: 70 }) + .webp({ quality: 70 }) + .tiff({ quality: 70 }); + const imageUint8Array = new Uint8Array(await compressed.toBuffer()); + + const request = await fetch( + `https://api.cloudflare.com/client/v4/accounts/${process.env.CF_AI_ACC}/ai/run/@cf/facebook/detr-resnet-50`, + { + method: 'POST', + headers: { + Authorization: `Bearer ${process.env.CF_AI_TOKEN}`, + 'Content-Type': 'application/octet-stream', + }, + body: imageUint8Array, + } + ).then(async (res) => await res.json()); + if (request.errors.length > 0) { + return ctx.editReply(`Hubo un error! ${codeBlock(JSON.stringify(request.errors))}`); + } + + // all canvas stuff, this was fun to make + const imgMetadata = await compressed.metadata(); + const canvas = createCanvas(imgMetadata.width!, imgMetadata.height!); + const ctxCanvas = canvas.getContext('2d'); + const img = await loadImage(image.url); + ctxCanvas.drawImage(img, 0, 0, imgMetadata.width!, imgMetadata.height!); + ctxCanvas.font = '30px sans-serif'; + ctxCanvas.fillStyle = 'red'; + ctxCanvas.strokeStyle = 'red'; + ctxCanvas.lineWidth = 3; + for (const result of request.result) { + if (result.score < 0.5) continue; + const box = result.box; + ctxCanvas.strokeRect(box.xmin, box.ymin, box.xmax - box.xmin, box.ymax - box.ymin); + ctxCanvas.fillText(result.label, box.xmin, box.ymin - 5); + } + const canvasBuffer = canvas.toBuffer('image/png'); + const attachment = new AttachmentBuilder(canvasBuffer, { name: 'generatedImage.png' }); + + await ctx.editReply({ files: [attachment] }); + }, +});