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
|
||||
PMTWO=false
|
||||
# Set this to true if you want to use the image API
|
||||
API=false
|
||||
|
||||
# Enable/disable Twitter bot (true/false)
|
||||
TWITTER=false
|
||||
|
|
65
api/index.js
65
api/index.js
|
@ -1,11 +1,14 @@
|
|||
require("dotenv").config();
|
||||
const magick = require("../utils/image.js");
|
||||
const Job = require("./job.js");
|
||||
const { version } = require("../package.json");
|
||||
const express = require("express");
|
||||
const execPromise = require("util").promisify(require("child_process").exec);
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
const jobs = new Map();
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.send(`esmBot v${version}`);
|
||||
});
|
||||
|
@ -22,19 +25,71 @@ app.post("/run", express.json(), async (req, res, next) => {
|
|||
return res.sendStatus(400);
|
||||
}
|
||||
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", "");
|
||||
object.delay = (100 / delay.split("/")[0]) * delay.split("/")[1];
|
||||
}
|
||||
|
||||
const data = await magick.run(object, true);
|
||||
res.contentType(type ? type : "png");
|
||||
res.send(data);
|
||||
const job = new Job(object);
|
||||
jobs.set(id, job);
|
||||
res.send({
|
||||
id: id,
|
||||
status: "queued"
|
||||
});
|
||||
job.run();
|
||||
} catch (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, () => {
|
||||
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 logger = require("../utils/logger.js");
|
||||
const misc = require("../utils/misc.js");
|
||||
const client = require("../utils/client.js");
|
||||
|
||||
// run when the bot is added to a 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({
|
||||
id: guild.id,
|
||||
tags: misc.tagDefaults,
|
||||
|
@ -15,4 +14,5 @@ module.exports = async (guild) => {
|
|||
tagsDisabled: false
|
||||
});
|
||||
await guildDB.save();
|
||||
return guildDB;
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ const client = require("../utils/client.js");
|
|||
const database = require("../utils/database.js");
|
||||
const logger = require("../utils/logger.js");
|
||||
const collections = require("../utils/collections.js");
|
||||
const guildCreate = require("./guildCreate.js");
|
||||
const commands = [...collections.aliases.keys(), ...collections.commands.keys()];
|
||||
|
||||
// run when someone sends a message
|
||||
|
@ -24,7 +25,10 @@ module.exports = async (message) => {
|
|||
if (!valid) return;
|
||||
|
||||
// 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
|
||||
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
|
||||
});
|
||||
await newGuild.save();
|
||||
} else if (guildDB) {
|
||||
} else {
|
||||
if (!guildDB.warns) {
|
||||
logger.log(`Creating warn object for guild ${id}...`);
|
||||
guildDB.set("warns", {});
|
||||
|
|
|
@ -23,10 +23,10 @@ class CaptionWorker : public Napi::AsyncWorker {
|
|||
Image caption_image(Geometry(query), Color("white"));
|
||||
caption_image.fillColor("black");
|
||||
caption_image.alpha(true);
|
||||
caption_image.font("./assets/caption.otf");
|
||||
caption_image.font("Futura");
|
||||
caption_image.fontPointsize(width / 10);
|
||||
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);
|
||||
|
||||
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 fileType = require("file-type");
|
||||
const execPromise = promisify(require("child_process").exec);
|
||||
const servers = require("../servers.json").image;
|
||||
|
||||
const formats = ["image/jpeg", "image/png", "image/webp", "image/gif"];
|
||||
|
||||
exports.run = async (object, fromAPI = false) => {
|
||||
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",
|
||||
body: JSON.stringify(object),
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
const buffer = await req.buffer();
|
||||
console.log(buffer.toString());
|
||||
if (buffer.toString() === "nogif") return {
|
||||
const json = await req.json();
|
||||
if (json.status === "nogif") return {
|
||||
buffer: "nogif",
|
||||
type: null
|
||||
};
|
||||
return {
|
||||
buffer: buffer,
|
||||
type: req.headers.get("content-type").split("/")[1]
|
||||
};
|
||||
let data;
|
||||
while (!data) {
|
||||
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 {
|
||||
let type;
|
||||
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 {
|
||||
buffer: "nogif",
|
||||
type: null
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const fetch = require("node-fetch");
|
||||
const execPromise = require("util").promisify(require("child_process").exec);
|
||||
|
||||
// gets the proper image paths
|
||||
const getImage = async (image, image2, gifv = false) => {
|
||||
|
@ -8,10 +9,15 @@ const getImage = async (image, image2, gifv = false) => {
|
|||
path: image
|
||||
};
|
||||
if (gifv) {
|
||||
if (image2.includes("tenor.com") && 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();
|
||||
payload.path = json.results[0].media[0].gif.url;
|
||||
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 json = await data.json();
|
||||
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")) {
|
||||
payload.path = `https://media0.giphy.com/media/${image2.split("-").pop()}/giphy.gif`;
|
||||
} else if (image2.includes("imgur.com")) {
|
||||
|
|
|
@ -6,7 +6,7 @@ const moment = require("moment");
|
|||
require("moment-duration-format");
|
||||
const { Manager } = require("@lavacord/eris");
|
||||
|
||||
const nodes = require("../lavanodes.json");
|
||||
const nodes = require("../servers.json").lava;
|
||||
|
||||
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!`);
|
||||
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 (!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 { 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);
|
||||
|
@ -59,11 +60,16 @@ exports.play = async (sound, message, music = false) => {
|
|||
if (music) {
|
||||
this.queues.set(voiceChannel.guild.id, oldQueue ? [...oldQueue, tracks[0].track] : [tracks[0].track]);
|
||||
}
|
||||
const connection = await this.manager.join({
|
||||
guild: voiceChannel.guild.id,
|
||||
channel: voiceChannel.id,
|
||||
node: node.id
|
||||
});
|
||||
let connection;
|
||||
if (player) {
|
||||
connection = player;
|
||||
} else {
|
||||
connection = await this.manager.join({
|
||||
guild: voiceChannel.guild.id,
|
||||
channel: voiceChannel.id,
|
||||
node: node.id
|
||||
});
|
||||
}
|
||||
|
||||
if (oldQueue && music) {
|
||||
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);
|
||||
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) => {
|
||||
if (playingMessage.channel.messages.get(playingMessage.id)) playingMessage.delete();
|
||||
this.manager.leave(voiceChannel.guild.id);
|
||||
|
@ -111,24 +117,26 @@ exports.nextSong = async (message, connection, track, info, music, voiceChannel,
|
|||
logger.error(error);
|
||||
});
|
||||
}
|
||||
connection.on("end", async (data) => {
|
||||
if (data.reason === "REPLACED") return;
|
||||
const queue = this.queues.get(voiceChannel.guild.id);
|
||||
const newQueue = queue ? queue.slice(1) : [];
|
||||
this.queues.set(voiceChannel.guild.id, newQueue);
|
||||
if (newQueue.length === 0) {
|
||||
this.manager.leave(voiceChannel.guild.id);
|
||||
connection.destroy();
|
||||
this.players.delete(voiceChannel.guild.id);
|
||||
this.queues.delete(voiceChannel.guild.id);
|
||||
if (music) await client.createMessage(message.channel.id, "🔊 The current voice channel session has ended.");
|
||||
if (playingMessage.channel.messages.get(playingMessage.id)) await playingMessage.delete();
|
||||
} else {
|
||||
const track = await fetch(`http://${connection.node.host}:${connection.node.port}/decodetrack?track=${encodeURIComponent(newQueue[0])}`, { headers: { Authorization: connection.node.password } }).then(res => res.json());
|
||||
this.nextSong(message, connection, newQueue[0], track, music, voiceChannel, true);
|
||||
if (playingMessage.channel.messages.get(playingMessage.id)) await playingMessage.delete();
|
||||
}
|
||||
});
|
||||
if (connection.listeners("end").length === 0) {
|
||||
connection.on("end", async (data) => {
|
||||
if (data.reason === "REPLACED") return;
|
||||
const queue = this.queues.get(voiceChannel.guild.id);
|
||||
const newQueue = queue ? queue.slice(1) : [];
|
||||
this.queues.set(voiceChannel.guild.id, newQueue);
|
||||
if (newQueue.length === 0) {
|
||||
this.manager.leave(voiceChannel.guild.id);
|
||||
connection.destroy();
|
||||
this.players.delete(voiceChannel.guild.id);
|
||||
this.queues.delete(voiceChannel.guild.id);
|
||||
if (music) await client.createMessage(message.channel.id, "🔊 The current voice channel session has ended.");
|
||||
if (playingMessage.channel.messages.get(playingMessage.id)) await playingMessage.delete();
|
||||
} else {
|
||||
const track = await fetch(`http://${connection.node.host}:${connection.node.port}/decodetrack?track=${encodeURIComponent(newQueue[0])}`, { headers: { Authorization: connection.node.password } }).then(res => res.json());
|
||||
this.nextSong(message, connection, newQueue[0], track, music, voiceChannel, true);
|
||||
if (playingMessage.channel.messages.get(playingMessage.id)) await playingMessage.delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.stop = async (message) => {
|
||||
|
|
Loading…
Reference in a new issue