modern-canvacord
Version:
Easy image manipulation for discord.js bots.
321 lines (278 loc) • 9.19 kB
JavaScript
const Canvas = require("canvas");
const Util = require("./Util");
const assets = require("./Assets");
/**
* Spotify presence card builder
*/
class Spotify {
/**
* Creates spotify presence card
* @example
* const card = new canvacord.Spotify()
.setAuthor("Indila")
.setAlbum("Mini World")
.setStartTimestamp(Date.now() - 10000)
.setEndTimestamp(Date.now() + 50000)
.setImage("https://is5-ssl.mzstatic.com/image/thumb/Features111/v4/a4/89/a1/a489a1cb-4543-6861-a276-4470d41d6a90/mzl.zcdmhnlk.jpg/800x800bb.jpeg")
.setTitle("S.O.S");
card.build()
.then(data => {
canvacord.write(data, "./images/spotify.png");
});
*/
constructor() {
/**
* Song title
* @type {string}
*/
this.title = null;
/**
* Thumbnail
* @type {string|Buffer|Canvas.Image}
*/
this.image = null;
/**
* Song artist
* @type {string}
*/
this.artist = null;
/**
* Spotify album name
* @type {string}
*/
this.album = null;
/**
* Discord presence started timestamp
* @type {number}
*/
this.start = null;
/**
* Discord presence ending timestamp
* @type {number}
*/
this.end = null;
/**
* @typedef {object} SpotifyDataBG
* @property {number} type Background type
* @property {string|Buffer} data Background data
*/
/**
* Background
* @type {SpotifyDataBG}
*/
this.background = {
type: 0,
data: "#2F3136"
};
/**
* @typedef {object} SpotifyProgressBarData
* @property {string} bgColor Progressbar bg color
* @property {string} color Progressbar bg color
*/
/**
* Progressbar details
* @type {SpotifyProgressBarData}
*/
this.progressBar = {
bgColor: "#E8E8E8",
color: "#1DB954"
};
this.__registerFonts();
}
/**
* Register fonts
* @returns {void}
* @ignore
* @private
*/
__registerFonts() {
setTimeout(() => {
Canvas.registerFont(assets("FONT").MANROPE_REGULAR, {
family: "Manrope",
weight: "regular",
style: "normal"
});
Canvas.registerFont(assets("FONT").MANROPE_BOLD, {
family: "Manrope",
weight: "bold",
style: "normal"
});
}, 250);
}
/**
* Set progressbar details
* @param {"TRACK"|"BAR"} type Progressbar type
* @param {string} color Color to set
* @returns {Spotify}
*/
setProgressBar(type, color) {
switch(type) {
case "BAR":
this.progressBar.color = color && typeof color === "string" ? color : "#1DB954";
break;
case "TRACK":
this.progressBar.bgColor = color && typeof color === "string" ? color : "#E8E8E8";
break;
default:
throw new Error(`Invalid progressbar type "${type}"!`);
}
return this;
}
/**
* Set title
* @param {string} title Title to set
* @returns {Spotify}
*/
setTitle(title) {
if (!title || typeof title !== "string") throw new Error(`Expected title, received ${typeof title}!`);
this.title = title;
return this;
}
/**
* Set image
* @param {string|Buffer|Canvas.Image} source Image source
* @returns {Spotify}
*/
setImage(source) {
if (!source) throw new Error(`Expected image source, received ${typeof title}!`);
this.image = source;
return this;
}
/**
* Set artist name
* @param {string} name Artist name
* @returns {Spotify}
*/
setAuthor(name) {
if (!name || typeof name !== "string") throw new Error(`Expected artist name, received ${typeof name}!`);
this.artist = name;
return this;
}
/**
* Set album name
* @param {string} name Album name
* @returns {Spotify}
*/
setAlbum(name) {
if (!name || typeof name !== "string") throw new Error(`Expected album name, received ${typeof name}!`);
this.album = name;
return this;
}
/**
* Set start timestamp
* @param {Date|number} time Timestamp
* @returns {Spotify}
*/
setStartTimestamp(time) {
if (!time) throw new Error(`Expected timestamp, received ${typeof time}!`);
if (time instanceof Date) time = time.getTime();
this.start = time;
return this;
}
/**
* Set end timestamp
* @param {Date|number} time Timestamp
* @returns {Spotify}
*/
setEndTimestamp(time) {
if (!time) throw new Error(`Expected timestamp, received ${typeof time}!`);
if (time instanceof Date) time = time.getTime();
this.end = time;
return this;
}
/**
* Set background
* @param {"COLOR"|"IMAGE"} type Background type
* @param {string|Buffer|Canvas.Image} data Background data
* @returns {Spotify}
*/
setBackground(type = "COLOR", data = "#2F3136") {
switch(type) {
case "COLOR":
this.background.type = 0;
this.background.data = data && typeof data === "string" ? data : "#2F3136";
break;
case "IMAGE":
if (!data) throw new Error("Missing background data!");
this.background.type = 1;
this.background.data = data;
break;
default:
throw new Error(`Invalid background type "${type}"!`);
}
return this;
}
/**
* This function converts raw data into spotify presence card.
* @returns {Promise<Buffer>}
*/
async build() {
if (!this.title) throw new Error('Missing "title" in options.');
if (!this.artist) throw new Error('Missing "artist" in options.');
if (!this.start) throw new Error('Missing "start" in options.');
if (!this.end) throw new Error('Missing "end" in options.');
const total = this.end - this.start;
const progress = Date.now() - this.start;
const progressF = Util.formatTime(progress > total ? total : progress);
const ending = Util.formatTime(total);
const canvas = Canvas.createCanvas(600, 150);
const ctx = canvas.getContext("2d");
// background
ctx.beginPath();
if (this.background.type === 0) {
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = this.background.data || "#2F3136";
ctx.fillRect(0, 0, canvas.width, canvas.height);
} else {
let img = await Canvas.loadImage(this.background.data);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
// draw image
const img = await Canvas.loadImage(this.image);
ctx.drawImage(img, 30, 15, 120, 120);
// draw songname
ctx.fillStyle = "#FFFFFF";
ctx.font = "bold 20px Manrope";
await Util.renderEmoji(ctx, Util.shorten(this.title, 30), 170, 40);
// draw artist name
ctx.fillStyle = "#F1F1F1";
ctx.font = "14px Manrope";
await Util.renderEmoji(ctx, `by ${Util.shorten(this.artist, 40)}`, 170, 70);
// add album
if (this.album && typeof this.album === "string") {
ctx.fillStyle = "#F1F1F1";
ctx.font = "14px Manrope";
await Util.renderEmoji(ctx, `on ${Util.shorten(this.album, 40)}`, 170, 90);
}
// ending point
ctx.fillStyle = "#B3B3B3";
ctx.font = "14px Manrope";
await Util.renderEmoji(ctx, ending, 430, 130);
// progress
ctx.fillStyle = "#B3B3B3";
ctx.font = "14px Manrope";
await Util.renderEmoji(ctx, progressF, 170, 130);
// progressbar track
ctx.rect(170, 170, 300, 4);
ctx.fillStyle = "#E8E8E8";
ctx.fillRect(170, 110, 300, 4);
// progressbar
ctx.fillStyle = "#1DB954";
ctx.fillRect(170, 110, this.__calculateProgress(progress, total), 4);
// return
return canvas.toBuffer();
}
/**
* Returns progress
* @type {number}
* @private
* @ignore
*/
__calculateProgress(progress, total) {
let prg = (progress / total) * 300;
if (isNaN(prg) || prg < 0) return 0;
if (prg > 300) return 300;
return prg;
}
}
module.exports = Spotify;