modern-canvacord
Version:
Easy image manipulation for discord.js bots.
1,373 lines (1,185 loc) • 51.9 kB
JavaScript
const Trigger = require("../libs/Trigger");
const Greyscale = require("../libs/Greyscale");
const Invert = require("../libs/Invert");
const Sepia = require("../libs/Sepia");
const assets = require("./Assets");
const fs = require("fs");
const Brightness = require("../libs/Brightness");
const Threshold = require("../libs/Threshold");
const Convolute = require("../libs/Convolute");
const rect = require("../plugins/rect");
const Canvas = require("canvas");
const Darkness = require("../libs/Darkness");
const circle = require("../plugins/circle");
const round = require("../plugins/round");
const Util = require("./Util");
/**
* Canvacord Memes Generator
* @example const Canvacord = require("canvacord");
*
* Canvacord.Canvas.trigger("./image.png")
* .then(triggered => {
* Canvacord.write(triggered, "triggered.gif");
* })
*/
class Canvacord {
/**
* **⚠ You may not instantiate Canvacord class! ⚠**
* @hideconstructor
*/
constructor() {
throw new Error(`The ${this.constructor.name} class may not be instantiated!`);
}
/**
* This method can be used to apply Triggered effect on image.
* @param {string|Buffer} image Image to trigger
* @returns {Promise<Buffer>}
*/
static async trigger(image) {
if (!image) throw new Error("Expected image, received nothing!");
await Canvacord.__wait();
return await Trigger(image, assets("IMAGE").TRIGGERED);
}
/**
* Inverts color of the image
* @param {string|Buffer} image Img to invert
* @returns {Promise<Buffer>}
*/
static async invert(image) {
if (!image) throw new Error("Expected image, received nothing!");
return await Invert(image);
}
/**
* Apply sepia wash on img
* @param {string|Buffer} image Img
* @returns {Promise<Buffer>}
*/
static async sepia(image) {
if (!image) throw new Error("Expected image, received nothing!");
return await Sepia(image);
}
/**
* Greyscale effect over image
* @param {string|Buffer} image Img
* @returns {Promise<Buffer>}
*/
static async greyscale(image) {
if (!image) throw new Error("Expected image, received nothing!");
return await Greyscale(image);
}
/**
* Edit image brightness
* @param {string|Buffer} image Img
* @param {number} amount Brightness amount
* @returns {Promise<Buffer>}
*/
static async brightness(image, amount) {
if (!image) throw new Error("Expected image, received nothing!");
if (isNaN(amount)) throw new Error("Amount must be a number!");
return await Brightness(image, amount);
}
/**
* Edit image darkness
* @param {string|Buffer} image Img
* @param {number} amount Darkness amount
* @returns {Promise<Buffer>}
*/
static async darkness(image, amount) {
if (!image) throw new Error("Expected image, received nothing!");
if (isNaN(amount)) throw new Error("Amount must be a number!");
return await Darkness(image, amount);
}
/**
* Image threshold
* @param {string|Buffer} img Image
* @param {number} amount Threshold amount
* @returns {Promise<Buffer>}
*/
static async threshold(img, amount) {
if (!img) throw new Error("Expected image, received nothing!");
if (isNaN(amount)) throw new Error("Amount must be a number!");
return await Threshold(img, amount);
}
/**
* Image Convolution
* @param {string|Buffer} img Image
* @param {number[]} matrix Convolution matrix
* @param {boolean} opaque If convolution should be opaque
* @returns {Promise<Buffer>}
*/
static async convolute(img, matrix, opaque) {
if (!img) throw new Error("Expected image, received nothing!");
if (!Array.isArray(matrix)) throw new Error("Convolution matrix must be Array.");
return await Convolute(img, matrix, opaque);
}
/**
* Creates Progress bar
* @param {object} track Progressbar track options
* @param {number} [track.x] The x-axis
* @param {number} [track.y] The y-axis
* @param {number} [track.width] Progressbar track width
* @param {number} [track.height] Progressbar track height
* @param {string} [track.color] Progressbar track color
* @param {boolean} [track.stroke] Use stroke for track
* @param {number} [track.lineWidth] This param will be used if `track.stroke` is set to `true`
* @param {object} bar Progressbar options
* @param {number} [bar.width] Progressbar width
* @param {string} [bar.color] Progressbar color
* @returns {Buffer}
*/
static createProgressBar(
track = { x: false, y: false, width: false, height: false, color: false, stroke: false, lineWidth: false },
bar = { width: false, color: false }
) {
if (!track) throw new Error("Invalid track args!");
if (!bar) throw new Error("Invalid progressbar args!");
const canvas = Canvas.createCanvas(track.width, track.height);
const ctx = canvas.getContext("2d");
if (bar.width > track.width) bar.width = track.width;
if (bar.width < 0) bar.width = 0;
if (track.stroke) {
rect(ctx, track.x, track.y, track.height, bar.width, bar.color, false);
rect(ctx, track.x, track.y, track.height, track.width, track.color, track.stroke, track.lineWidth);
} else {
rect(ctx, track.x, track.y, track.height, track.width, track.color, track.stroke, track.lineWidth);
rect(ctx, track.x, track.y, track.height, bar.width, bar.color, false);
}
return canvas.toBuffer();
}
/**
* Blur an image
* @param {string|Buffer} image Image to blur
* @returns {Promise<Buffer>}
*/
static async blur(image) {
if (!image) throw new Error("Image was not provided!");
const img = await Canvas.loadImage(image);
const canvas = Canvas.createCanvas(img.width, img.height);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width / 4, canvas.height / 4);
ctx.imageSmoothingEnabled = true;
ctx.drawImage(canvas, 0, 0, canvas.width / 4, canvas.height / 4, 0, 0, canvas.width + 5, canvas.height + 5);
return canvas.toBuffer();
}
/**
* Pixelate
* @param {string|Buffer} image Image to pixelate
* @param {number} pixels Pixels
* @returns {Promise<Buffer>}
*/
static async pixelate(image, pixels = 5) {
if (!image) throw new Error("Image was not provided!");
if (!pixels || typeof pixels !== "number") pixels = 100;
if (pixels < 1) pixels = 100;
if (pixels > 100) pixels = 100;
const img = await Canvas.loadImage(image);
const canvas = Canvas.createCanvas(img.width, img.height);
const ctx = canvas.getContext("2d");
const pixel = pixels / 100;
ctx.drawImage(img, 0, 0, canvas.width * pixel, canvas.height * pixel);
ctx.imageSmoothingEnabled = false;
ctx.drawImage(canvas, 0, 0, canvas.width * pixel, canvas.height * pixel, 0, 0, canvas.width + 5, canvas.height + 5);
return canvas.toBuffer();
}
/**
* Sharpen an image
* @param {string|Buffer} image Image to sharpen
* @param {number} lvl sharpness intensity
* @returns {Promise<Buffer>}
*/
static async sharpen(image, lvl = 1) {
if (!image) throw new Error("Image was not provided!");
return await Convolute(image, Canvacord.CONVOLUTION_MATRIX.SHARPEN, true, lvl);
}
/**
* Applies burn effect on an image
* @param {string|Buffer} image Image source
* @param {number} lvl intensity
* @returns {Promise<Buffer>}
*/
static async burn(image, lvl = 1) {
if (!image) throw new Error("Image was not provided!");
return await Convolute(image, Canvacord.CONVOLUTION_MATRIX.BURN, true, lvl);
}
/**
* HTML5 color to image
* @param {string} color HTML5 color
* @param {boolean} displayHex If it should display hex
* @param {number} height Image height
* @param {number} width Image width
* @returns {Buffer}
*/
static color(color = "#FFFFFF", displayHex = false, height = 1024, width = 1024) {
const canvas = Canvas.createCanvas(width, height);
const ctx = canvas.getContext("2d");
rect(ctx, 0, 0, height, width, color);
if (!!displayHex) {
const ic = Util.invertColor(color);
ctx.font = "bold 72px Manrope";
ctx.fillStyle = ic;
ctx.fillText(color.toUpperCase(), canvas.width / 3, canvas.height / 2);
}
return canvas.toBuffer();
}
/**
* Creates circular image
* @param {string|Buffer} image Image source
* @returns {Promise<Buffer>}
*/
static async circle(image) {
if (!image) throw new Error("Image was not provided!");
const img = await Canvas.loadImage(image);
const canvas = Canvas.createCanvas(img.width, img.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
circle(ctx, canvas.width, canvas.height);
return canvas.toBuffer();
}
/**
* Creates a rectangle
* @param {number} x x-axis
* @param {number} y y-axis
* @param {number} width width
* @param {number} height height
* @param {string} color color
* @param {boolean} stroke If it should stroke
* @param {number} lineWidth line width
* @returns {Buffer}
*/
static rectangle(x, y, width, height, color, stroke, lineWidth) {
const canvas = Canvas.createCanvas(width, height);
const ctx = canvas.getContext("2d");
rect(ctx, x, y, canvas.height, canvas.width, color, !!stroke, lineWidth);
round(ctx, x, y, canvas.width, canvas.height);
return canvas.toBuffer();
}
/**
* Fuse two images
* @param {string|Buffer} image1 First image
* @param {string|Buffer} image2 Second image
* @returns {Promise<Buffer>}
*/
static async fuse(image1, image2) {
if (!image1) throw new Error("Missing parameter 'image1'!");
if (!image2) throw new Error("Missing parameter 'image2'!");
const img1 = await Canvas.loadImage(image1);
const img2 = await Canvas.loadImage(image2);
const canvas = Canvas.createCanvas(img1.width, img1.height);
const ctx = canvas.getContext("2d");
ctx.globalAlpha = 0.5;
ctx.drawImage(img1, 0, 0);
ctx.drawImage(img2, 0, 0, canvas.width, canvas.height);
return canvas.toBuffer();
}
/**
* Resize an image
* @param {string|Buffer} image Image source
* @param {number} width width
* @param {number} height height
* @returns {Promise<Buffer>}
*/
static async resize(image, width, height) {
if (!image) throw new Error("Image was not provided!");
const img = await Canvas.loadImage(image);
const w = width && !isNaN(width) ? width : img.width;
const h = height && !isNaN(height) ? width : img.height;
const canvas = await Canvas.createCanvas(w, h);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
return canvas.toBuffer();
}
/**
* Kiss each other ( ͡° ͜ʖ ͡°)
* @param {string|Buffer} image1 First image
* @param {string|Buffer} image2 Second image
* @returns {Promise<Buffer>}
*/
static async kiss(image1, image2) {
if (!image1) throw new Error("First image was not provided!");
if (!image2) throw new Error("Second image was not provided!");
await this.__wait();
const canvas = Canvas.createCanvas(768, 574);
const ctx = canvas.getContext("2d");
const background = await Canvas.loadImage(Canvacord.assets("IMAGE").KISS);
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
const avatar = await Canvas.loadImage(image1);
const avatar1 = await Canvas.loadImage(image2);
ctx.drawImage(avatar1, 370, 25, 200, 200);
ctx.drawImage(avatar, 150, 25, 200, 200);
return canvas.toBuffer();
}
/**
* Spank someone ( ͡° ͜ʖ ͡°)
* @param {string|Buffer} image1 First image
* @param {string|Buffer} image2 Second image
* @returns {Promise<Buffer>}
*/
static async spank(image1, image2) {
if (!image1) throw new Error("First image was not provided!");
if (!image2) throw new Error("Second image was not provided!");
await this.__wait();
const canvas = Canvas.createCanvas(500, 500);
const ctx = canvas.getContext("2d");
const background = await Canvas.loadImage(Canvacord.assets("IMAGE").SPANK);
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
const avatar = await Canvas.loadImage(image1);
const avatar1 = await Canvas.loadImage(image2);
ctx.drawImage(avatar1, 350, 220, 120, 120);
ctx.drawImage(avatar, 225, 5, 140, 140);
return canvas.toBuffer();
}
/**
* Loads font
* @param {any[]} fontArray Font array
* @returns {Promise<void>}
*/
static async registerFonts(fontArray = []) {
if (!fontArray.length) {
await Canvacord.__wait();
// default fonts
Canvas.registerFont(assets("FONT").MANROPE_BOLD, {
family: "Manrope",
weight: "bold",
style: "normal"
});
Canvas.registerFont(assets("FONT").MANROPE_REGULAR, {
family: "Manrope",
weight: "regular",
style: "normal"
});
Canvas.registerFont(assets("FONT").WHITNEY_MEDIUM, {
family: "Whitney",
weight: "regular",
style: "normal"
});
Canvas.registerFont(assets("FONT").WHITNEY_BOOK, {
family: "Whitney",
weight: "bold",
style: "normal"
});
Canvas.registerFont(assets("FONT").ROBOTO_LIGHT, {
family: "Roboto",
weight: "light",
style: "normal"
});
Canvas.registerFont(assets("FONT").ROBOTO_REGULAR, {
family: "Roboto",
weight: "regular",
style: "normal"
});
} else {
fontArray.forEach(font => {
Canvas.registerFont(font.path, font.face);
});
}
return;
}
/**
* Slap someone ( ͡° ͜ʖ ͡°)
* @param {string|Buffer} image1 First image
* @param {string|Buffer} image2 Second image
* @returns {Promise<Buffer>}
*/
static async slap(image1, image2) {
if (!image1) throw new Error("First image was not provided!");
if (!image2) throw new Error("Second image was not provided!");
await this.__wait();
const canvas = Canvas.createCanvas(1000, 500);
const ctx = canvas.getContext("2d");
const background = await Canvas.loadImage(Canvacord.assets("IMAGE").BATSLAP);
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
const avatar = await Canvas.loadImage(image1);
const avatar1 = await Canvas.loadImage(image2);
ctx.drawImage(avatar1, 580, 260, 200, 200);
ctx.drawImage(avatar, 350, 70, 220, 220);
return canvas.toBuffer();
}
/**
* Oh this? This is beautiful!
* @param {string|Buffer} image Source image
* @returns {Promise<Buffer>}
*/
static async beautiful(image) {
if (!image) throw new Error("Image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(image);
const base = await Canvas.loadImage(Canvacord.assets("IMAGE").BEAUTIFUL);
const canvas = Canvas.createCanvas(376, 400);
const ctx = canvas.getContext("2d");
ctx.drawImage(base, 0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 258, 28, 84, 95);
ctx.drawImage(img, 258, 229, 84, 95);
return canvas.toBuffer();
}
/**
* facepalm
* @param {string|Buffer} image image
* @returns {Promise<Buffer>}
*/
static async facepalm(image) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
let layer = await Canvas.loadImage(Canvacord.assets("IMAGE").FACEPALM);
let canvas = Canvas.createCanvas(632, 357);
let ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 632, 357);
let avatar = await Canvas.loadImage(image);
ctx.drawImage(avatar, 199, 112, 235, 235);
ctx.drawImage(layer, 0, 0, 632, 357);
return canvas.toBuffer();
}
/**
* Rainbow ( ͡° ͜ʖ ͡°)
* @param {string|Buffer} image Image source
* @returns {Promise<Buffer>}
*/
static async rainbow(image) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
let bg = await Canvas.loadImage(Canvacord.assets("IMAGE").GAY);
let img = await Canvas.loadImage(image);
const canvas = Canvas.createCanvas(img.width, img.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
return canvas.toBuffer();
}
/**
* "F" in the chat
* @param {string|Buffer} image image source
* @returns {Promise<Buffer>}
*/
static async rip(image) {
if (!image) throw new Error("Image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(image);
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").RIP);
const canvas = Canvas.createCanvas(244, 253);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 63, 110, 90, 90);
return canvas.toBuffer();
}
/**
* Trash?
* @param {string|Buffer} image Image source
* @returns {Promise<Buffer>}
*/
static async trash(image) {
if (!image) throw new Error("Image was not provided!");
await this.__wait();
const blur = await Canvacord.blur(image);
const img = await Canvas.loadImage(blur);
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").TRASH);
const canvas = Canvas.createCanvas(bg.width, bg.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, 0, 0);
ctx.drawImage(img, 309, 0, 309, 309);
return canvas.toBuffer();
}
/**
* Worse than hitler
* @param {string|Buffer} image Source image
* @returns {Promise<Buffer>}
*/
static async hitler(image) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(image);
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").HITLER);
const canvas = Canvas.createCanvas(bg.width, bg.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, 0, 0);
ctx.drawImage(img, 46, 43, 140, 140);
return canvas.toBuffer();
}
/**
* Updates image color
* @param {string|Buffer} image Image source
* @param {string} color HTML5 color
* @returns {Promise<Buffer>}
*/
static async colorfy(image, color) {
if (!image) throw new Error("Image was not provided!");
const img = await Canvas.loadImage(image);
const canvas = Canvas.createCanvas(img.width, img.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
if (color) {
ctx.globalCompositeOperation = "color";
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
return canvas.toBuffer();
}
/**
* whoosh
* @param {string|Buffer} image Image source
* @returns {Promise<Buffer>}
*/
static async jokeOverHead(image) {
if (!image) throw new Error("Image wasn ot provided!");
await this.__wait();
const layer = await Canvas.loadImage(Canvacord.assets("IMAGE").JOKEOVERHEAD);
const img = await Canvas.loadImage(image)
const canvas = Canvas.createCanvas(425, 404);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, 425, 404);
ctx.drawImage(img, 125, 130, 140, 135);
ctx.drawImage(layer, 0, 0, 425, 404);
return canvas.toBuffer();
}
/**
* Distracted boyfriend
* @param {string|Buffer} image1 Face for the girl in red color
* @param {string|Buffer} image2 Face for the boy
* @param {string|Buffer} image3 Face for the other girl [optional]
* @returns {Promise<Buffer>}
*/
static async distracted(image1, image2, image3 = null) {
if (!image1) throw new Error("First image was not provided!");
if (!image2) throw new Error("Second image was not provided!");
await this.__wait();
const background = await Canvas.loadImage(Canvacord.assets("IMAGE").DISTRACTED);
const avatar1 = await Canvas.loadImage(await Canvacord.circle(image1));
const avatar2 = await Canvas.loadImage(await Canvacord.circle(image2));
const avatar3 = image3 ? await Canvas.loadImage(await Canvacord.circle(image3)) : null;
const canvas = Canvas.createCanvas(background.width, background.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
ctx.drawImage(avatar1, 180, 90, 150, 150);
ctx.drawImage(avatar2, 480, 35, 130, 130);
if (avatar3) ctx.drawImage(avatar3, 730, 110, 130, 130);
return canvas.toBuffer();
}
/**
* No, it doesn't affect my baby.
* @param {string|Buffer} image Source image
* @returns {Promise<Buffer>}
*/
static async affect(image) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(image);
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").AFFECT);
const canvas = Canvas.createCanvas(bg.width, bg.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, 0, 0);
ctx.drawImage(img, 180, 383, 200, 157);
return canvas.toBuffer();
}
/**
* Jail
* @param {string|Buffer} image Source image
* @param {boolean} greyscale If it should greyscale image
* @returns {Promise<Buffer>}
*/
static async jail(image, greyscale = false) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(greyscale ? await Canvacord.greyscale(image) : image);
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").JAIL);
const canvas = Canvas.createCanvas(350, 350);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
return canvas.toBuffer();
}
/**
* bed
* @param {string|Buffer} image1 First image
* @param {string|Buffer} image2 Second image
* @returns {Promise<Buffer>}
*/
static async bed(image1, image2) {
if (!image1) throw new Error("First image was not provided!");
if (!image2) throw new Error("Second image was not provided!");
await this.__wait();
const avatar = await Canvas.loadImage(image1);
const avatar1 = await Canvas.loadImage(image2);
const background = await Canvas.loadImage(Canvacord.assets("IMAGE").BED);
const canvas = Canvas.createCanvas(background.width, background.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(background, 0, 0, canvas.width, canvas.height);
ctx.drawImage(avatar, 25, 100, 100, 100);
ctx.drawImage(avatar, 25, 300, 100, 100);
ctx.drawImage(avatar, 53, 450, 70, 70);
ctx.drawImage(avatar1, 53, 575, 100, 100);
return canvas.toBuffer();
}
/**
* Delete
* @param {string|Buffer} image Source image
* @param {boolean} dark If image should be in dark mode
* @returns {Promise<Buffer>}
*/
static async delete(image, dark = false) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(image);
const bg = await Canvas.loadImage(dark ? await Canvacord.invert(Canvacord.assets("IMAGE").DELETE) : Canvacord.assets("IMAGE").DELETE);
const canvas = Canvas.createCanvas(bg.width, bg.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 120, 135, 195, 195);
return canvas.toBuffer();
}
/**
* TicTacToe
* @param {object} fill TicTacToe params
* @param {"X"|"O"} [fill.a1] a1 value
* @param {"X"|"O"} [fill.b1] b1 value
* @param {"X"|"O"} [fill.c1] c1 value
* @param {"X"|"O"} [fill.a2] a2 value
* @param {"X"|"O"} [fill.b2] b2 value
* @param {"X"|"O"} [fill.c2] c2 value
* @param {"X"|"O"} [fill.a3] a3 value
* @param {"X"|"O"} [fill.b3] b3 value
* @param {"X"|"O"} [fill.c3] c3 value
* @param {object} color Color params
* @param {string} [color.bg] Background clolor
* @param {string} [color.bar] TicTacToe bar color
* @param {string} [color.x] Color of **X**
* @param {string} [color.o] Color of **O**
* @returns {Buffer}
*/
static tictactoe(fill = { a1: 0, b1: 0, c1: 0, a2: 0, b2: 0, c2: 0, a3: 0, b3: 0, c3: 0 }, color = { bg: 0, bar: 0, x: 0, o: 0 }) {
color = {
bg: color.bg || "white",
bar: color.bar || "black",
x: color.x || "red",
o: color.o || "blue"
};
const canvas = Canvas.createCanvas(2048, 2048);
const ctx = canvas.getContext("2d");
const drawO = (x, y) => {
let halfSectionSize = (0.5 * 682);
let centerX = x + halfSectionSize;
let centerY = y + halfSectionSize;
let radius = (682 - 100) / 2;
let startAngle = 0 * Math.PI;
let endAngle = 2 * Math.PI;
ctx.lineWidth = 40;
ctx.strokeStyle = color.o;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.stroke();
};
const drawX = (x, y) => {
ctx.strokeStyle = color.x;
ctx.lineWidth = 40;
ctx.beginPath();
let offset = 50;
ctx.moveTo(x + offset, y + offset);
ctx.lineTo(x + 682 - offset, y + 682 - offset);
ctx.moveTo(x + offset, y + 682 - offset);
ctx.lineTo(x + 682 - offset, y + offset);
ctx.stroke();
};
const params = {
a1: {
x: 5,
y: 5
},
b1: {
x: 682,
y: 5
},
c1: {
x: 1364,
y: 5
},
a2: {
x: 5,
y: 682
},
b2: {
x: 682,
y: 682
},
c2: {
x: 1364,
y: 682
},
a3: {
x: 5,
y: 1364
},
b3: {
x: 682,
y: 1364
},
c3: {
x: 1364,
y: 1364
}
};
// background
ctx.fillStyle = color.bg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Lines
ctx.lineWidth = 30;
ctx.lineCap = "round";
ctx.strokeStyle = color.bar;
ctx.beginPath();
//Horizontal lines
for (var y = 1; y <= 2; y++) {
ctx.moveTo(4, y * 682);
ctx.lineTo(2043, y * 682);
}
// Vertical lines
for (var x = 1; x <= 2; x++) {
ctx.moveTo(x * 682, 4);
ctx.lineTo(x * 682, 2043);
}
ctx.stroke();
// apply
Object.keys(fill).forEach(x => {
if (!fill[x] || !["X", "O"].includes(fill[x])) return;
const data = params[x];
fill[x] === "X" ? drawX(data.x, data.y) : drawO(data.x, data.y);
});
return canvas.toBuffer();
}
/**
* Opinion
* @param {string|Buffer} avatar Image
* @param {string} msg Message
* @returns {Promise<Buffer>}
*/
static async opinion(avatar, msg) {
if (!avatar) throw new Error("Avatar was not provided!");
if (!msg) throw new Error("Message was not provided!");
await this.__wait();
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").OPINION);
const ava = await Canvas.loadImage(avatar);
const canvas = Canvas.createCanvas(482, 481);
const ctx = canvas.getContext("2d");
ctx.drawImage(ava, 62, 340, 85, 85);
ctx.drawImage(ava, 260, 180, 70, 70);
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
ctx.font = "bold 15px arial";
ctx.fillStyle = "#000000";
await Util.renderEmoji(ctx, Util.shorten(msg, 24), canvas.width / 10, canvas.height / 1.51);
return canvas.toBuffer();
}
/**
* Creates Gradient
* @param {string} colorFrom Starting color
* @param {string} colorTo Ending color
* @param {number} width Image width
* @param {number} height Image height
* @returns {Buffer}
*/
static gradient(colorFrom, colorTo, width, height) {
if (!colorFrom) throw new Error("ColorFrom was not provided!");
if (!colorTo) throw new Error("ColorTo was not provided!");
const canvas = Canvas.createCanvas(width || 400, height || 200);
const ctx = canvas.getContext("2d");
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop(0, colorFrom);
gradient.addColorStop(1, colorTo);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
return canvas.toBuffer();
}
/**
* Oh no! It's Stupid.
* @param {string} message Message
* @returns {Promise<Buffer>}
*/
static async ohno(message) {
if (!message) throw new Error("Message was not provided!");
await Canvacord.__wait();
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").OHNO);
const canvas = Canvas.createCanvas(1000, 1000);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
ctx.font = "bold 50px Times New Roman";
ctx.fillStyle = "#000000";
await Util.renderEmoji(ctx, Util.shorten(message, 20), 540, 195);
return canvas.toBuffer();
}
/**
* Change my mind (taken from jgoralcz/image-microservice)
* @param {String} text Text
* @see https://github.com/jgoralcz/image-microservice/blob/master/src/workers/canvas/ChangeMyMind.js
* @returns {Promise<Buffer>}
*/
static async changemymind(text) {
if (!text) throw new Error("missing text!");
await this.__wait();
const base = await Canvas.loadImage(Canvacord.assets("IMAGE").CHANGEMYMIND);
const canvas = Canvas.createCanvas(base.width, base.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(base, 0, 0, canvas.width, canvas.height);
let x = text.length;
let fontSize = 70;
if (x <= 15) {
ctx.translate(310, 365);
} else if (x <= 30) {
fontSize = 50;
ctx.translate(315, 365);
} else if (x <= 70) {
fontSize = 40;
ctx.translate(315, 365);
} else if (x <= 85) {
fontSize = 32;
ctx.translate(315, 365);
} else if (x < 100) {
fontSize = 26;
ctx.translate(315, 365);
} else if (x < 120) {
fontSize = 21;
ctx.translate(315, 365);
} else if (x < 180) {
fontSize = 0.0032 * (x * x) - 0.878 * x + 80.545;
ctx.translate(315, 365);
} else if (x < 700) {
fontSize = 0.0000168 * (x * x) - 0.0319 * x + 23.62;
ctx.translate(310, 338);
} else {
fontSize = 7;
ctx.translate(310, 335);
}
ctx.font = `${fontSize}px 'Arial'`;
ctx.rotate(-0.39575);
const lines = Util.getLines({ text, ctx, maxWidth: 345 });
let i = 0;
while (i < lines.length) {
ctx.fillText(lines[i], 10, i * fontSize - 5);
i++;
}
return canvas.toBuffer();
}
/**
* Clyde
* @param {string} message Message
* @returns {Promise<Buffer>}
*/
static async clyde(message) {
if (!message) messgae = "Please provide text!";
await this.__wait()
let avatar = await Canvas.loadImage(await Canvacord.circle(Canvacord.assets("IMAGE").CLYDE));
let badge = await Canvas.loadImage(Canvacord.assets("IMAGE").BOTBADGE);
Canvas.registerFont(Canvacord.assets("FONT").WHITNEY_MEDIUM, {
family: "Whitney",
weight: "regular",
style: "normal"
});
Canvas.registerFont(Canvacord.assets("FONT").MANROPE_REGULAR, {
family: "Manrope",
weight: "regular",
style: "normal"
});
const canvas = Canvas.createCanvas(1500, 300);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#36393E";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(avatar, 75, 30, 130, 130);
ctx.drawImage(badge, 360, 45, 100, 40);
ctx.font = "40px Manrope";
ctx.fillStyle = "#FFFFFF";
ctx.textAlign = "start";
await Util.renderEmoji(ctx, Util.shorten(message, 66), 230, 150);
ctx.font = "50px Whitney";
ctx.fillStyle = "#FFFFFF";
ctx.textAlign = "start";
ctx.fillText("Clyde", 230, 80);
ctx.font = "40px Whitney";
ctx.fillStyle = "#7D7D7D";
ctx.textAlign = "start";
ctx.fillText(Util.discordTime(), 470, 80);
ctx.font = "20px Manrope";
ctx.fillStyle = "#7D7D7D";
ctx.textAlign = "start";
ctx.fillText("Only you can see this —", 240, 190);
ctx.font = "20px Manrope";
ctx.fillStyle = "#2785C7";
ctx.textAlign = "start";
ctx.fillText("delete this message.", 240 + ctx.measureText("Only you can see this —").width + 10, 190);
return canvas.toBuffer();
}
/**
* Fake Quote
* @param {object} options Options
* @param {Buffer|string} [options.image] Image
* @param {string} [options.message] Message
* @param {string} [options.username] Username
* @param {string} [options.color] Color
* @returns {Promise<Buffer>}
*/
static async quote(options = { image, message, username, color }) {
await this.__wait();
if (!options.image) options.image = Canvacord.assets("IMAGE").CLYDE;
if (!options.message) options.message = "Please provide text!";
if (!options.username) options.username = "Clyde";
if (!options.color) options.color = "#FFFFFF";
let image = await Canvas.loadImage(await Canvacord.circle(options.image));
Canvas.registerFont(Canvacord.assets("FONT").WHITNEY_MEDIUM, {
family: "Whitney",
weight: "regular",
style: "normal"
});
Canvas.registerFont(Canvacord.assets("FONT").MANROPE_REGULAR, {
family: "Manrope",
weight: "regular",
style: "normal"
});
const canvas = Canvas.createCanvas(1500, 300);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#36393E";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 75, 30, 130, 130);
ctx.font = "40px Manrope";
ctx.fillStyle = "#FFFFFF";
ctx.textAlign = "start";
await Util.renderEmoji(ctx, Util.shorten(options.message, 66), 230, 150);
ctx.font = "50px Whitney";
ctx.fillStyle = typeof options.color == "string" ? options.color : "#FFFFFF";
ctx.textAlign = "start";
ctx.fillText(typeof options.username === "string" ? Util.shorten(options.username, 17) : "Clyde", 230, 80);
ctx.font = "40px Whitney";
ctx.fillStyle = "#7D7D7D";
ctx.textAlign = "start";
ctx.fillText(Util.discordTime(), 240 + ctx.measureText(Util.shorten(options.username, 17)).width + 110, 80);
return canvas.toBuffer();
}
/**
* PornHub Comment
* @param {Object} options Options
* @param {String} [options.username] Username
* @param {String} [options.message] Comment
* @param {String|Buffer} [options.image] Image
* @returns {Promise<Buffer>}
*/
static async phub(options = { username: null, message: null, image: null }) {
if (!options.username) throw new Error("Username may not be empty!");
if (!options.message) throw new Error("Message may not be empty!");
if (!options.image) throw new Error("Image may not be empty!");
await this.__wait();
let image = await Canvas.loadImage(options.image);
let baseImage = await Canvas.loadImage(Canvacord.assets("IMAGE").PHUB);
let canvas = Canvas.createCanvas(baseImage.width, baseImage.height);
let ctx = canvas.getContext("2d");
ctx.drawImage(baseImage, 0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 30, 310, 70, 70);
ctx.font = "32px Arial";
ctx.fillStyle = "#F99600";
ctx.textAlign = "start";
ctx.fillText(Util.shorten(options.username, 20), 115, 350);
ctx.font = "32px Arial";
ctx.fillStyle = "#CCCCCC";
ctx.textAlign = "start";
await Util.renderEmoji(ctx, Util.shorten(options.message, 50), 30, 430);
return canvas.toBuffer();
}
/**
* Wanted
* @param {string|Buffer} image Source image
* @returns {Promise<Buffer>}
*/
static async wanted(image) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(image);
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").WANTED);
const canvas = Canvas.createCanvas(bg.width, bg.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 145, 282, 447, 447);
return canvas.toBuffer();
}
/**
* Wasted
* @param {string|Buffer} image Source image
* @returns {Promise<Buffer>}
*/
static async wasted(image) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(await Canvacord.greyscale(image));
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").WASTED);
const canvas = Canvas.createCanvas(512, 512);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
return canvas.toBuffer();
}
/**
* YouTube comment
* @param {object} ops YouTube comment options
* @param {string} [ops.username] Comment author username
* @param {string} [ops.content] The comment
* @param {string|Buffer} [ops.avatar] Avatar source
* @param {boolean} [ops.dark=false] Dark mode?
* @returns {Promise<Buffer>}
*/
static async youtube(ops = { username: null, content: null, avatar: null, dark: false }) {
if (!ops.username || typeof ops.username !== "string") throw new Error("Username may not be empty!");
if (!ops.content || typeof ops.content !== "string") throw new Error("Content may not be empty!");
if (!ops.avatar) throw new Error("Avatar source may not be empty!");
ops.dark = !!ops.dark;
await this.__wait();
const bg = await Canvas.loadImage(!ops.dark ? Canvacord.assets("IMAGE").YOUTUBE : await Canvacord.invert(Canvacord.assets("IMAGE").YOUTUBE));
const avatar = await Canvas.loadImage(await Canvacord.circle(ops.avatar));
const canvas = Canvas.createCanvas(bg.width, bg.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, -3, -3, canvas.width + 6, canvas.height + 6);
ctx.drawImage(avatar, 17, 33, 52, 52);
let time = Math.floor(Math.random() * (59 - 1)) + 1;
time = `${time + (time == 1 ? " minute" : " minutes")} ago`;
const username = Util.shorten(ops.username, 21);
const comment = Util.shorten(ops.content, 60);
ctx.font = "20px Roboto";
ctx.fillStyle = ops.dark ? "#FFFFFF" : "#000000";
ctx.fillText(username, 92, 50);
ctx.font = "16px Roboto";
ctx.fillStyle = "#909090";
ctx.fillText(time, ctx.measureText(username).width + 140, 50);
ctx.font = "18px Roboto";
ctx.fillStyle = ops.dark ? "#FFFFFF" : "#000000";
await Util.renderEmoji(ctx, comment, 92, 80);
return canvas.toBuffer();
}
/**
* Oh Shit!
* @param {string|Buffer} image Source image
* @returns {Promise<Buffer>}
*/
static async shit(image) {
if (!image) throw new Error("image was not provided!");
await this.__wait();
const img = await Canvas.loadImage(await Canvacord.circle(image));
const bg = await Canvas.loadImage(Canvacord.assets("IMAGE").SHIT);
const canvas = Canvas.createCanvas(bg.width, bg.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 210, 700, 170, 170);
return canvas.toBuffer();
}
/**
* Writes the data as file
* @param {Buffer} data data to write
* @param {string} name file name
* @returns {void}
*/
static write(data, name) {
return fs.writeFileSync(name, data);
}
/**
* Returns default icon of a discord server
* @param {string} name Guild name
* @param {number} size Icon size. Valid: `16`, `32`, `64`, `128`, `256`, `512`, `1024`, `2048` & `4096`
* @returns {Promise<Buffer>}
*/
static async guildIcon(name, size = 1024) {
const str = Util.getAcronym(name);
if (!str) throw new Error("Couldn't parse acronym!");
if (typeof size !== "number" || size < 0 || size > 4096 || size % 16 !== 0) throw new Error("Invalid icon size!");
const canvas = Canvas.createCanvas(size, size);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#7289DA";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFFFF";
ctx.font = `bold ${size / 4}px Whitney`;
await Util.renderEmoji(ctx, str, canvas.width / 4, canvas.height / 1.7);
return canvas.toBuffer();
}
/**
* Discord Reply Clone
* @param {object} options Options
* @param {string|Buffer} [options.avatar1] Avatar of the person who replied
* @param {string|Buffer} [options.avatar2] Avatar of the other person
* @param {string} [options.user1] Username of the person who replied
* @param {string} [options.user2] Username of the other person
* @param {string} [options.hex1] Hex color of the person who replied
* @param {string} [options.hex2] Hex color of the other person
* @param {string} [options.mainText] The message
* @param {string} [options.replyText] The reply message
* @returns {Promise<Buffer>}
* @example const img = "https://cdn.discordapp.com/embed/avatars/0.png";
* const img2 = "https://cdn.discordapp.com/embed/avatars/4.png";
* canvacord.Canvas.reply({
* avatar1: img,
* avatar2: img2,
* user1: "Maximus",
* user2: "Snowflake",
* hex1: "#FF3300",
* hex2: "#7289da",
* mainText: "kok",
* replyText: "Pog"
* })
* .then(img => canvacord.write(img, "reply.png"));
*/
static async reply(options = { avatar1: null, avatar2: null, user1: null, user2: null, hex1: null, hex2: null, mainText: null, replyText: null }) {
const { avatar1, avatar2, user1, user2, hex1, hex2, mainText, replyText } = options;
if (!avatar1) throw new Error("First avatar was not provided!");
if (!avatar2) throw new Error("Second avatar was not provided!");
if (!user1) throw new Error("First username was not provided!");
if (!user2) throw new Error("Second username was not provided!");
if (!mainText || typeof mainText !== "string") throw new Error("Main text was not provided!");
if (!replyText || typeof replyText !== "string") throw new Error("Reply text was not provided!");
if (!hex1 || typeof hex1 !== "string") hex1 = "#FFFFFF";
if (!hex2 || typeof hex2 !== "string") hex2 = "#FFFFFF";
const img1 = await Canvas.loadImage(avatar1);
const img2 = await Canvas.loadImage(avatar2);
Canvas.registerFont(Canvacord.assets("FONT").WHITNEY_MEDIUM, {
family: "Whitney",
weight: "regular",
style: "normal"
});
Canvas.registerFont(Canvacord.assets("FONT").MANROPE_REGULAR, {
family: "Manrope",
weight: "regular",
style: "normal"
});
const canvas = Canvas.createCanvas(1300, 250);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#36393E";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#ffffff";
ctx.textAlign = "left";
ctx.font = "38px Manrope";
await Util.renderEmoji(ctx, Util.shorten(replyText, 32), 186, 200);
ctx.font = "38px Whitney";
ctx.fillStyle = Util.formatHex(hex1, "#FFFFFF");
ctx.fillText(user1, 185, 147);
const usernameWidth = ctx.measureText(user1).width;
ctx.fillStyle = "#d1d1d1";
ctx.font = "38px Manrope";
ctx.fillText(" replied to ", 165 + usernameWidth + 20, 147);
const repliedWidth = ctx.measureText(" replied to ").width;
ctx.fillStyle = Util.formatHex(hex2, "#FFFFFF");
ctx.font = "38px Whitney";
ctx.fillText(user2, 165 + usernameWidth + repliedWidth + 20, 167 - 20);
const secondMemberUserWidth = ctx.measureText(user2).width;
ctx.font = "26px Whitney";
ctx.fillStyle = "#7a7c80";
const time = Util.discordTime();
ctx.fillText(` ${time}`, 165 + usernameWidth + repliedWidth + secondMemberUserWidth + 3 + 20, 167 - 20)
ctx.font = "29px Whitney";
ctx.globalAlpha = 0.7;
ctx.fillStyle = "#d1d1d1";
ctx.fillText(Util.shorten(mainText, 64), 195 + 20 + 20, 100 + 5 - 20);
ctx.strokeStyle = "#a3a2a2";
ctx.lineWidth = 4;
ctx.globalAlpha = 0.4;
ctx.moveTo(34 + (105 / 2) + 70 + 20, 92 + 5 - 20);
ctx.lineTo(34 + (105 / 2) + 20, 92 + 5 - 20);
ctx.moveTo(34 + (105 / 2) + 20, 92 + 5 - 20);
ctx.quadraticCurveTo(34 + (105 / 2) + 4, 92 + 5 - 20, 34 + (105 / 2), 103 + 5 - 20);
ctx.moveTo(34 + (105 / 2), 125 - 20);
ctx.lineTo(34 + (105 / 2), 103 + 5 - 20);
ctx.stroke();
ctx.globalAlpha = 1;
ctx.save();
ctx.beginPath();
ctx.lineWidth = 1;
ctx.arc(90, 182 - 20, 50, 0, Math.PI * 2, true);
ctx.strokeStyle = "#36393E";
ctx.stroke();