├── .github └── FUNDING.yml ├── LICENSE ├── index.js ├── package.json ├── readme.md ├── src ├── Canvas.js ├── CanvasUtil.js └── assets │ ├── affect.png │ ├── batslap.png │ ├── beautiful.png │ ├── bed.png │ ├── bold-font.ttf │ ├── delete.png │ ├── facepalm.png │ ├── fonts │ ├── Whitney-Book.ttf │ ├── Whitney-medium.otf │ ├── bold-font.ttf │ └── regular-font.ttf │ ├── gay.png │ ├── hitler.png │ ├── images │ ├── affect.png │ ├── batslap.png │ ├── beautiful.png │ ├── bed.png │ ├── changemymind.jpg │ ├── delete.png │ ├── distracted.jpg │ ├── dnd.png │ ├── facepalm.png │ ├── gay.png │ ├── hitler.png │ ├── idle.png │ ├── jail.png │ ├── jokeoverhead.png │ ├── kiss.png │ ├── offline.png │ ├── online.png │ ├── phub.png │ ├── rankcard.png │ ├── rankcard2.png │ ├── rankcard3.png │ ├── rip.png │ ├── shit.png │ ├── spank.png │ ├── trash.png │ ├── triggered.png │ ├── twitter.png │ ├── wanted.png │ ├── wasted.png │ ├── welcomebg.png │ └── youtube.png │ ├── jail.png │ ├── jokeoverhead.png │ ├── kiss.png │ ├── rankcard.png │ ├── rankcard2.png │ ├── regular-font.ttf │ ├── rip.png │ ├── spank.png │ ├── trash.png │ ├── triggered.png │ ├── wanted.png │ ├── wasted.png │ └── welcomebg.png └── test ├── changemymind.png ├── clyde.png ├── color.png ├── deepfried.png ├── image.png ├── index.js ├── rank-custom-bg-and-color.png ├── rank-custom-bg-no-overlay.png ├── rank-custom-bg.png ├── rank-custom-color-bg-no-overlay.png ├── rank-custom-color.png ├── rank-default.png └── triggered.gif /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: 666fern666 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://mrfernoff.diaka.ua/donate','https://steamcommunity.com/tradeoffer/new/?partner=1251415311&token=h2fp5RJY'] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 YuriProject 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./src/Canvas"); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yuri-canvas", 3 | "version": "4.0.6", 4 | "description": "Simple & easy to use image manipulation module for discord bots with a lot of features like rank card, memes & more.", 5 | "main": "index.js", 6 | "scripts": { 7 | "docsgen": "jsdoc --configure .jsdoc.json --verbose", 8 | "test": "cd test && node index.js", 9 | "format": "prettier --write \"src/**/*.js\"" 10 | }, 11 | "homepage": "https://canvacord.snowflakedev.xyz", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/yuri-project-ml/yuri-canvas" 15 | }, 16 | "keywords": [ 17 | "YuriCanvas", 18 | "discord-canvas", 19 | "discord.js-canvas", 20 | "discord.js-image", 21 | "memes", 22 | "rank", 23 | "rank-card", 24 | "welcomer", 25 | "leaver", 26 | "dank-memer", 27 | "imagen", 28 | "imgen", 29 | "imagegen", 30 | "canvas", 31 | "jimp", 32 | "api" 33 | ], 34 | "author": "Yuri-Project", 35 | "license": "ISC", 36 | "dependencies": { 37 | "@jimp/custom": "^x", 38 | "@jimp/plugin-circle": "^x", 39 | "@jimp/plugin-resize": "^x", 40 | "@jimp/plugin-color": "^x", 41 | "canvas": "^2.6.1", 42 | "gifencoder": "^2.0.1", 43 | "jimp": "^x", 44 | "moment": "^2.27.0", 45 | "moment-duration-format": "^2.3.2", 46 | "node-canvas-with-twemoji-and-discord-emoji": "^1.1.4" 47 | }, 48 | "devDependencies": { 49 | "jsdoc": "^3.6.5", 50 | "jsdoc-skyceil": "^1.0.5", 51 | "prettier": "^2.0.5" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Yuri-Canvas 2 | yuricanvas is a wrapper for canvas & jimp which can be used to create/manipulate images easily. 3 | This package is meant for beginners who don't know how to use canvas & stuffs. 4 | 5 | > ⚠ This package is not recommended to you if you know how to use canvas/other image manipulation tools. 6 | 7 | # Installing 8 | 9 | ```bash 10 | npm i --save yuri-canvas 11 | ``` 12 | 13 | # Features 14 | - Beginner friendly 15 | - Supports Buffer or image url 16 | - Super fast image manipulation 17 | - Welcomer and leaver images 18 | - Rank card 19 | - and more... 20 | 21 | # Limitations 22 | - You can only create stuffs using the mentioned functions. 23 | - You cannot create super-fancy things 24 | - If you want to go deeper, you must learn canvas 25 | 26 | # Methods 27 | **[All The Methods are listed here](https://yuricanvas.snowflakedev.xyz/)** 28 | 29 | # Example 30 | 31 | ```js 32 | 33 | const yuricanvas = require("yuri-canvas"); 34 | 35 | async function create() { 36 | let img = await yuricanvas.trigger("./image.png"); 37 | yuricanvas.write(img, "triggered.gif"); 38 | 39 | let color = await yuricanvas.color("#4E5D94"); 40 | yuricanvas.write(color, "color.png"); 41 | } 42 | 43 | create(); 44 | 45 | ``` 46 | 47 | # Discord.js v13 Example 48 | 49 | ```js 50 | const { Client, Intents, MessageAttachment } = require("discord.js"); 51 | const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILDS_MESSAGES] }); 52 | const yuricanvas = require("yuri-canvas"); 53 | 54 | client.on("ready", () => { 55 | console.log("I'm online!"); 56 | }); 57 | 58 | client.on("messageCreate", async (message) => { 59 | if (message.author.bot) return; 60 | if (message.content === "!trigger") { 61 | let avatar = message.author.displayAvatarURL({ format: 'png' }); 62 | let image = await yuricanvas.trigger(avatar); 63 | let attachment = new MessageAttachment(image, "triggered.gif"); 64 | return message.channel.send({ files: [attachment] }); 65 | } 66 | if (message.content === "!delete") { 67 | let avatar = message.author.displayAvatarURL({ format: 'png' }); 68 | let image = await yuricanvas.delete(avatar); 69 | let attachment = new MessageAttachment(image, "deleted.png"); 70 | return message.channel.send({ files: [attachment] }); 71 | } 72 | if (message.content === "!rank") { 73 | let rank = getRankSomehow(); 74 | let image = await yuricanvas.rank({ 75 | username, 76 | discrim, 77 | level: rank.level, 78 | rank: rank.rank, 79 | neededXP: rank.neededXP, 80 | currentXP: rank.currentXP, 81 | avatarURL: message.author.displayAvatarURL({ format: "png" }), 82 | color: "white", 83 | background: "https://link-to/superDuperBackground" 84 | }); 85 | let attachment = new MessageAttachment(image, "rank.png"); 86 | return message.channel.send({ files: [attachment] }); 87 | } 88 | }); 89 | 90 | client.login("Your_Bot_Token_here"); 91 | 92 | ``` 93 | 94 | # Documentation 95 | **[https://canvacord.snowflakedev.xyz](https://canvacord.snowflakedev.xyz)** 96 | 97 | # Previews: 98 | 99 | # Change My Mind 100 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/changemymind.png) 101 | 102 | # Rank Cards 103 | ## Default 104 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/rank-default.png) 105 | 106 | ## Custom Background & Color 107 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/rank-custom-bg-and-color.png) 108 | 109 | ## Custom Background & No Overlay 110 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/rank-custom-bg-no-overlay.png) 111 | 112 | ## Custom Background 113 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/rank-custom-bg.png) 114 | 115 | ## Custom Background, Color & No Overlay 116 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/rank-custom-color-bg-no-overlay.png) 117 | 118 | ## Custom Color 119 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/rank-custom-color.png) 120 | 121 | # Triggered 122 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/triggered.gif) 123 | 124 | # Color 125 | ![img](https://raw.githubusercontent.com/yuri-project-ml/yuri-canvas/master/test/color.png) 126 | 127 | > ### Read the docs for more endpoints 128 | 129 | # Join Our Discord Server 130 | **[discord.gg/crnmuSD](https://discord.gg/crnmuSD)** 131 | -------------------------------------------------------------------------------- /src/Canvas.js: -------------------------------------------------------------------------------- 1 | const Canvas = require("canvas"); 2 | const jimp = require("jimp"); 3 | const GIFEncoder = require("gifencoder"); 4 | const circle = require("@jimp/plugin-circle"); 5 | const configure = require("@jimp/custom"); 6 | const fs = require("fs"); 7 | const Util = require("./CanvasUtil"); 8 | const statuses = { 9 | dnd: __dirname + "/assets/images/dnd.png", 10 | idle: __dirname + "/assets/images/idle.png", 11 | online: __dirname + "/assets/images/online.png", 12 | offline: __dirname + "/assets/images/offline.png" 13 | }; 14 | 15 | // load custom plugins 16 | configure({ plugins: [circle] }, jimp); 17 | 18 | /** 19 | * YuriCanvas 20 | * Simple and easy to use image manipulation module 21 | * created and maintained by Snowflake107. 22 | */ 23 | class YuriCanvas { 24 | constructor() { 25 | throw new Error(`The class ${this.constructor.name} may not be instantiated!`); 26 | } 27 | 28 | /** 29 | * batslap 30 | * @param {Image1} image1 first image 31 | * @param {Image2} image2 second image 32 | * @returns {Promise} 33 | * @example let img = await canva.batslap(img, img1); 34 | * canva.write(img, "img.png"); 35 | */ 36 | static async batslap(image1, image2) { 37 | if (!image1) throw new Error("first image was not provided!"); 38 | if (!image2) throw new Error("second image was not provided!"); 39 | let base = await jimp.read(__dirname + "/assets/images/batslap.png"); 40 | image1 = await jimp.read(image1); 41 | image2 = await jimp.read(image2); 42 | base.resize(1000, 500); 43 | image1.resize(220, 220); 44 | image2.resize(200, 200); 45 | base.composite(image2, 580, 260); 46 | base.composite(image1, 350, 70); 47 | let raw = await base.getBufferAsync("image/png"); 48 | return raw; 49 | } 50 | 51 | /** 52 | * beautiful 53 | * @param {Image} image image 54 | * @returns {Promise} 55 | * @example let img = await canva.beautiful(img); 56 | * canva.write(img, "img.png"); 57 | */ 58 | static async beautiful(image) { 59 | if (!image) throw new Error("image was not provided!"); 60 | let base = await jimp.read(__dirname + "/assets/images/beautiful.png"); 61 | base.resize(376, 400); 62 | let img = await jimp.read(image); 63 | img.resize(84, 95); 64 | base.composite(img, 258, 28); 65 | base.composite(img, 258, 229); 66 | let raw = await base.getBufferAsync("image/png"); 67 | return raw; 68 | } 69 | 70 | /** 71 | * facepalm 72 | * @param {Image} image image 73 | * @returns {Promise} 74 | * @example let img = await canva.facepalm(img); 75 | * canva.write(img, "img.png"); 76 | */ 77 | static async facepalm(image) { 78 | if (!image) throw new Error("image was not provided!"); 79 | let canvas = Canvas.createCanvas(632, 357); 80 | let ctx = canvas.getContext("2d"); 81 | ctx.fillStyle = "black"; 82 | ctx.fillRect(0, 0, 632, 357); 83 | let avatar = await Canvas.loadImage(image); 84 | ctx.drawImage(avatar, 199, 112, 235, 235); 85 | let layer = await Canvas.loadImage(__dirname + "/assets/images/facepalm.png"); 86 | ctx.drawImage(layer, 0, 0, 632, 357); 87 | return canvas.toBuffer(); 88 | } 89 | 90 | /** 91 | * gay 92 | * @param {Image} image Image 93 | * @returns {Promise} 94 | * @example let img = await canva.gay(img); 95 | * canva.write(img, "img.png"); 96 | */ 97 | static async gay(image) { 98 | if (!image) throw new Error("image was not provided!"); 99 | let bg = await Canvas.loadImage(__dirname + "/assets/images/gay.png"); 100 | let img = await Canvas.loadImage(image); 101 | const canvas = Canvas.createCanvas(400, 400); 102 | const ctx = canvas.getContext("2d"); 103 | ctx.drawImage(img, 0, 0, 400, 400); 104 | ctx.drawImage(bg, 0, 0, 400, 400); 105 | return canvas.toBuffer(); 106 | } 107 | 108 | /** 109 | * kiss 110 | * @param {image1} image1 first image 111 | * @param {image2} image2 second image 112 | * @returns {Promise} 113 | * @example let img = await canva.kiss(img); 114 | * canva.write(img, "img.png"); 115 | */ 116 | static async kiss(image1, image2) { 117 | if (!image1) throw new Error("first image was not provided!"); 118 | if (!image2) throw new Error("second image was not provided!"); 119 | const canvas = Canvas.createCanvas(768, 574); 120 | const ctx = canvas.getContext("2d"); 121 | const background = await Canvas.loadImage(__dirname + "/assets/images/kiss.png"); 122 | ctx.drawImage(background, 0, 0, canvas.width, canvas.height); 123 | const avatar = await Canvas.loadImage(image1); 124 | const avatar1 = await Canvas.loadImage(image2); 125 | ctx.drawImage(avatar1, 370, 25, 200, 200); 126 | ctx.drawImage(avatar, 150, 25, 200, 200); 127 | return canvas.toBuffer(); 128 | } 129 | 130 | /** 131 | * rip 132 | * @param {Image} image Image 133 | * @returns {Promise} 134 | * @example let img = await canva.rip(img); 135 | * canva.write(img, "img.png"); 136 | */ 137 | static async rip(image) { 138 | if (!image) throw new Error("image was not provided!"); 139 | const canvas = Canvas.createCanvas(244, 253); 140 | const ctx = canvas.getContext("2d"); 141 | const background = await Canvas.loadImage(__dirname + "/assets/images/rip.png"); 142 | ctx.drawImage(background, 0, 0, canvas.width, canvas.height); 143 | const avatar = await Canvas.loadImage(image); 144 | ctx.drawImage(avatar, 63, 110, 90, 90); 145 | return canvas.toBuffer(); 146 | } 147 | 148 | /** 149 | * spank 150 | * @param {image1} image1 first image 151 | * @param {image2} image2 second image 152 | * @returns {Promise} 153 | * @example let img = await canva.spank(img, img1); 154 | * canva.write(img, "img.png"); 155 | */ 156 | static async spank(image1, image2) { 157 | if (!image1) throw new Error("first image was not provided!"); 158 | if (!image2) throw new Error("second image was not provided!"); 159 | let bg = await jimp.read(__dirname + "/assets/images/spank.png"); 160 | image1 = await jimp.read(image1); 161 | image2 = await jimp.read(image2); 162 | bg.resize(500, 500); 163 | image1.resize(140, 140); 164 | image2.resize(120, 120); 165 | bg.composite(image2, 350, 220); 166 | bg.composite(image1, 225, 5); 167 | let raw = await bg.getBufferAsync("image/png"); 168 | return raw; 169 | } 170 | 171 | /** 172 | * trash 173 | * @param {Image} image Image 174 | * @returns {Promise} 175 | * @example let img = await canva.trash(img); 176 | * canva.write(img, "img.png"); 177 | */ 178 | static async trash(image) { 179 | if (!image) throw new Error("image was not provided!"); 180 | let bg = await jimp.read(__dirname + "/assets/images/trash.png"); 181 | image = await jimp.read(image); 182 | image.resize(309, 309); 183 | image.blur(5); 184 | bg.composite(image, 309, 0); 185 | let raw = await bg.getBufferAsync("image/png"); 186 | return raw; 187 | } 188 | 189 | /** 190 | * blur 191 | * @param {Image} image Image 192 | * @param {Number} level blur level 193 | * @returns {Promise} 194 | * @example let img = await canva.blur(img); 195 | * canva.write(img, "img.png"); 196 | */ 197 | static async blur(image, level = 5) { 198 | if (!image) throw new Error("image was not provided!"); 199 | image = await jimp.read(image); 200 | image.blur(isNaN(level) ? 5 : parseInt(level)); 201 | let raw = await image.getBufferAsync("image/png"); 202 | return raw; 203 | } 204 | 205 | /** 206 | * greyscale 207 | * @param {Image} image Image 208 | * @returns {Promise} 209 | * @example let img = await canva.greyscale(img); 210 | * canva.write(img, "img.png"); 211 | */ 212 | static async greyscale(image) { 213 | if (!image) throw new Error("image was not provided!"); 214 | image = await jimp.read(image); 215 | image.greyscale(); 216 | let raw = await image.getBufferAsync("image/png"); 217 | return raw; 218 | } 219 | 220 | /** 221 | * sepia 222 | * @param {Image} image Image 223 | * @returns {Promise} 224 | * @example let img = await canva.sepia(img); 225 | * canva.write(img, "img.png"); 226 | */ 227 | static async sepia(image) { 228 | if (!image) throw new Error("image was not provided!"); 229 | image = await jimp.read(image); 230 | image.sepia(); 231 | let raw = await image.getBufferAsync("image/png"); 232 | return raw; 233 | } 234 | 235 | /** 236 | * invert 237 | * @param {Image} image Image 238 | * @returns {Promise} 239 | * @example let img = await canva.invert(img); 240 | * canva.write(img, "img.png"); 241 | */ 242 | static async invert(image) { 243 | if (!image) throw new Error("image was not provided!"); 244 | image = await jimp.read(image); 245 | image.invert(); 246 | let raw = await image.getBufferAsync("image/png"); 247 | return raw; 248 | } 249 | 250 | /** 251 | * delete 252 | * @param {Image} image Image 253 | * @returns {Promise} 254 | * @example let img = await canva.delete(img); 255 | * canva.write(img, "img.png"); 256 | */ 257 | static async delete(image) { 258 | if (!image) throw new Error("image was not provided!"); 259 | let bg = await jimp.read(__dirname + "/assets/images/delete.png"); 260 | image = await jimp.read(image); 261 | image.resize(195, 195); 262 | bg.composite(image, 120, 135); 263 | let raw = await bg.getBufferAsync("image/png"); 264 | return raw; 265 | } 266 | 267 | /** 268 | * color 269 | * @param {Color} color name/hex 270 | * @returns {Promise} 271 | * @example let img = await canva.color("#FF0000"); 272 | * canva.write(img, "img.png"); 273 | */ 274 | static async color(color = "RANDOM") { 275 | const canvas = Canvas.createCanvas(2048, 2048); 276 | const ctx = canvas.getContext("2d"); 277 | ctx.fillStyle = YuriCanvas._getHex(color); 278 | ctx.fillRect(0, 0, canvas.width, canvas.height); 279 | return canvas.toBuffer(); 280 | } 281 | 282 | /** 283 | * Resolves Color 284 | * @param {Color} color HTML5 color 285 | * @returns {Color} 286 | * @example const color = canva._getHex([255,89,56]) 287 | * console.log(color); 288 | * @private 289 | * @ignore 290 | */ 291 | static _getHex(color) { 292 | if (!color) return "#000000"; 293 | if (color === "RANDOM") return "#" + Math.floor(Math.random() * (0xffffff + 1)).toString(16); 294 | if (["dnd", "online", "idle", "offline"].includes(color.toLowerCase())) return status[color.toLowerCase()]; 295 | if (Array.isArray(color)) return "#" + ((color[0] << 16) + (color[1] << 8) + color[2]).toString(16); 296 | if (isNaN(color) && (color.startsWith("#") || color.startsWith("0x"))) return color.replace("0x", "#"); 297 | if (!isNaN(color) && String(color).startsWith("0x")) return String(color).replace("0x", "#"); 298 | if (!isNaN(color)) return `#${color.toString(16)}`; 299 | return color; 300 | } 301 | 302 | /** 303 | * trigger 304 | * @param {Image} image image 305 | * @returns {Promise} 306 | * @example let img = await canva.trigger(img); 307 | * canva.write(img, "img.gif"); 308 | */ 309 | static async trigger(image) { 310 | if (!image) throw new Error("image was not provided!"); 311 | const base = await Canvas.loadImage(__dirname + "/assets/images/triggered.png"); 312 | const img = await Canvas.loadImage(image); 313 | const GIF = new GIFEncoder(256, 310); 314 | GIF.start(); 315 | GIF.setRepeat(0); 316 | GIF.setDelay(15); 317 | const canvas = Canvas.createCanvas(256, 310); 318 | const ctx = canvas.getContext("2d"); 319 | const BR = 20; 320 | const LR = 10; 321 | let i = 0; 322 | while (i < 9) { 323 | ctx.clearRect(0, 0, 256, 310); 324 | ctx.drawImage( 325 | img, 326 | Math.floor(Math.random() * BR) - BR, 327 | Math.floor(Math.random() * BR) - BR, 328 | 256 + BR, 329 | 310 - 54 + BR 330 | ); 331 | ctx.fillStyle = "#FF000033"; 332 | ctx.fillRect(0, 0, 256, 310); 333 | ctx.drawImage( 334 | base, 335 | Math.floor(Math.random() * LR) - LR, 336 | 310 - 54 + Math.floor(Math.random() * LR) - LR, 337 | 256 + LR, 338 | 54 + LR 339 | ); 340 | GIF.addFrame(ctx); 341 | i++; 342 | } 343 | GIF.finish(); 344 | return GIF.out.getData(); 345 | } 346 | 347 | /** 348 | * hitler 349 | * @param {Image} image Image 350 | * @returns {Promise} 351 | * @example let img = await canva.hitler(img); 352 | * canva.write(img, "img.png"); 353 | */ 354 | static async hitler(image) { 355 | if (!image) throw new Error("Image was not provided!"); 356 | let bg = await jimp.read(__dirname + "/assets/images/hitler.png"); 357 | let img = await jimp.read(image); 358 | img.resize(140, 140); 359 | bg.composite(img, 46, 43); 360 | let raw = await bg.getBufferAsync("image/png"); 361 | return raw; 362 | } 363 | 364 | /** 365 | * bed 366 | * @param {image1} image1 first image 367 | * @param {image2} image2 second image 368 | * @returns {Promise} 369 | * @example let img = await canva.bed(img, img1); 370 | * canva.write(img, "img.png"); 371 | */ 372 | static async bed(image1, image2) { 373 | if (!image1) throw new Error("first image was not provided!"); 374 | if (!image2) throw new Error("second image was not provided!"); 375 | let bg = await jimp.read(__dirname + "/assets/images/bed.png"); 376 | image1 = await jimp.read(image1); 377 | image2 = await jimp.read(image2); 378 | image1.resize(100, 100); 379 | image2.resize(70, 70); 380 | let image3 = image1.clone().resize(70, 70); 381 | bg.composite(image1, 25, 100); 382 | bg.composite(image1, 25, 300); 383 | bg.composite(image3, 53, 450); 384 | bg.composite(image2, 53, 575); 385 | let raw = await bg.getBufferAsync("image/png"); 386 | return raw; 387 | } 388 | 389 | /** 390 | * wanted 391 | * @param {image} Image image 392 | * @returns {Promise} 393 | * @example let img = await canva.wanted(img); 394 | * canva.write(img, "img.png"); 395 | */ 396 | static async wanted(image) { 397 | if (!image) throw new Error("no image provided!"); 398 | let base = await jimp.read(__dirname + "/assets/images/wanted.png"); 399 | let img = await jimp.read(image); 400 | img.resize(447, 447); 401 | base.composite(img, 145, 282); 402 | let raw = await base.getBufferAsync("image/png"); 403 | return raw; 404 | } 405 | 406 | /** 407 | * circle 408 | * @param {Image} image Image 409 | * @returns {Promise} 410 | * @example let img = await canva.circle(img); 411 | * canva.write(img, "img.png"); 412 | */ 413 | static async circle(image) { 414 | if (!image) throw new Error("image was not provided!"); 415 | image = await jimp.read(image); 416 | image.circle(); 417 | let raw = await image.getBufferAsync("image/png"); 418 | return raw; 419 | } 420 | 421 | /** 422 | * jail 423 | * @param {image} Image image 424 | * @returns {Promise} 425 | * @example let img = await canva.jail(img); 426 | * canva.write(img, "img.png"); 427 | */ 428 | static async jail(image) { 429 | if (!image) throw new Error("no image provided!"); 430 | let canvas = Canvas.createCanvas(350, 350); 431 | let ctx = canvas.getContext("2d"); 432 | ctx.fillStyle = "black"; 433 | ctx.fillRect(0, 0, 350, 350); 434 | let avatar = await Canvas.loadImage(image); 435 | ctx.drawImage(avatar, 0, 0, 350, 350); 436 | let layer = await Canvas.loadImage(__dirname + "/assets/images/jail.png"); 437 | ctx.drawImage(layer, 0, 0, 350, 350); 438 | return canvas.toBuffer(); 439 | } 440 | 441 | /** 442 | * affect 443 | * @param {image} Image image 444 | * @returns {Promise} 445 | * @example let img = await canva.affect(img); 446 | * canva.write(img, "img.png"); 447 | */ 448 | static async affect(image) { 449 | if (!image) throw new Error("no image provided!"); 450 | let base = await jimp.read(__dirname + "/assets/images/affect.png"); 451 | let img = await jimp.read(image); 452 | img.resize(200, 157); 453 | base.composite(img, 180, 383); 454 | let raw = await base.getBufferAsync("image/png"); 455 | return raw; 456 | } 457 | 458 | /** 459 | * dither 460 | * @param {Image} image Image 461 | * @returns {Promise} 462 | * @example let img = await canva.dither(img); 463 | * canva.write(img, "img.png"); 464 | */ 465 | static async dither(image) { 466 | if (!image) throw new Error("image was not provided!"); 467 | image = await jimp.read(image); 468 | image.dither565(); 469 | let raw = await image.getBufferAsync("image/png"); 470 | return raw; 471 | } 472 | 473 | /** 474 | * Resize an image 475 | * @param {string|Buffer} image Image source 476 | * @param {number} width width 477 | * @param {number} height height 478 | * @returns {Promise} 479 | */ 480 | static async resizes(image, width, height) { 481 | if (!image) throw new Error("Image was not provided!"); 482 | const img = await Canvas.loadImage(image); 483 | const w = width && !isNaN(width) ? width : img.width; 484 | const h = height && !isNaN(height) ? width : img.height; 485 | const canvas = await Canvas.createCanvas(w, h); 486 | const ctx = canvas.getContext("2d"); 487 | ctx.drawImage(img, 0, 0, canvas.width, canvas.height); 488 | return canvas.toBuffer(); 489 | } 490 | 491 | /** 492 | * wasted 493 | * @param {Buffer} Image Image to manipulate 494 | * @returns {Promise} 495 | * @example let img = await canva.wasted(img); 496 | * canva.write(img, "img.png"); 497 | */ 498 | static async wasted(Image) { 499 | let converted = await YuriCanvas.greyscale(Image); 500 | const canvas = Canvas.createCanvas(500, 500); 501 | const ctx = canvas.getContext("2d"); 502 | const base = await Canvas.loadImage(__dirname + "/assets/images/wasted.png"); 503 | const img = await Canvas.loadImage(converted); 504 | ctx.drawImage(img, 0, 0, 500, 500); 505 | ctx.drawImage(base, 0, 0, 500, 500); 506 | return canvas.toBuffer(); 507 | } 508 | 509 | /** 510 | * Reads the image 511 | * @param {Buffer|String} image buffer or string to read image from. 512 | * @returns {Promise} 513 | */ 514 | static async read(image) { 515 | if (!image) throw new Error("No image provided!"); 516 | let i = await Canvas.loadImage(image); 517 | return i; 518 | } 519 | 520 | /** 521 | * rank card 522 | * @param {String} username Username 523 | * @param {String} discrim Discriminator 524 | * @param {String} level User level 525 | * @param {String} rank User rank 526 | * @param {String} neededXP XP needed to reach next level 527 | * @param {String} currentXP Current XP of a user 528 | * @param {Buffer|String} avatarURL Avatar URL or {Buffer} or YuriCanvas {Buffer} itself 529 | * @param {String} color Hex or HTML5 color name or rgb 530 | * @param {String|Buffer} background Rank card background image 531 | * @param {Boolean} overlay Keep overlay or not 532 | * @param {String} status The user status. Must be one of online, idle, offline or dnd 533 | * @param {Array} gradient Creates the gradient filled progress bar. Color will be ignored! 534 | * @returns {Promise} 535 | * @example let img = await canva.rank({ username: "Snowflake", discrim: "0007", level: 4, rank: 12, neededXP: 500, currentXP: 407, avatarURL: "...", color: "#FFFFFF" }); 536 | * canva.write(img, "img.png"); 537 | */ 538 | static async rank( 539 | options = { 540 | username, 541 | discrim, 542 | level, 543 | rank, 544 | neededXP, 545 | currentXP, 546 | avatarURL, 547 | color: "#FFFFFF", 548 | background, 549 | overlay: true, 550 | status: "online", 551 | gradient: [] 552 | } 553 | ) { 554 | if (!options.username) throw new Error("No username was provided!"); 555 | if (!options.level) throw new Error("No level was provided!"); 556 | if (!options.neededXP) throw new Error("No totalXP was provided!"); 557 | if (!options.currentXP) throw new Error("No currentXP was provided!"); 558 | if (!options.avatarURL) throw new Error("No avatarURL was provided!"); 559 | if (!options.color || typeof options.color !== "string") options.color = "#FFFFFF"; 560 | if (options.overlay !== false) options.overlay = true; 561 | if (!options.status) options.status = "online"; 562 | if ( 563 | typeof options.status !== "string" || 564 | !["online", "offline", "idle", "dnd"].includes(options.status.toLowerCase()) 565 | ) 566 | throw new Error("Status must be one of online, idle, dnd or offline."); 567 | let { 568 | username, 569 | discrim, 570 | level, 571 | rank, 572 | neededXP, 573 | currentXP, 574 | avatarURL, 575 | color, 576 | background, 577 | overlay, 578 | status, 579 | gradient 580 | } = options; 581 | 582 | Canvas.registerFont(__dirname + "/assets/fonts/regular-font.ttf", { 583 | family: "Manrope", 584 | weight: "regular", 585 | style: "normal" 586 | }); 587 | Canvas.registerFont(__dirname + "/assets/fonts/bold-font.ttf", { 588 | family: "Manrope", 589 | weight: "bold", 590 | style: "normal" 591 | }); 592 | const canvas = Canvas.createCanvas(934, 282); 593 | const ctx = canvas.getContext("2d"); 594 | let bg; 595 | let rankCard; 596 | if ((overlay && typeof background === "string") || Buffer.isBuffer(background)) { 597 | bg = await Canvas.loadImage(background); 598 | ctx.drawImage(bg, 0, 0, canvas.width, canvas.height); 599 | rankCard = await Canvas.loadImage(__dirname + "/assets/images/rankcard2.png"); 600 | } else if (!overlay && (typeof background === "string" || Buffer.isBuffer(background))) { 601 | bg = await Canvas.loadImage(background); 602 | ctx.drawImage(bg, 0, 0, canvas.width, canvas.height); 603 | rankCard = await Canvas.loadImage(__dirname + "/assets/images/rankcard3.png"); 604 | } else rankCard = await Canvas.loadImage(__dirname + "/assets/images/rankcard.png"); 605 | ctx.drawImage(rankCard, 0, 0, canvas.width, canvas.height); 606 | 607 | const avatar = await Canvas.loadImage(await YuriCanvas.circle(avatarURL)); 608 | ctx.drawImage(avatar, 70, 50, 180, 180); 609 | 610 | let i = await Canvas.loadImage(await YuriCanvas.circle(statuses[status.toLowerCase() || "online"])); 611 | ctx.drawImage(i, 200, 185, 40, 40); 612 | 613 | const font = "Manrope"; 614 | 615 | ctx.font = `bold 36px ${font}`; 616 | ctx.fillStyle = color || "#FFFFFF"; 617 | ctx.textAlign = "start"; 618 | const name = username.length > 12 ? username.substring(0, 12).trim() + "..." : username; 619 | ctx.fillText(`${name}`, 280, 164); 620 | ctx.font = `36px ${font}`; 621 | ctx.fillStyle = "rgba(255, 255, 255, 0.4)"; 622 | ctx.textAlign = "center"; 623 | if (discrim) ctx.fillText(`#${discrim}`, ctx.measureText(name).width + 20 + 335, 164); 624 | 625 | ctx.font = `bold 36px ${font}`; 626 | ctx.fillStyle = color || "#FFFFFF"; 627 | ctx.textAlign = "end"; 628 | ctx.fillText(level, 934 - 64, 82); 629 | ctx.fillStyle = color || "#FFFFFF"; 630 | ctx.fillText("LEVEL", 934 - 64 - ctx.measureText(level).width - 16, 82); 631 | 632 | if(rank) { 633 | ctx.font = `bold 36px ${font}`; 634 | ctx.fillStyle = color || "#FFFFFF"; 635 | ctx.textAlign = "end"; 636 | ctx.fillText(rank, 934 - 64 - ctx.measureText(level).width - 16 - ctx.measureText(`LEVEL`).width - 16, 82); 637 | ctx.fillStyle = color || "#FFFFFF"; 638 | ctx.fillText( 639 | "RANK", 640 | 934 - 641 | 64 - 642 | ctx.measureText(level).width - 643 | 16 - 644 | ctx.measureText(`LEVEL`).width - 645 | 16 - 646 | ctx.measureText(rank).width - 647 | 16, 648 | 82 649 | ); 650 | }; 651 | 652 | ctx.font = `bold 36px ${font}`; 653 | ctx.fillStyle = color || "#FFFFFF"; 654 | ctx.textAlign = "start"; 655 | ctx.fillText("/ " + Util.toAbbrev(neededXP), 670 + ctx.measureText(Util.toAbbrev(currentXP)).width + 15, 164); 656 | ctx.fillStyle = color || "#FFFFFF"; 657 | ctx.fillText(Util.toAbbrev(currentXP), 670, 164); 658 | 659 | let widthXP = (currentXP * 615) / neededXP; 660 | if (widthXP > 615 - 18.5) widthXP = 615 - 18.5; 661 | 662 | ctx.beginPath(); 663 | ctx.fillStyle = "#424751"; 664 | ctx.arc(257 + 18.5, 147.5 + 18.5 + 36.25, 18.5, 1.5 * Math.PI, 0.5 * Math.PI, true); 665 | ctx.fill(); 666 | ctx.fillRect(257 + 18.5, 147.5 + 36.25, 615 - 18.5, 37.5); 667 | ctx.arc(257 + 615, 147.5 + 18.5 + 36.25, 18.75, 1.5 * Math.PI, 0.5 * Math.PI, false); 668 | ctx.fill(); 669 | 670 | ctx.beginPath(); 671 | if (Array.isArray(gradient) && gradient.length > 0) { 672 | gradient.length = 2; 673 | let gradientContext = ctx.createRadialGradient(widthXP, 0, 500, 0); 674 | gradient.forEach((i) => { 675 | gradientContext.addColorStop(gradient.indexOf(i), i); 676 | }); 677 | ctx.fillStyle = gradientContext; 678 | } else { 679 | ctx.fillStyle = color; 680 | } 681 | ctx.arc(257 + 18.5, 147.5 + 18.5 + 36.25, 18.5, 1.5 * Math.PI, 0.5 * Math.PI, true); 682 | ctx.fill(); 683 | ctx.fillRect(257 + 18.5, 147.5 + 36.25, widthXP, 37.5); 684 | ctx.arc(257 + 18.5 + widthXP, 147.5 + 18.5 + 36.25, 18.75, 1.5 * Math.PI, 0.5 * Math.PI, false); 685 | ctx.fill(); 686 | 687 | return canvas.toBuffer(); 688 | } 689 | 690 | /** 691 | * welcome 692 | * @param {String} username Username 693 | * @param {String} discrim Discriminator 694 | * @param {String} avatarURL Avatar URL or {Buffer} or YuriCanvas {Buffer} itself 695 | * @param {String} color Hex or HTML5 color name or rgb 696 | * @returns {Promise} 697 | * @example let img = await canva.welcome({ username: "Snowflake", discrim: "0007", avatarURL: "..." }); 698 | * canva.write(img, "img.png"); 699 | */ 700 | static async welcome({ username, discrim, avatarURL }) { 701 | if (!username) throw new Error("No username was provided!"); 702 | if (!discrim) throw new Error("No discrim was provided!"); 703 | if (!avatarURL) throw new Error("No avatarURL was provided!"); 704 | 705 | Canvas.registerFont(__dirname + "/assets/fonts/regular-font.ttf", { 706 | family: "Manrope", 707 | weight: "regular", 708 | style: "normal" 709 | }); 710 | Canvas.registerFont(__dirname + "/assets/fonts/bold-font.ttf", { 711 | family: "Manrope", 712 | weight: "bold", 713 | style: "normal" 714 | }); 715 | 716 | const canvas = Canvas.createCanvas(700, 250); 717 | const ctx = canvas.getContext("2d"); 718 | 719 | const background = await Canvas.loadImage(__dirname + "/assets/images/welcomebg.png"); 720 | ctx.drawImage(background, 0, 0, canvas.width, canvas.height); 721 | 722 | const font = "Manrope"; 723 | 724 | ctx.font = `20px ${font}`; 725 | ctx.fillStyle = "#ffffff"; 726 | ctx.textAlign = "start"; 727 | ctx.shadowBlur = 10; 728 | ctx.shadowColor = "black"; 729 | ctx.fillText("Welcome", 260, 100); 730 | 731 | const welcometextPosition = { width: 260, height: 150 }; 732 | 733 | let fontSize = 55; 734 | ctx.font = `bold ${fontSize}px ${font}`; 735 | 736 | do { 737 | fontSize -= 1; 738 | ctx.font = `bold ${fontSize}px ${font}`; 739 | } while (ctx.measureText(`${username}#${discrim}!`).width > 430); 740 | 741 | ctx.fillStyle = "#ffffff"; 742 | ctx.textAlign = "start"; 743 | ctx.fillText(`${username}`, welcometextPosition.width, welcometextPosition.height, 455); 744 | 745 | ctx.fillStyle = "rgba(255, 255, 255, 0.5)"; 746 | ctx.textAlign = "start"; 747 | ctx.fillText( 748 | `#${discrim}!`, 749 | ctx.measureText(`${username}`).width + welcometextPosition.width, 750 | welcometextPosition.height 751 | ); 752 | 753 | ctx.shadowBlur = 0; 754 | ctx.beginPath(); 755 | ctx.arc(125, 125, 100, 0, Math.PI * 2, true); 756 | ctx.closePath(); 757 | ctx.clip(); 758 | 759 | const avatar = await Canvas.loadImage(avatarURL); 760 | ctx.drawImage(avatar, 25, 25, 200, 200); 761 | 762 | return canvas.toBuffer(); 763 | } 764 | 765 | /** 766 | * welcome 767 | * @param {String} username Username 768 | * @param {String} discrim Discriminator 769 | * @param {String} avatarURL Avatar URL or {Buffer} or YuriCanvas {Buffer} itself 770 | * @param {String} color Hex or HTML5 color name or rgb 771 | * @returns {Promise} 772 | * @example let img = await canva.welcomer({ username: "Snowflake", discrim: "0007", avatarURL: "..." }); 773 | * canva.write(img, "img.png"); 774 | */ 775 | static async welcomer(...options) { 776 | return YuriCanvas.welcome(...options); 777 | } 778 | 779 | /** 780 | * leaver 781 | * @param {String} username Username 782 | * @param {String} discrim Discriminator 783 | * @param {String} avatarURL Avatar URL or {Buffer} or YuriCanvas {Buffer} itself 784 | * @param {String} color Hex or HTML5 color name or rgb 785 | * @returns {Promise} 786 | * @example let img = await canva.leaver({ username: "Snowflake", discrim: "0007", avatarURL: "..." }); 787 | * canva.write(img, "img.png"); 788 | */ 789 | static async leaver({ username, discrim, avatarURL, color }) { 790 | if (!username) throw new Error("No username was provided!"); 791 | if (!discrim) throw new Error("No discrim was provided!"); 792 | if (!avatarURL) throw new Error("No avatarURL was provided!"); 793 | 794 | Canvas.registerFont(__dirname + "/assets/fonts/regular-font.ttf", { 795 | family: "Manrope", 796 | weight: "regular", 797 | style: "normal" 798 | }); 799 | Canvas.registerFont(__dirname + "/assets/fonts/bold-font.ttf", { 800 | family: "Manrope", 801 | weight: "bold", 802 | style: "normal" 803 | }); 804 | 805 | const canvas = Canvas.createCanvas(700, 250); 806 | const ctx = canvas.getContext("2d"); 807 | 808 | const background = await Canvas.loadImage(__dirname + "/assets/images/welcomebg.png"); 809 | ctx.drawImage(background, 0, 0, canvas.width, canvas.height); 810 | 811 | const font = "Manrope"; 812 | 813 | ctx.font = `20px ${font}`; 814 | ctx.fillStyle = "#ffffff"; 815 | ctx.textAlign = "start"; 816 | ctx.shadowBlur = 10; 817 | ctx.shadowColor = "black"; 818 | ctx.fillText("Goodbye", 260, 100); 819 | 820 | const welcometextPosition = { width: 260, height: 150 }; 821 | 822 | let fontSize = 55; 823 | ctx.font = `bold ${fontSize}px ${font}`; 824 | 825 | do { 826 | fontSize -= 1; 827 | ctx.font = `bold ${fontSize}px ${font}`; 828 | } while (ctx.measureText(`${username}#${discrim}!`).width > 430); 829 | 830 | ctx.fillStyle = "#ffffff"; 831 | ctx.textAlign = "start"; 832 | ctx.fillText(`${username}`, welcometextPosition.width, welcometextPosition.height, 455); 833 | 834 | ctx.fillStyle = "rgba(255, 255, 255, 0.5)"; 835 | ctx.textAlign = "start"; 836 | ctx.fillText( 837 | `#${discrim}!`, 838 | ctx.measureText(`${username}`).width + welcometextPosition.width, 839 | welcometextPosition.height 840 | ); 841 | 842 | ctx.shadowBlur = 0; 843 | ctx.beginPath(); 844 | ctx.arc(125, 125, 100, 0, Math.PI * 2, true); 845 | ctx.closePath(); 846 | ctx.clip(); 847 | 848 | const avatar = await Canvas.loadImage(avatarURL); 849 | ctx.drawImage(avatar, 25, 25, 200, 200); 850 | 851 | return canvas.toBuffer(); 852 | } 853 | 854 | /** 855 | * leaver 856 | * @param {String} username Username 857 | * @param {String} discrim Discriminator 858 | * @param {String} avatarURL Avatar URL or {Buffer} or YuriCanvas {Buffer} itself 859 | * @param {String} color Hex or HTML5 color name or rgb 860 | * @returns {Promise} 861 | * @example let img = await canva.leave({ username: "Snowflake", discrim: "0007", avatarURL: "..." }); 862 | * canva.write(img, "img.png"); 863 | */ 864 | static async leave(...options) { 865 | return YuriCanvas.leaver(...options); 866 | } 867 | 868 | /** 869 | * pixelate 870 | * @param {Image} image Image 871 | * @param {Number} level pixelation level 872 | * @returns {Promise} 873 | * @example let img = await canva.pixelate({ username: "Snowflake", discrim: "0007", avatarURL: "..." }); 874 | * canva.write(img, "img.png"); 875 | */ 876 | static async pixelate(image, level = 10) { 877 | if (!image) throw new Error("image was not provided!"); 878 | image = await jimp.read(image); 879 | image.pixelate(isNaN(level) ? 10 : parseInt(level)); 880 | let raw = await image.getBufferAsync("image/png"); 881 | return raw; 882 | } 883 | 884 | /** 885 | * writes the buffer to a file 886 | * @param {{Buffer}} buffer 887 | * @param {String} filename 888 | * @returns {Promise} 889 | * @example canva.write(await canva.color("RED"), "redColor.png"); 890 | */ 891 | static async write(buffer, filename) { 892 | if (!buffer) throw new Error("No buffer provided!"); 893 | if (!filename) filename = "image.png"; 894 | return fs.writeFileSync(filename, buffer); 895 | } 896 | 897 | /** 898 | * JokeOverTheHead 899 | * @param {String|Buffer} image Image to manipulate 900 | * @returns {Promise} 901 | * @example let img = await canva.jokeoverhead(image); 902 | * canva.write(img, "img.png"); 903 | */ 904 | static async jokeoverhead(image) { 905 | if (!image) throw new Error("no image provided!"); 906 | let canvas = Canvas.createCanvas(425, 404); 907 | let ctx = canvas.getContext("2d"); 908 | ctx.fillStyle = "black"; 909 | ctx.fillRect(0, 0, 425, 404); 910 | image = await Canvas.loadImage(await YuriCanvas.circle(image)); 911 | ctx.drawImage(image, 125, 130, 140, 135); 912 | let layer = await Canvas.loadImage(__dirname + "/assets/images/jokeoverhead.png"); 913 | ctx.drawImage(layer, 0, 0, 425, 404); 914 | return canvas.toBuffer(); 915 | } 916 | 917 | /** 918 | * Blurplefy the image 919 | * @param {String|Buffer} image Image to manipulate 920 | * @param {Number} r Red color placeholder 921 | * @param {Number} g Green color placeholder 922 | * @param {Number} b Blue color placeholder 923 | * @returns {Promise} 924 | * @example let img = await canva.replaceColor(image, { r, g, b }); 925 | * canva.write(img, "img.png"); 926 | */ 927 | static async replaceColor(image, { r, g, b }) { 928 | if (!image) throw new Error("No image provided!"); 929 | 930 | image = await jimp.read(image); 931 | if (Array.isArray(r) && Array.isArray(g) && Array.isArray(b)) { 932 | image.color([ 933 | { apply: "red", params: r }, 934 | { apply: "green", params: g }, 935 | { apply: "blue", params: b } 936 | ]); 937 | } 938 | if (!r || !g || !b || isNaN(r) || isNaN(g) || isNaN(b)) return image; 939 | image.color([ 940 | { apply: "red", params: [parseInt(r)] }, 941 | { apply: "green", params: [parseInt(g)] }, 942 | { apply: "blue", params: [parseInt(b)] } 943 | ]); 944 | 945 | return await image.getBufferAsync("image/png"); 946 | } 947 | 948 | /** 949 | * Change my mind 950 | * @param {String} text Text 951 | * @returns {Promise} 952 | * @example let img = await canva.changemymind(text); 953 | * canva.write(img, "img.png"); 954 | */ 955 | static async changemymind(text) { 956 | if (!text) throw new Error("No text was provided!"); 957 | const base = await Canvas.loadImage(__dirname + "/assets/images/changemymind.jpg"); 958 | const canvas = Canvas.createCanvas(base.width, base.height); 959 | const ctx = canvas.getContext("2d"); 960 | ctx.drawImage(base, 0, 0, canvas.width, canvas.height); 961 | let x = text.length; 962 | let fontSize = 70; 963 | if (x <= 15) { 964 | ctx.translate(310, 365); 965 | } else if (x <= 30) { 966 | fontSize = 50; 967 | ctx.translate(315, 365); 968 | } else if (x <= 70) { 969 | fontSize = 40; 970 | ctx.translate(315, 365); 971 | } else if (x <= 85) { 972 | fontSize = 32; 973 | ctx.translate(315, 365); 974 | } else if (x < 100) { 975 | fontSize = 26; 976 | ctx.translate(315, 365); 977 | } else if (x < 120) { 978 | fontSize = 21; 979 | ctx.translate(315, 365); 980 | } else if (x < 180) { 981 | fontSize = 0.0032 * (x * x) - 0.878 * x + 80.545; 982 | ctx.translate(315, 365); 983 | } else if (x < 700) { 984 | fontSize = 0.0000168 * (x * x) - 0.0319 * x + 23.62; 985 | ctx.translate(310, 338); 986 | } else { 987 | fontSize = 7; 988 | ctx.translate(310, 335); 989 | } 990 | ctx.font = `${fontSize}px 'Arial'`; 991 | ctx.rotate(-0.39575); 992 | 993 | const lines = Util.getLines({ text, ctx, maxWidth: 345 }); 994 | let i = 0; 995 | while (i < lines.length) { 996 | ctx.fillText(lines[i], 10, i * fontSize - 5); 997 | i++; 998 | } 999 | return canvas.toBuffer(); 1000 | } 1001 | 1002 | /** 1003 | * Deepfry the image 1004 | * @param {String|Buffer} image image to deepfry 1005 | * @returns {Promise} 1006 | * @example let img = await canva.deepfry(image); 1007 | * canva.write(img, "img.png"); 1008 | */ 1009 | static async deepfry(image) { 1010 | if (!image) throw new Error("No image provided!"); 1011 | image = await Canvas.loadImage(image); 1012 | const canvas = Canvas.createCanvas(1024, 1024); 1013 | const ctx = canvas.getContext("2d"); 1014 | ctx.drawImage(image, 0, 0, canvas.width, canvas.height); 1015 | ctx.globalAlpha = 0; 1016 | ctx.fillStyle = "#FF591A"; 1017 | ctx.fillRect(0, 0, canvas.width, canvas.height); 1018 | ctx.globalAlpha = 1.0; 1019 | ctx.globalCompositeOperation = "saturation"; 1020 | ctx.fillStyle = "hsl(0, 100%, 50%)"; 1021 | ctx.fillRect(0, 0, canvas.width, canvas.height); 1022 | ctx.globalCompositeOperation = "source-over"; 1023 | 1024 | let data = ctx.getImageData(0, 0, canvas.width, canvas.height); 1025 | data = Util.brightnessContrastPhotoshop(data, 52, 60); 1026 | ctx.putImageData(data, 0, 0); 1027 | data = Util.brightnessContrastPhotoshop(data, 32, 40); 1028 | data = Util.grain(data); 1029 | ctx.putImageData(data, 0, 0); 1030 | 1031 | return canvas.toBuffer(); 1032 | } 1033 | 1034 | /** 1035 | * Creates qr code 1036 | * @param {String} text text for the qr code 1037 | * @param {Object} options QR code options 1038 | * @param {String} [options.color] QR Code color 1039 | * @param {String} [options.background] Background color of the qr code 1040 | * @returns {Promise} 1041 | * @example let img = await canva.createQRCode(text); 1042 | * canva.write(img, "img.png"); 1043 | */ 1044 | static async createQRCode(text, options = { background: "#FFFFFF", color: "#000000" }) { 1045 | if (!text) throw new Error("No text specified!"); 1046 | let img = `https://api.qrserver.com/v1/create-qr-code/?size=1024x1024&data=${encodeURIComponent( 1047 | text 1048 | )}&color=${options.color.replace("#", "")}&bgcolor=${options.background.replace("#", "")}`; 1049 | img = await jimp.read(img); 1050 | return await img.getBufferAsync("image/png"); 1051 | } 1052 | 1053 | /** 1054 | * YouTube comment 1055 | * @param {String|Buffer} image Image 1056 | * @param {String} username Username 1057 | * @param {String} comment Comment 1058 | * @returns {Promise} 1059 | * @example let img = await canva.youtube(image, "PewDiePie", "B*tch Lasagna"); 1060 | * canva.write(img, "img.png"); 1061 | */ 1062 | static async youtube(image, username, comment) { 1063 | if (!image) throw new Error("No image provided!"); 1064 | if (!username) throw new Error("No username provided!"); 1065 | if (!comment) throw new Error("No comment provided!"); 1066 | 1067 | let base = await jimp.read(__dirname + "/assets/images/youtube.png"); 1068 | base.resize(650, 183); 1069 | let avatar = await await jimp.read(await YuriCanvas.circle(image)); 1070 | avatar.resize(52, 52); 1071 | 1072 | base.composite(avatar, 17, 33); 1073 | 1074 | let font = await jimp.loadFont(jimp.FONT_SANS_16_BLACK); 1075 | 1076 | let time = Math.floor(Math.random() * (59 - 1)) + 1; 1077 | time = `${time + (time == 1 ? " minute" : " minutes")} ago`; 1078 | 1079 | base.print(font, 92, 34, username.substr(0, 20)); 1080 | base.print(font, 200, 34, time); 1081 | base.print(font, 92, 59, comment.substr(0, 40)); 1082 | 1083 | return await base.getBufferAsync("image/png"); 1084 | } 1085 | 1086 | /** 1087 | * Ew, I stepped in shit... 1088 | * @param {String|Buffer} image Image source 1089 | * @returns {Promise} 1090 | * @example let img = await canva.shit(image); 1091 | * canva.write(img, "img.png"); 1092 | */ 1093 | static async shit(image) { 1094 | if (!image) throw new Error("No image provided!"); 1095 | image = await jimp.read(image); 1096 | image.resize(170, 170); 1097 | let base = await jimp.read(__dirname + "/assets/images/shit.png"); 1098 | image.rotate(52, false); 1099 | base.composite(image, 210, 700); 1100 | return await base.getBufferAsync("image/png"); 1101 | } 1102 | 1103 | /** 1104 | * Distracted boyfriend 1105 | * @param {String|Buffer} image1 Face for the girl in red color 1106 | * @param {String|Buffer} image2 Face for the boy 1107 | * @param {String|Buffer} image3 Face for the other girl [optional] 1108 | * @returns {Promise} 1109 | * @example let img = await YuriCanvas.distracted("firstImage.png", "secondImage.png", "thirdImage.png") 1110 | * YuriCanvas.write(img, "distracted.png"); 1111 | */ 1112 | static async distracted(image1, image2, image3 = null) { 1113 | if (!image1) throw new Error("No image1 provided!"); 1114 | if (!image2) throw new Error("No image2 provided!"); 1115 | let base = await jimp.read(__dirname + "/assets/images/distracted.jpg"); 1116 | 1117 | image1 = await jimp.read(await YuriCanvas.circle(image1)); 1118 | image1.resize(150, 150); 1119 | 1120 | image2 = await jimp.read(await YuriCanvas.circle(image2)); 1121 | image2.resize(130, 130); 1122 | 1123 | base.composite(image1, 180, 90); 1124 | base.composite(image2, 480, 35); 1125 | if (image3 && image3 !== null) { 1126 | image3 = await jimp.read(await YuriCanvas.circle(image3)); 1127 | image3.resize(130, 130); 1128 | base.composite(image3, 730, 110); 1129 | } 1130 | 1131 | return await base.getBufferAsync("image/png"); 1132 | } 1133 | 1134 | /** 1135 | * Clyde 1136 | * @param {String} message Message 1137 | * @returns {Promise} 1138 | * @example const image = await YuriCanvas.clyde("You can't do that"); 1139 | * canva.write(image, "clyde.png"); 1140 | */ 1141 | static async clyde(message) { 1142 | if (!message) messgae = "Please provide text!"; 1143 | let avatar = await Canvas.loadImage( 1144 | await YuriCanvas.circle( 1145 | "https://images.discordapp.net/avatars/536953835361665024/ea7664a628a7a4b772dd06ed81637334.png?size=512" 1146 | ) 1147 | ); 1148 | let badge = await Canvas.loadImage("https://i.imgur.com/f3sGMSW.png"); 1149 | Canvas.registerFont(__dirname + "/assets/fonts/Whitney-medium.otf", { 1150 | family: "Whitney", 1151 | weight: "regular", 1152 | style: "normal" 1153 | }); 1154 | 1155 | Canvas.registerFont(__dirname + "/assets/fonts/regular-font.ttf", { 1156 | family: "Manrope", 1157 | weight: "regular", 1158 | style: "normal" 1159 | }); 1160 | 1161 | const canvas = Canvas.createCanvas(1500, 300); 1162 | 1163 | const ctx = canvas.getContext("2d"); 1164 | ctx.fillStyle = "#36393E"; 1165 | ctx.fillRect(0, 0, canvas.width, canvas.height); 1166 | 1167 | ctx.drawImage(avatar, 75, 30, 130, 130); 1168 | ctx.drawImage(badge, 360, 45, 100, 40); 1169 | 1170 | ctx.font = "40px Manrope"; 1171 | ctx.fillStyle = "#FFFFFF"; 1172 | ctx.textAlign = "start"; 1173 | await Util.toEmojiMessage(ctx, message.substr(0, 66), 230, 150); 1174 | 1175 | ctx.font = "50px Whitney"; 1176 | ctx.fillStyle = "#FFFFFF"; 1177 | ctx.textAlign = "start"; 1178 | ctx.fillText("Clyde", 230, 80); 1179 | 1180 | ctx.font = "40px Whitney"; 1181 | ctx.fillStyle = "#7D7D7D"; 1182 | ctx.textAlign = "start"; 1183 | ctx.fillText(Util.toDiscordTime(), 470, 80); 1184 | 1185 | ctx.font = "20px Manrope"; 1186 | ctx.fillStyle = "#7D7D7D"; 1187 | ctx.textAlign = "start"; 1188 | ctx.fillText("Only you can see this —", 240, 190); 1189 | 1190 | ctx.font = "20px Manrope"; 1191 | ctx.fillStyle = "#2785C7"; 1192 | ctx.textAlign = "start"; 1193 | ctx.fillText("delete this message.", 240 + ctx.measureText("Only you can see this —").width + 10, 190); 1194 | 1195 | return canvas.toBuffer(); 1196 | } 1197 | 1198 | /** 1199 | * Fake Quote 1200 | * @param {Object} options Options 1201 | * @param {Buffer|String} [options.image] Image 1202 | * @param {String} [options.message] Message 1203 | * @param {String} [options.username] Username 1204 | * @param {String} [options.color] Color 1205 | * @returns {Promise} 1206 | * @example const image = await YuriCanvas.quote({ image: "...", message: "You can't do that", username: "YuriCanvas", color: "pink" }); 1207 | * canva.write(image, "quote.png"); 1208 | */ 1209 | static async quote(options = { image, message, username, color }) { 1210 | if (!options.image) 1211 | options.image = 1212 | "https://images.discordapp.net/avatars/536953835361665024/ea7664a628a7a4b772dd06ed81637334.png?size=512"; 1213 | if (!options.message) options.message = "Please provide text!"; 1214 | if (!options.username) options.username = "Clyde"; 1215 | if (!options.color) options.color = "#FFFFFF"; 1216 | 1217 | options.image = await Canvas.loadImage(await YuriCanvas.circle(options.image)); 1218 | 1219 | Canvas.registerFont(__dirname + "/assets/fonts/Whitney-medium.otf", { 1220 | family: "Whitney", 1221 | weight: "regular", 1222 | style: "normal" 1223 | }); 1224 | 1225 | Canvas.registerFont(__dirname + "/assets/fonts/regular-font.ttf", { 1226 | family: "Manrope", 1227 | weight: "regular", 1228 | style: "normal" 1229 | }); 1230 | 1231 | const canvas = Canvas.createCanvas(1500, 300); 1232 | 1233 | const ctx = canvas.getContext("2d"); 1234 | ctx.fillStyle = "#36393E"; 1235 | ctx.fillRect(0, 0, canvas.width, canvas.height); 1236 | 1237 | ctx.drawImage(options.image, 75, 30, 130, 130); 1238 | 1239 | ctx.font = "40px Manrope"; 1240 | ctx.fillStyle = "#FFFFFF"; 1241 | ctx.textAlign = "start"; 1242 | await Util.toEmojiMessage(ctx, options.message.substr(0, 66), 230, 150); 1243 | 1244 | ctx.font = "50px Whitney"; 1245 | ctx.fillStyle = typeof options.color == "string" ? options.color : "#FFFFFF"; 1246 | ctx.textAlign = "start"; 1247 | ctx.fillText(typeof options.username === "string" ? options.username.substr(0, 17) : "Clyde", 230, 80); 1248 | 1249 | ctx.font = "40px Whitney"; 1250 | ctx.fillStyle = "#7D7D7D"; 1251 | ctx.textAlign = "start"; 1252 | ctx.fillText(Util.toDiscordTime(), 240 + ctx.measureText(options.username.substr(0, 17)).width + 110, 80); 1253 | 1254 | return canvas.toBuffer(); 1255 | } 1256 | 1257 | /** 1258 | * PornHub Comment 1259 | * @param {Object} options Options 1260 | * @param {String} [options.username] Username 1261 | * @param {String} [options.message] Comment 1262 | * @param {String|Buffer} [options.image] Image 1263 | * @returns {Promise} 1264 | * @example const image = await YuriCanvas.phub({ username: "User", message: "Looks good 👏🏻", image: "..." }); 1265 | */ 1266 | static async phub(options = { username, message, image }) { 1267 | if (!options.username) throw new Error("Username may not be empty!"); 1268 | if (!options.message) throw new Error("Message may not be empty!"); 1269 | if (!options.image) throw new Error("Image may not be empty!"); 1270 | 1271 | let image = await Canvas.loadImage(options.image); 1272 | let baseImage = await Canvas.loadImage(__dirname + "/assets/images/phub.png"); 1273 | 1274 | let canvas = Canvas.createCanvas(baseImage.width, baseImage.height); 1275 | let ctx = canvas.getContext("2d"); 1276 | 1277 | ctx.drawImage(baseImage, 0, 0, canvas.width, canvas.height); 1278 | ctx.drawImage(image, 30, 310, 70, 70); 1279 | 1280 | ctx.font = "32px Arial"; 1281 | ctx.fillStyle = "#F99600"; 1282 | ctx.textAlign = "start"; 1283 | ctx.fillText(options.username.substr(0, 20), 115, 350); 1284 | 1285 | ctx.font = "32px Arial"; 1286 | ctx.fillStyle = "#FFFFFF"; 1287 | ctx.textAlign = "start"; 1288 | await Util.toEmojiMessage(ctx, options.message.substr(0, 50), 30, 430); 1289 | 1290 | return canvas.toBuffer(); 1291 | } 1292 | 1293 | /** 1294 | * YuriCanvas Version 1295 | */ 1296 | static get version() { 1297 | return require("../package.json").version; 1298 | } 1299 | 1300 | /** 1301 | * Spotify card 1302 | * @param {object} options required params 1303 | * @param {string} [options.title] Song name 1304 | * @param {string} [options.artist] Song name 1305 | * @param {string} [options.album] Ablum 1306 | * @param {string|Buffer} [options.image] Image 1307 | * @param {number} [options.start] Timestamp when song started 1308 | * @param {number} [options.end] Timestamp when song ends 1309 | * @returns {Promise} 1310 | * @example const img = await YuriCanvas.spotify({ 1311 | * title: "Faded", 1312 | * artist: "Alan Walker", 1313 | * album: "Different World", 1314 | * image: "...", 1315 | * start: START_TIMESTAMP, 1316 | * end: END_TIMESTAMP 1317 | * }); 1318 | * 1319 | * YuriCanvas.write(img, "spotify.png"); 1320 | */ 1321 | static async spotify(options = { title: null, image: null, artist: null, album: null, start: null, end: null }) { 1322 | if (!options) throw new Error('Missing parameter "options".'); 1323 | if (!options.title) throw new Error('Missing "title" in options.'); 1324 | if (!options.artist) throw new Error('Missing "artist" in options.'); 1325 | if (!options.start) throw new Error('Missing "start" in options.'); 1326 | if (!options.end) throw new Error('Missing "end" in options.'); 1327 | 1328 | const total = options.end - options.start; 1329 | const progress = Date.now() - options.start; 1330 | const progressF = Util.formatTime(progress > total ? total : progress); 1331 | const ending = Util.formatTime(total); 1332 | 1333 | const getProgress = () => { 1334 | let prg = (progress / total) * 300; 1335 | if (isNaN(prg) || prg < 0) return 0; 1336 | if (prg > 300) return 300; 1337 | return prg; 1338 | }; 1339 | 1340 | Canvas.registerFont(__dirname + "/assets/fonts/regular-font.ttf", { 1341 | family: "Manrope", 1342 | weight: "regular", 1343 | style: "normal" 1344 | }); 1345 | 1346 | Canvas.registerFont(__dirname + "/assets/fonts/bold-font.ttf", { 1347 | family: "Manrope", 1348 | weight: "bold", 1349 | style: "normal" 1350 | }); 1351 | 1352 | const canvas = Canvas.createCanvas(600, 150); 1353 | const ctx = canvas.getContext("2d"); 1354 | 1355 | // background 1356 | ctx.beginPath(); 1357 | ctx.rect(0, 0, canvas.width, canvas.height); 1358 | ctx.fillStyle = "#2F3136"; 1359 | ctx.fillRect(0, 0, canvas.width, canvas.height); 1360 | 1361 | // draw image 1362 | const img = await Canvas.loadImage(options.image); 1363 | 1364 | ctx.drawImage(img, 20, 20, 100, 100); 1365 | 1366 | // draw songname 1367 | ctx.fillStyle = "#FFFFFF"; 1368 | ctx.font = "bold 20px Manrope"; 1369 | ctx.fillText(Util.shorten(options.title, 30), 170, 40); 1370 | 1371 | // draw artist name 1372 | ctx.fillStyle = "#F1F1F1"; 1373 | ctx.font = "14px Manrope"; 1374 | ctx.fillText(`by ${Util.shorten(options.artist, 40)}`, 170, 70); 1375 | 1376 | // add album 1377 | if (options.album && typeof options.album === "string") { 1378 | ctx.fillStyle = "#F1F1F1"; 1379 | ctx.font = "14px Manrope"; 1380 | ctx.fillText(`on ${Util.shorten(options.album, 40)}`, 170, 90); 1381 | } 1382 | 1383 | // ending point 1384 | ctx.fillStyle = "#B3B3B3"; 1385 | ctx.font = "14px Manrope"; 1386 | ctx.fillText(ending, 430, 130); 1387 | 1388 | // progress 1389 | ctx.fillStyle = "#B3B3B3"; 1390 | ctx.font = "14px Manrope"; 1391 | ctx.fillText(progressF, 170, 130); 1392 | 1393 | // progressbar track 1394 | ctx.rect(170, 170, 300, 4); 1395 | ctx.fillStyle = "#E8E8E8"; 1396 | ctx.fillRect(170, 110, 300, 4); 1397 | 1398 | // progressbar 1399 | ctx.fillStyle = "#1DB954"; 1400 | ctx.fillRect(170, 110, getProgress(), 4); 1401 | 1402 | // return 1403 | return canvas.toBuffer(); 1404 | } 1405 | 1406 | /** 1407 | * Attachment object for discord.js 1408 | * @param {string|Buffer} data Data 1409 | * @param {string} name name 1410 | * @example const data = await YuriCanvas.color("blue"); 1411 | * const img = YuriCanvas.Attachment(data, "color.png"); 1412 | * return message.channel.send(img); 1413 | */ 1414 | static Attachment(data, name, raw = false) { 1415 | if (!raw) 1416 | return { 1417 | files: [{ attachment: data, name: name || null }] 1418 | }; 1419 | 1420 | return { attachment: data, name: name || null }; 1421 | } 1422 | 1423 | /** 1424 | * Rotates image 1425 | * @param {string|Buffer} image Image to rotate 1426 | * @param {number} angle Rotation angle 1427 | * @example const img = await YuriCanvas.rotate(image, 90); 1428 | * YuriCanvas.write(img, "image.png"); 1429 | * @returns {Promise} 1430 | */ 1431 | static async rotate(image, angle = 0) { 1432 | if (!image) throw new Error("Invalid image provided!"); 1433 | image = await jimp.read(image); 1434 | image.rotate(angle); 1435 | return await image.getBufferAsync("image/png"); 1436 | } 1437 | } 1438 | 1439 | module.exports = YuriCanvas; 1440 | -------------------------------------------------------------------------------- /src/CanvasUtil.js: -------------------------------------------------------------------------------- 1 | const Canvas = require("canvas"); 2 | const { fillTextWithTwemoji } = require("node-canvas-with-twemoji-and-discord-emoji"); 3 | const moment = require("moment"); 4 | require("moment-duration-format"); 5 | 6 | class CanvasUtil { 7 | constructor() { 8 | throw new Error(`Class ${this.constructor.name} may not be instantiated!`); 9 | } 10 | 11 | static getLines({ text, ctx, maxWidth }) { 12 | if (!text) return []; 13 | if (!ctx) throw new Error("Canvas context was not provided!"); 14 | if (!maxWidth) throw new Error("No max-width provided!"); 15 | const lines = []; 16 | 17 | while (text.length) { 18 | let i; 19 | for (i = text.length; ctx.measureText(text.substr(0, i)).width > maxWidth; i -= 1); 20 | const result = text.substr(0, i); 21 | let j; 22 | if (i !== text.length) for (j = 0; result.indexOf(" ", j) !== -1; j = result.indexOf(" ", j) + 1); 23 | lines.push(result.substr(0, j || result.length)); 24 | text = text.substr(lines[lines.length - 1].length, text.length); 25 | } 26 | 27 | return lines; 28 | } 29 | 30 | static twitterTimeFormat(date = new Date()) { 31 | if (typeof date === "number") date = new Date(date); 32 | if (!date instanceof Date) date = new Date(); 33 | const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; 34 | const day = date.getDate(); 35 | let month = date.getMonth(); 36 | const year = date.getFullYear(); 37 | month = months[month]; 38 | let hours = date.getHours(); 39 | let minutes = date.getMinutes(); 40 | const ampm = hours >= 12 ? "PM" : "AM"; 41 | hours %= 12; 42 | hours = hours || 12; 43 | minutes = minutes < 10 ? `0${minutes}` : minutes; 44 | return `${hours}:${minutes} ${ampm} - ${day} ${month} ${year}`; 45 | } 46 | 47 | static grain(image) { 48 | const pixels = image.data; 49 | 50 | for (let idx = 0; idx < pixels.length; idx++) { 51 | if (Math.random() > 0.98) { 52 | pixels[idx] = Math.max(128, Math.floor(Math.random() * 255)); 53 | pixels[idx + 1] = Math.max(128, Math.floor(Math.random() * 255)); 54 | } 55 | 56 | if (Math.random() > 0.98) { 57 | const v = Math.random() * 60; 58 | pixels[idx] = Math.max(pixels[idx] - v, 0); 59 | } 60 | } 61 | 62 | return image; 63 | } 64 | 65 | static brightnessContrastPhotoshop(image, brightness, contrast) { 66 | const pixels = image.data; 67 | const data = Canvas.createImageData(image.width, image.height); 68 | const dataPixels = data.data; 69 | 70 | brightness = (brightness + 100) / 100; 71 | contrast = (contrast + 100) / 100; 72 | 73 | this.mapRGB(pixels, dataPixels, (value) => { 74 | value *= brightness; 75 | value = (value - 127.5) * contrast + 127.5; 76 | return (value + 0.5) | 0; 77 | }); 78 | return data; 79 | } 80 | 81 | static buildMap(f) { 82 | const m = []; 83 | for (let k = 0, v; k < 256; k += 1) { 84 | m[k] = (v = f(k)) > 255 ? 255 : v < 0 ? 0 : v | 0; 85 | } 86 | return m; 87 | } 88 | 89 | static applyMap(src, dst, map) { 90 | for (let i = 0, l = src.length; i < l; i += 4) { 91 | dst[i] = map[src[i]]; 92 | dst[i + 1] = map[src[i + 1]]; 93 | dst[i + 2] = map[src[i + 2]]; 94 | dst[i + 3] = src[i + 3]; 95 | } 96 | } 97 | 98 | static mapRGB(src, dst, func) { 99 | this.applyMap(src, dst, this.buildMap(func)); 100 | } 101 | 102 | static toAbbrev(num) { 103 | if (!num) return "NaN"; 104 | if (typeof num === "string") num = parseInt(num); 105 | let decPlaces = Math.pow(10, 1); 106 | var abbrev = ["K", "M", "B", "T"]; 107 | for (var i = abbrev.length - 1; i >= 0; i--) { 108 | var size = Math.pow(10, (i + 1) * 3); 109 | if (size <= num) { 110 | num = Math.round((num * decPlaces) / size) / decPlaces; 111 | if (num == 1000 && i < abbrev.length - 1) { 112 | num = 1; 113 | i++; 114 | } 115 | num += abbrev[i]; 116 | break; 117 | } 118 | } 119 | return num; 120 | } 121 | 122 | static async toEmojiMessage(ctx, text, x, y) { 123 | return fillTextWithTwemoji(ctx, text, x, y); 124 | } 125 | 126 | static toDiscordTime() { 127 | let date = new Date(); 128 | let hours = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours(); 129 | let minutes = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes(); 130 | return `Today at ${hours}:${minutes}`; 131 | } 132 | 133 | static formatTime(time) { 134 | if (!time) return "00:00"; 135 | const fmt = moment.duration(time).format("dd:hh:mm:ss"); 136 | 137 | const chunk = fmt.split(":"); 138 | if (chunk.length < 2) chunk.unshift("00"); 139 | return chunk.join(":"); 140 | } 141 | 142 | static shorten(text, len) { 143 | if (typeof text !== "string") return ""; 144 | if (text.length <= len) return text; 145 | return text.substr(0, len).trim() + "..."; 146 | } 147 | } 148 | 149 | module.exports = CanvasUtil; 150 | -------------------------------------------------------------------------------- /src/assets/affect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/affect.png -------------------------------------------------------------------------------- /src/assets/batslap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/batslap.png -------------------------------------------------------------------------------- /src/assets/beautiful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/beautiful.png -------------------------------------------------------------------------------- /src/assets/bed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/bed.png -------------------------------------------------------------------------------- /src/assets/bold-font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/bold-font.ttf -------------------------------------------------------------------------------- /src/assets/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/delete.png -------------------------------------------------------------------------------- /src/assets/facepalm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/facepalm.png -------------------------------------------------------------------------------- /src/assets/fonts/Whitney-Book.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/fonts/Whitney-Book.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Whitney-medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/fonts/Whitney-medium.otf -------------------------------------------------------------------------------- /src/assets/fonts/bold-font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/fonts/bold-font.ttf -------------------------------------------------------------------------------- /src/assets/fonts/regular-font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/fonts/regular-font.ttf -------------------------------------------------------------------------------- /src/assets/gay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/gay.png -------------------------------------------------------------------------------- /src/assets/hitler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/hitler.png -------------------------------------------------------------------------------- /src/assets/images/affect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/affect.png -------------------------------------------------------------------------------- /src/assets/images/batslap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/batslap.png -------------------------------------------------------------------------------- /src/assets/images/beautiful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/beautiful.png -------------------------------------------------------------------------------- /src/assets/images/bed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/bed.png -------------------------------------------------------------------------------- /src/assets/images/changemymind.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/changemymind.jpg -------------------------------------------------------------------------------- /src/assets/images/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/delete.png -------------------------------------------------------------------------------- /src/assets/images/distracted.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/distracted.jpg -------------------------------------------------------------------------------- /src/assets/images/dnd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/dnd.png -------------------------------------------------------------------------------- /src/assets/images/facepalm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/facepalm.png -------------------------------------------------------------------------------- /src/assets/images/gay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/gay.png -------------------------------------------------------------------------------- /src/assets/images/hitler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/hitler.png -------------------------------------------------------------------------------- /src/assets/images/idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/idle.png -------------------------------------------------------------------------------- /src/assets/images/jail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/jail.png -------------------------------------------------------------------------------- /src/assets/images/jokeoverhead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/jokeoverhead.png -------------------------------------------------------------------------------- /src/assets/images/kiss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/kiss.png -------------------------------------------------------------------------------- /src/assets/images/offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/offline.png -------------------------------------------------------------------------------- /src/assets/images/online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/online.png -------------------------------------------------------------------------------- /src/assets/images/phub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/phub.png -------------------------------------------------------------------------------- /src/assets/images/rankcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/rankcard.png -------------------------------------------------------------------------------- /src/assets/images/rankcard2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/rankcard2.png -------------------------------------------------------------------------------- /src/assets/images/rankcard3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/rankcard3.png -------------------------------------------------------------------------------- /src/assets/images/rip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/rip.png -------------------------------------------------------------------------------- /src/assets/images/shit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/shit.png -------------------------------------------------------------------------------- /src/assets/images/spank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/spank.png -------------------------------------------------------------------------------- /src/assets/images/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/trash.png -------------------------------------------------------------------------------- /src/assets/images/triggered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/triggered.png -------------------------------------------------------------------------------- /src/assets/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/twitter.png -------------------------------------------------------------------------------- /src/assets/images/wanted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/wanted.png -------------------------------------------------------------------------------- /src/assets/images/wasted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/wasted.png -------------------------------------------------------------------------------- /src/assets/images/welcomebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/welcomebg.png -------------------------------------------------------------------------------- /src/assets/images/youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/images/youtube.png -------------------------------------------------------------------------------- /src/assets/jail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/jail.png -------------------------------------------------------------------------------- /src/assets/jokeoverhead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/jokeoverhead.png -------------------------------------------------------------------------------- /src/assets/kiss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/kiss.png -------------------------------------------------------------------------------- /src/assets/rankcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/rankcard.png -------------------------------------------------------------------------------- /src/assets/rankcard2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/rankcard2.png -------------------------------------------------------------------------------- /src/assets/regular-font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/regular-font.ttf -------------------------------------------------------------------------------- /src/assets/rip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/rip.png -------------------------------------------------------------------------------- /src/assets/spank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/spank.png -------------------------------------------------------------------------------- /src/assets/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/trash.png -------------------------------------------------------------------------------- /src/assets/triggered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/triggered.png -------------------------------------------------------------------------------- /src/assets/wanted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/wanted.png -------------------------------------------------------------------------------- /src/assets/wasted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/wasted.png -------------------------------------------------------------------------------- /src/assets/welcomebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/src/assets/welcomebg.png -------------------------------------------------------------------------------- /test/changemymind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/changemymind.png -------------------------------------------------------------------------------- /test/clyde.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/clyde.png -------------------------------------------------------------------------------- /test/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/color.png -------------------------------------------------------------------------------- /test/deepfried.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/deepfried.png -------------------------------------------------------------------------------- /test/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/image.png -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | (async () => { 2 | const canvacord = require("../index"); 3 | 4 | const img = await canvacord.clyde("😩 What do u want? <:kek:706738061265993831>"); 5 | 6 | canvacord.write(img, "clyde.png"); 7 | })(); -------------------------------------------------------------------------------- /test/rank-custom-bg-and-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/rank-custom-bg-and-color.png -------------------------------------------------------------------------------- /test/rank-custom-bg-no-overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/rank-custom-bg-no-overlay.png -------------------------------------------------------------------------------- /test/rank-custom-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/rank-custom-bg.png -------------------------------------------------------------------------------- /test/rank-custom-color-bg-no-overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/rank-custom-color-bg-no-overlay.png -------------------------------------------------------------------------------- /test/rank-custom-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/rank-custom-color.png -------------------------------------------------------------------------------- /test/rank-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/rank-default.png -------------------------------------------------------------------------------- /test/triggered.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunix-world/yuri-canvas/f3709ff223345c9ebbc037f0a5a597aec69efd7c/test/triggered.gif --------------------------------------------------------------------------------