Caption now supports more unicode characters, more api work, fixed multiple bugs
This commit is contained in:
parent
a03d3a5e79
commit
0600cf230f
11 changed files with 174 additions and 50 deletions
|
@ -40,6 +40,8 @@ TEMPDIR=
|
||||||
|
|
||||||
# Set this to true if you're using PM2 to manage the bot
|
# Set this to true if you're using PM2 to manage the bot
|
||||||
PMTWO=false
|
PMTWO=false
|
||||||
|
# Set this to true if you want to use the image API
|
||||||
|
API=false
|
||||||
|
|
||||||
# Enable/disable Twitter bot (true/false)
|
# Enable/disable Twitter bot (true/false)
|
||||||
TWITTER=false
|
TWITTER=false
|
||||||
|
|
65
api/index.js
65
api/index.js
|
@ -1,11 +1,14 @@
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
const magick = require("../utils/image.js");
|
const magick = require("../utils/image.js");
|
||||||
|
const Job = require("./job.js");
|
||||||
const { version } = require("../package.json");
|
const { version } = require("../package.json");
|
||||||
const express = require("express");
|
const express = require("express");
|
||||||
const execPromise = require("util").promisify(require("child_process").exec);
|
const execPromise = require("util").promisify(require("child_process").exec);
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 3000;
|
const port = 3000;
|
||||||
|
|
||||||
|
const jobs = new Map();
|
||||||
|
|
||||||
app.get("/", (req, res) => {
|
app.get("/", (req, res) => {
|
||||||
res.send(`esmBot v${version}`);
|
res.send(`esmBot v${version}`);
|
||||||
});
|
});
|
||||||
|
@ -22,19 +25,71 @@ app.post("/run", express.json(), async (req, res, next) => {
|
||||||
return res.sendStatus(400);
|
return res.sendStatus(400);
|
||||||
}
|
}
|
||||||
object.type = type.split("/")[1];
|
object.type = type.split("/")[1];
|
||||||
if (object.type !== "gif" && object.onlyGIF) return res.send("nogif");
|
if (object.type !== "gif" && object.onlyGIF) return res.send({
|
||||||
|
status: "nogif"
|
||||||
|
});
|
||||||
|
object.delay = object.delay ? object.delay : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = Math.random().toString(36).substring(2, 15);
|
||||||
|
if (object.type === "gif" && !object.delay) {
|
||||||
const delay = (await execPromise(`ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate ${object.path}`)).stdout.replace("\n", "");
|
const delay = (await execPromise(`ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate ${object.path}`)).stdout.replace("\n", "");
|
||||||
object.delay = (100 / delay.split("/")[0]) * delay.split("/")[1];
|
object.delay = (100 / delay.split("/")[0]) * delay.split("/")[1];
|
||||||
}
|
}
|
||||||
|
const job = new Job(object);
|
||||||
const data = await magick.run(object, true);
|
jobs.set(id, job);
|
||||||
res.contentType(type ? type : "png");
|
res.send({
|
||||||
res.send(data);
|
id: id,
|
||||||
|
status: "queued"
|
||||||
|
});
|
||||||
|
job.run();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next(e);
|
next(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get("/status", (req, res) => {
|
||||||
|
if (!req.query.id) return res.sendStatus(400);
|
||||||
|
const job = jobs.get(req.query.id);
|
||||||
|
if (!job) return res.sendStatus(400);
|
||||||
|
const timeout = setTimeout(function() {
|
||||||
|
job.removeAllListeners();
|
||||||
|
return res.send({
|
||||||
|
id: req.query.id,
|
||||||
|
status: job.status
|
||||||
|
});
|
||||||
|
}, 10000);
|
||||||
|
job.once("data", function() {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
res.send({
|
||||||
|
id: req.query.id,
|
||||||
|
status: job.status
|
||||||
|
});
|
||||||
|
//jobs.delete(req.query.id);
|
||||||
|
});
|
||||||
|
job.on("error", function(e) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
res.status(500);
|
||||||
|
res.send({
|
||||||
|
id: req.query.id,
|
||||||
|
status: job.status,
|
||||||
|
error: e
|
||||||
|
});
|
||||||
|
jobs.delete(req.query.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/image", (req, res) => {
|
||||||
|
if (!req.query.id) return res.sendStatus(400);
|
||||||
|
const job = jobs.get(req.query.id);
|
||||||
|
if (!job) return res.sendStatus(400);
|
||||||
|
if (!job.data) return res.sendStatus(400);
|
||||||
|
if (job.error) return;
|
||||||
|
jobs.delete(req.query.id);
|
||||||
|
res.contentType(job.options.type ? job.options.type : "png");
|
||||||
|
return res.send(job.data);
|
||||||
|
});
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
console.log(`Started image API on port ${port}.`);
|
console.log(`Started image API on port ${port}.`);
|
||||||
});
|
});
|
28
api/job.js
Normal file
28
api/job.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
const { EventEmitter } = require("events");
|
||||||
|
const magick = require("../utils/image.js");
|
||||||
|
|
||||||
|
class Job extends EventEmitter {
|
||||||
|
constructor(options) {
|
||||||
|
super();
|
||||||
|
this.options = options;
|
||||||
|
this.status = "queued";
|
||||||
|
this.data = null;
|
||||||
|
this.error = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
this.status = "processing";
|
||||||
|
magick.run(this.options, true).then(data => {
|
||||||
|
this.status = "success";
|
||||||
|
this.data = data;
|
||||||
|
return this.emit("data", data, this.options.type);
|
||||||
|
}).catch(e => {
|
||||||
|
this.status = "error";
|
||||||
|
this.error = e;
|
||||||
|
return this.emit("error", e);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Job;
|
|
@ -1,11 +1,10 @@
|
||||||
const db = require("../utils/database.js");
|
const db = require("../utils/database.js");
|
||||||
const logger = require("../utils/logger.js");
|
const logger = require("../utils/logger.js");
|
||||||
const misc = require("../utils/misc.js");
|
const misc = require("../utils/misc.js");
|
||||||
const client = require("../utils/client.js");
|
|
||||||
|
|
||||||
// run when the bot is added to a guild
|
// run when the bot is added to a guild
|
||||||
module.exports = async (guild) => {
|
module.exports = async (guild) => {
|
||||||
logger.log("info", `[GUILD JOIN] ${guild.name} (${guild.id}) added the bot. Owner: ${client.users.get(guild.ownerID).username}#${client.users.get(guild.ownerID).discriminator} (${guild.ownerID})`);
|
logger.log("info", `[GUILD JOIN] ${guild.name} (${guild.id}) added the bot.`);
|
||||||
const guildDB = new db.guilds({
|
const guildDB = new db.guilds({
|
||||||
id: guild.id,
|
id: guild.id,
|
||||||
tags: misc.tagDefaults,
|
tags: misc.tagDefaults,
|
||||||
|
@ -15,4 +14,5 @@ module.exports = async (guild) => {
|
||||||
tagsDisabled: false
|
tagsDisabled: false
|
||||||
});
|
});
|
||||||
await guildDB.save();
|
await guildDB.save();
|
||||||
|
return guildDB;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ const client = require("../utils/client.js");
|
||||||
const database = require("../utils/database.js");
|
const database = require("../utils/database.js");
|
||||||
const logger = require("../utils/logger.js");
|
const logger = require("../utils/logger.js");
|
||||||
const collections = require("../utils/collections.js");
|
const collections = require("../utils/collections.js");
|
||||||
|
const guildCreate = require("./guildCreate.js");
|
||||||
const commands = [...collections.aliases.keys(), ...collections.commands.keys()];
|
const commands = [...collections.aliases.keys(), ...collections.commands.keys()];
|
||||||
|
|
||||||
// run when someone sends a message
|
// run when someone sends a message
|
||||||
|
@ -24,7 +25,10 @@ module.exports = async (message) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
|
|
||||||
// prefix can be a mention or a set of special characters
|
// prefix can be a mention or a set of special characters
|
||||||
const guildDB = message.channel.guild ? await database.guilds.findOne({ id: message.channel.guild.id }).lean().exec() : null;
|
let guildDB = message.channel.guild ? await database.guilds.findOne({ id: message.channel.guild.id }).lean().exec() : null;
|
||||||
|
if (message.channel.guild && !guildDB) {
|
||||||
|
guildDB = await guildCreate(message.channel.guild);
|
||||||
|
}
|
||||||
// there's a bit of a workaround here due to member.mention not accounting for both mention types
|
// there's a bit of a workaround here due to member.mention not accounting for both mention types
|
||||||
const prefix = message.channel.guild ? (message.content.startsWith(message.channel.guild.members.get(client.user.id).mention) ? `${message.channel.guild.members.get(client.user.id).mention} ` : (message.content.startsWith(`<@${client.user.id}>`) ? `<@${client.user.id}> ` : guildDB.prefix)) : "";
|
const prefix = message.channel.guild ? (message.content.startsWith(message.channel.guild.members.get(client.user.id).mention) ? `${message.channel.guild.members.get(client.user.id).mention} ` : (message.content.startsWith(`<@${client.user.id}>`) ? `<@${client.user.id}> ` : guildDB.prefix)) : "";
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ module.exports = async () => {
|
||||||
tagsDisabled: false
|
tagsDisabled: false
|
||||||
});
|
});
|
||||||
await newGuild.save();
|
await newGuild.save();
|
||||||
} else if (guildDB) {
|
} else {
|
||||||
if (!guildDB.warns) {
|
if (!guildDB.warns) {
|
||||||
logger.log(`Creating warn object for guild ${id}...`);
|
logger.log(`Creating warn object for guild ${id}...`);
|
||||||
guildDB.set("warns", {});
|
guildDB.set("warns", {});
|
||||||
|
|
|
@ -23,10 +23,10 @@ class CaptionWorker : public Napi::AsyncWorker {
|
||||||
Image caption_image(Geometry(query), Color("white"));
|
Image caption_image(Geometry(query), Color("white"));
|
||||||
caption_image.fillColor("black");
|
caption_image.fillColor("black");
|
||||||
caption_image.alpha(true);
|
caption_image.alpha(true);
|
||||||
caption_image.font("./assets/caption.otf");
|
caption_image.font("Futura");
|
||||||
caption_image.fontPointsize(width / 10);
|
caption_image.fontPointsize(width / 10);
|
||||||
caption_image.textGravity(Magick::CenterGravity);
|
caption_image.textGravity(Magick::CenterGravity);
|
||||||
caption_image.read("caption:" + caption);
|
caption_image.read("pango:" + caption);
|
||||||
caption_image.extent(Geometry(width, caption_image.rows() + (width / 10)), Magick::CenterGravity);
|
caption_image.extent(Geometry(width, caption_image.rows() + (width / 10)), Magick::CenterGravity);
|
||||||
|
|
||||||
coalesceImages(&coalesced, frames.begin(), frames.end());
|
coalesceImages(&coalesced, frames.begin(), frames.end());
|
||||||
|
|
8
servers.json
Normal file
8
servers.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"lava": [
|
||||||
|
{ "id": "1", "host": "localhost", "port": 2333, "password": "youshallnotpass" }
|
||||||
|
],
|
||||||
|
"image": [
|
||||||
|
"http://localhost:3000"
|
||||||
|
]
|
||||||
|
}
|
|
@ -4,32 +4,45 @@ const { promisify } = require("util");
|
||||||
const AbortController = require("abort-controller");
|
const AbortController = require("abort-controller");
|
||||||
const fileType = require("file-type");
|
const fileType = require("file-type");
|
||||||
const execPromise = promisify(require("child_process").exec);
|
const execPromise = promisify(require("child_process").exec);
|
||||||
|
const servers = require("../servers.json").image;
|
||||||
|
|
||||||
const formats = ["image/jpeg", "image/png", "image/webp", "image/gif"];
|
const formats = ["image/jpeg", "image/png", "image/webp", "image/gif"];
|
||||||
|
|
||||||
exports.run = async (object, fromAPI = false) => {
|
exports.run = async (object, fromAPI = false) => {
|
||||||
if (process.env.API === "true" && !fromAPI) {
|
if (process.env.API === "true" && !fromAPI) {
|
||||||
const req = await fetch(`${process.env.API_URL}/run`, {
|
const currentServer = servers[Math.floor(Math.random() * servers.length)];
|
||||||
|
const req = await fetch(`${currentServer}/run`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(object),
|
body: JSON.stringify(object),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const buffer = await req.buffer();
|
const json = await req.json();
|
||||||
console.log(buffer.toString());
|
if (json.status === "nogif") return {
|
||||||
if (buffer.toString() === "nogif") return {
|
|
||||||
buffer: "nogif",
|
buffer: "nogif",
|
||||||
type: null
|
type: null
|
||||||
};
|
};
|
||||||
return {
|
let data;
|
||||||
buffer: buffer,
|
while (!data) {
|
||||||
type: req.headers.get("content-type").split("/")[1]
|
const statusReq = await fetch(`${currentServer}/status?id=${json.id}`);
|
||||||
|
const statusJSON = await statusReq.json();
|
||||||
|
if (statusJSON.status === "success") {
|
||||||
|
const imageReq = await fetch(`${currentServer}/image?id=${json.id}`);
|
||||||
|
data = {
|
||||||
|
buffer: await imageReq.buffer(),
|
||||||
|
type: imageReq.headers.get("content-type").split("/")[1]
|
||||||
};
|
};
|
||||||
|
} else if (statusJSON.status === "error") {
|
||||||
|
throw new Error(statusJSON.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
} else {
|
} else {
|
||||||
let type;
|
let type;
|
||||||
if (!fromAPI && object.path) {
|
if (!fromAPI && object.path) {
|
||||||
type = (object.type ? object.type : await this.getType(object.path)).split("/")[1];
|
const newType = (object.type ? object.type : await this.getType(object.path));
|
||||||
|
type = newType ? newType.split("/")[1] : "png";
|
||||||
if (type !== "gif" && object.onlyGIF) return {
|
if (type !== "gif" && object.onlyGIF) return {
|
||||||
buffer: "nogif",
|
buffer: "nogif",
|
||||||
type: null
|
type: null
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const fetch = require("node-fetch");
|
const fetch = require("node-fetch");
|
||||||
|
const execPromise = require("util").promisify(require("child_process").exec);
|
||||||
|
|
||||||
// gets the proper image paths
|
// gets the proper image paths
|
||||||
const getImage = async (image, image2, gifv = false) => {
|
const getImage = async (image, image2, gifv = false) => {
|
||||||
|
@ -8,10 +9,15 @@ const getImage = async (image, image2, gifv = false) => {
|
||||||
path: image
|
path: image
|
||||||
};
|
};
|
||||||
if (gifv) {
|
if (gifv) {
|
||||||
if (image2.includes("tenor.com") && process.env.TENOR !== "") {
|
if (image2.includes("tenor.com")) {
|
||||||
|
if (process.env.TENOR !== "") {
|
||||||
const data = await fetch(`https://api.tenor.com/v1/gifs?ids=${image2.split("-").pop()}&key=${process.env.TENOR}`);
|
const data = await fetch(`https://api.tenor.com/v1/gifs?ids=${image2.split("-").pop()}&key=${process.env.TENOR}`);
|
||||||
const json = await data.json();
|
const json = await data.json();
|
||||||
payload.path = json.results[0].media[0].gif.url;
|
payload.path = json.results[0].media[0].gif.url;
|
||||||
|
} else {
|
||||||
|
const delay = (await execPromise(`ffprobe -v 0 -of csv=p=0 -select_streams v:0 -show_entries stream=r_frame_rate ${image}`)).stdout.replace("\n", "");
|
||||||
|
payload.delay = (100 / delay.split("/")[0]) * delay.split("/")[1];
|
||||||
|
}
|
||||||
} else if (image2.includes("giphy.com")) {
|
} else if (image2.includes("giphy.com")) {
|
||||||
payload.path = `https://media0.giphy.com/media/${image2.split("-").pop()}/giphy.gif`;
|
payload.path = `https://media0.giphy.com/media/${image2.split("-").pop()}/giphy.gif`;
|
||||||
} else if (image2.includes("imgur.com")) {
|
} else if (image2.includes("imgur.com")) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ const moment = require("moment");
|
||||||
require("moment-duration-format");
|
require("moment-duration-format");
|
||||||
const { Manager } = require("@lavacord/eris");
|
const { Manager } = require("@lavacord/eris");
|
||||||
|
|
||||||
const nodes = require("../lavanodes.json");
|
const nodes = require("../servers.json").lava;
|
||||||
|
|
||||||
exports.players = new Map();
|
exports.players = new Map();
|
||||||
|
|
||||||
|
@ -51,7 +51,8 @@ exports.play = async (sound, message, music = false) => {
|
||||||
if (!message.channel.guild.members.get(client.user.id).permission.has("voiceConnect") || !message.channel.permissionsOf(client.user.id).has("voiceConnect")) return client.createMessage(message.channel.id, `${message.author.mention}, I can't join this voice channel!`);
|
if (!message.channel.guild.members.get(client.user.id).permission.has("voiceConnect") || !message.channel.permissionsOf(client.user.id).has("voiceConnect")) return client.createMessage(message.channel.id, `${message.author.mention}, I can't join this voice channel!`);
|
||||||
const voiceChannel = message.channel.guild.channels.get(message.member.voiceState.channelID);
|
const voiceChannel = message.channel.guild.channels.get(message.member.voiceState.channelID);
|
||||||
if (!voiceChannel.permissionsOf(client.user.id).has("voiceConnect")) return client.createMessage(message.channel.id, `${message.author.mention}, I don't have permission to join this voice channel!`);
|
if (!voiceChannel.permissionsOf(client.user.id).has("voiceConnect")) return client.createMessage(message.channel.id, `${message.author.mention}, I don't have permission to join this voice channel!`);
|
||||||
if (!music && this.manager.voiceStates.has(message.channel.guild.id) && this.players.get(message.channel.guild.id).type === "music") return client.createMessage(message.channel.id, `${message.author.mention}, I can't play a sound effect while playing music!`);
|
const player = this.players.get(message.channel.guild.id);
|
||||||
|
if (!music && this.manager.voiceStates.has(message.channel.guild.id) && (player && player.type === "music")) return client.createMessage(message.channel.id, `${message.author.mention}, I can't play a sound effect while playing music!`);
|
||||||
const node = this.manager.idealNodes[0];
|
const node = this.manager.idealNodes[0];
|
||||||
const { tracks } = await fetch(`http://${node.host}:${node.port}/loadtracks?identifier=${sound}`, { headers: { Authorization: node.password } }).then(res => res.json());
|
const { tracks } = await fetch(`http://${node.host}:${node.port}/loadtracks?identifier=${sound}`, { headers: { Authorization: node.password } }).then(res => res.json());
|
||||||
const oldQueue = this.queues.get(voiceChannel.guild.id);
|
const oldQueue = this.queues.get(voiceChannel.guild.id);
|
||||||
|
@ -59,11 +60,16 @@ exports.play = async (sound, message, music = false) => {
|
||||||
if (music) {
|
if (music) {
|
||||||
this.queues.set(voiceChannel.guild.id, oldQueue ? [...oldQueue, tracks[0].track] : [tracks[0].track]);
|
this.queues.set(voiceChannel.guild.id, oldQueue ? [...oldQueue, tracks[0].track] : [tracks[0].track]);
|
||||||
}
|
}
|
||||||
const connection = await this.manager.join({
|
let connection;
|
||||||
|
if (player) {
|
||||||
|
connection = player;
|
||||||
|
} else {
|
||||||
|
connection = await this.manager.join({
|
||||||
guild: voiceChannel.guild.id,
|
guild: voiceChannel.guild.id,
|
||||||
channel: voiceChannel.id,
|
channel: voiceChannel.id,
|
||||||
node: node.id
|
node: node.id
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (oldQueue && music) {
|
if (oldQueue && music) {
|
||||||
client.createMessage(message.channel.id, `${message.author.mention}, your tune has been added to the queue!`);
|
client.createMessage(message.channel.id, `${message.author.mention}, your tune has been added to the queue!`);
|
||||||
|
@ -101,7 +107,7 @@ exports.nextSong = async (message, connection, track, info, music, voiceChannel,
|
||||||
});
|
});
|
||||||
await connection.play(track);
|
await connection.play(track);
|
||||||
this.players.set(voiceChannel.guild.id, { player: connection, type: music ? "music" : "sound", host: message.author.id, voiceChannel: voiceChannel, originalChannel: message.channel });
|
this.players.set(voiceChannel.guild.id, { player: connection, type: music ? "music" : "sound", host: message.author.id, voiceChannel: voiceChannel, originalChannel: message.channel });
|
||||||
if (inQueue) {
|
if (inQueue && connection.listeners("error").length === 0) {
|
||||||
connection.on("error", (error) => {
|
connection.on("error", (error) => {
|
||||||
if (playingMessage.channel.messages.get(playingMessage.id)) playingMessage.delete();
|
if (playingMessage.channel.messages.get(playingMessage.id)) playingMessage.delete();
|
||||||
this.manager.leave(voiceChannel.guild.id);
|
this.manager.leave(voiceChannel.guild.id);
|
||||||
|
@ -111,6 +117,7 @@ exports.nextSong = async (message, connection, track, info, music, voiceChannel,
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (connection.listeners("end").length === 0) {
|
||||||
connection.on("end", async (data) => {
|
connection.on("end", async (data) => {
|
||||||
if (data.reason === "REPLACED") return;
|
if (data.reason === "REPLACED") return;
|
||||||
const queue = this.queues.get(voiceChannel.guild.id);
|
const queue = this.queues.get(voiceChannel.guild.id);
|
||||||
|
@ -129,6 +136,7 @@ exports.nextSong = async (message, connection, track, info, music, voiceChannel,
|
||||||
if (playingMessage.channel.messages.get(playingMessage.id)) await playingMessage.delete();
|
if (playingMessage.channel.messages.get(playingMessage.id)) await playingMessage.delete();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.stop = async (message) => {
|
exports.stop = async (message) => {
|
||||||
|
|
Loading…
Reference in a new issue