Skip to content

Commit

Permalink
Merge pull request #6 from nanoeray/feature/imageapi
Browse files Browse the repository at this point in the history
Implemented built in image api. Closes #5
  • Loading branch information
barbarbar338 committed Dec 28, 2023
2 parents 0d77107 + b2c7d46 commit 3af0246
Show file tree
Hide file tree
Showing 18 changed files with 317 additions and 40 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
dist
.env
.idea
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@
},
"dependencies": {
"@hammerhq/logger": "^0.1.5",
"canvas": "^2.11.2",
"discord.js": "^14.11.0",
"dotenv": "^16.1.3",
"express": "^4.18.2",
"mongodb-memory-server": "^8.12.2",
"mongoose": "^7.2.2"
}
}

Binary file added src/assets/ellips1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/ellips2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/fonts/Poppins-Bold.ttf
Binary file not shown.
Binary file added src/assets/fonts/Poppins-Light.ttf
Binary file not shown.
Binary file added src/assets/fonts/Poppins-Regular.ttf
Binary file not shown.
Binary file added src/assets/pin1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/pin2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/pin3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/pin4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/pin5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/pin6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/progress.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 13 additions & 31 deletions src/commands/rank.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { EmbedBuilder, SlashCommandBuilder } from "discord.js";
import { Embed, EmbedBuilder, SlashCommandBuilder,AttachmentBuilder } from "discord.js";
import { CONFIG } from "../config";
import { GuildMemberModel } from "../models/GuildMemberModel";
import { UserModel } from "../models/UserModel";
import { createCanvas, loadImage, registerFont } from "canvas";

registerFont(__dirname + '/../assets/fonts/Poppins-Bold.ttf', { family: 'Poppins Bold' });
registerFont(__dirname + '/../assets/fonts/Poppins-Light.ttf', { family: 'Poppins Light' });
registerFont(__dirname + '/../assets/fonts/Poppins-Regular.ttf', { family: 'Poppins Regular' });


const RankCommand: SlashLevel.ICommand = {
builder: new SlashCommandBuilder()
Expand Down Expand Up @@ -61,40 +67,16 @@ const RankCommand: SlashLevel.ICommand = {
const { xp, level } = guildMemberModel;
const requiredXP = client.utils.calculateRequiredExp(level + 1);

const url = `${
CONFIG.IMAGE_API_URL
}/v2/canvas/rankcard?color=${encodeURIComponent(
userModel.rankColor,
)}&xp=${encodeURIComponent(xp)}&level=${encodeURIComponent(
level,
)}&xpToLevel=${encodeURIComponent(
requiredXP,
)}&position=${encodeURIComponent(index + 1)}&tag=${encodeURIComponent(
member.user.tag,
)}&status=${encodeURIComponent(
member.presence?.status || "invisible",
)}&avatarURL=${encodeURIComponent(
member.displayAvatarURL({
extension: "png",
}),
)}`;

const embed = new EmbedBuilder()
.setColor(
member ? member.displayHexColor : `#${userModel.rankColor}`,
)
.setAuthor({
name: member.user.tag,
iconURL: member.displayAvatarURL({
extension: "png",
}),
url,
})
.setImage(url);
const img = await client.utils.createImage(member.user.tag,level,xp,requiredXP,member.displayAvatarURL({
extension: "png",
}))


const file = new AttachmentBuilder(img);
return interaction.editReply({
content: `🏆 Rank card of **${member.user.tag}**`,
embeds: [embed],
files: [file]
});
},
};
Expand Down
2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const CONFIG = {
PORT: parseInt(process.env.PORT as string),
CLIENT_ID,
INVITE: `https://discord.com/oauth2/authorize?client_id=${CLIENT_ID}&scope=bot+applications.commands&permissions=268725328`,
IMAGE_API_URL: "https://pinkie-api.fly.dev",
IMAGE_API_URL: "http://localhost:3000/imageApi",
EXAMPLE_GIF: "https://338.rocks/slash-example.gif",
REPO_URL: "https://github.com/barisbored/slash-level",
PRESENCE: {
Expand Down
164 changes: 163 additions & 1 deletion src/struct/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,52 @@
import { Canvas, Image, createCanvas, loadImage } from "canvas";

export class Utils {

public getMaxNextLine = (input: any, maxChars = 20) => {
// Split the string into an array of words.
const allWords = input.split(" ");
// Find the index in the words array at which we should stop or we will exceed
// maximum characters.
const lineIndex = allWords.reduce((prev: { done: any; position: number; }, cur: string | any[], index: any) => {
if (prev?.done) return prev;
const endLastWord = prev?.position || 0;
const position = endLastWord + 1 + cur.length;
return position >= maxChars ? { done: true, index } : { position, index };
});
// Using the index, build a string for this line ...
const line = allWords.slice(0, lineIndex.index).join(" ");
// And determine what's left.
const remainingChars = allWords.slice(lineIndex.index).join(" ");
// Return the result.
return { line, remainingChars };
};

public formatTitle = (title: any) => {
let output: any = [];
// If the title is 40 characters or longer, look to add ellipses at the end of
// the second line.
if (title.length >= 40) {
const firstLine = this.getMaxNextLine(title);
const secondLine = this.getMaxNextLine(firstLine.remainingChars);
output = [firstLine.line];
let fmSecondLine = secondLine.line;
if (secondLine.remainingChars.length > 0) fmSecondLine += " ...";
output.push(fmSecondLine);
}
// If 20 characters or longer, add the entire second line, using a max of half
// the characters, making the first line always slightly shorter than the
// second.
else if (title.length >= 20) {
const firstLine = this.getMaxNextLine(title, title.length / 2);
output = [firstLine.line, firstLine.remainingChars];
}
// Otherwise, return the short title.
else {
output = [title];
}

return output;
};
public random = <T>(array: T[]) =>
array[Math.floor(Math.random() * array.length)];

Expand Down Expand Up @@ -36,6 +84,120 @@ export class Utils {

const short = " kMGTPE"[length / 3];

return (outputNum + short).trim();
return longNumber < 999 ? `${longNumber}` : (outputNum + short).trim();
};

public createImage = async (tag: any, level: any, xp: any, requiredXP: any, avatar: any) => {
const post = {
nickname: tag,
rank: level,
xp: xp,
toLevel: requiredXP,
avatar: avatar
};

const width = 840;
const height = 214;

const canvas = createCanvas(width, height);
const context = canvas.getContext("2d");


/** Background **/
context.fillStyle = "#010144";
context.fillRect(0, 0, width, height);
/** Background **/

/** Texts **/

context.font = "16pt 'Poppins Regular'";
context.textAlign = "left";
context.fillStyle = "#fff";

const titleText = this.formatTitle(post.nickname);
context.fillText(titleText[0], 254, 145);
context.font = "13pt 'Poppins Light'";
context.textAlign = "left";
context.fillStyle = "#fff";
context.fillText(this.formatNumber(post.xp) + "/" + this.formatNumber(post.toLevel), 680, 145);
/** Texts **/

/** Background PINS **/
const pin = await loadImage(__dirname + "/../assets/pin1.png")
context.drawImage(pin, 151, -8, 74, 74);
const pin2 = await loadImage(__dirname + "/../assets/pin2.png")
context.drawImage(pin2, 28, 56, 44, 44);
const pin3 = await loadImage(__dirname + "/../assets/pin3.png")
context.drawImage(pin3, -41, 121, 135, 135);
const pin4 = await loadImage(__dirname + "/../assets/pin4.png")
context.drawImage(pin4, 201, 170, 36, 36);
const pin5 = await loadImage(__dirname + "/../assets/pin5.png")
context.drawImage(pin5, 750, 171, 74, 73);
const pin6 = await loadImage(__dirname + "/../assets/pin6.png")
context.drawImage(pin6, 747, -40, 167, 167);
/** Background PINS **/

/** Progress BG and Background Objects **/
const ellips = await loadImage(__dirname + "/../assets/ellips1.png")
context.drawImage(ellips, -90, -144, 300, 300);
const ellips2 = await loadImage(__dirname + "/../assets/ellips2.png")
context.drawImage(ellips2, 610, 50, 300, 300);
const progress = await loadImage(__dirname + "/../assets/progress.png")
context.drawImage(progress, 254, 155, 506, 28);

/** Progress BG and Background Objects **/

/** Progress Bar **/
function drawRoundedRect(ctx: {
beginPath: () => void;
moveTo: (arg0: any, arg1: any) => void;
arcTo: (arg0: any, arg1: any, arg2: any, arg3: any, arg4: any) => void;
closePath: () => void;
}, x: number, y: number, width: number, height: number, radius: number) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.arcTo(x + width, y, x + width, y + height, radius);
ctx.arcTo(x + width, y + height, x, y + height, radius);
ctx.arcTo(x, y + height, x, y, radius);
ctx.arcTo(x, y, x + width, y, radius);
ctx.closePath();
}

const percentage = (post.xp / post.toLevel) * 506;
const rectWidth = percentage;
const rectHeight = 28;
const cornerRadius = 15;

drawRoundedRect(context, 254, 155, rectWidth, rectHeight, cornerRadius);
context.fillStyle = '#8b17ef';
context.fill();
/** Progress Bar **/

context.font = "11pt 'Poppins Bold'";
context.textAlign = "left";
context.fillStyle = "#fff";
context.fillText("Level " + post.rank, 264, 174);

/** Avatar **/
const avatarLink = await loadImage(post.avatar)
context.font = "18pt 'Poppins Bold'";
context.textAlign = "left";
context.fillStyle = "#fff";
context.fillText("Rank #" + post.rank, 630, 50);
/** Avatar **/

/** Avatar Decoration **/
context.beginPath();
context.arc(150, 107, 70, 0, Math.PI * 2, true);
context.closePath();
context.clip();
context.drawImage(avatarLink, 80, 37, 140, 140);
/** Avatar Decoration **/


const buffer: any = canvas.toBuffer("image/png");
const img = Buffer.from(buffer, 'base64');

return buffer;
}
}
Loading

0 comments on commit 3af0246

Please sign in to comment.