initial update
This commit is contained in:
parent
3272429cf6
commit
db9b70bf66
280 changed files with 11772 additions and 11966 deletions
|
@ -1,71 +1,71 @@
|
|||
class Command {
|
||||
success = true;
|
||||
constructor(client, options) {
|
||||
this.client = client;
|
||||
this.origOptions = options;
|
||||
this.type = options.type;
|
||||
this.args = options.args;
|
||||
if (options.type === "classic") {
|
||||
this.message = options.message;
|
||||
this.channel = options.message.channel;
|
||||
this.guild = options.message.guild;
|
||||
this.author = options.message.author;
|
||||
this.member = options.message.member;
|
||||
this.content = options.content;
|
||||
this.options = options.specialArgs;
|
||||
this.reference = {
|
||||
messageReference: {
|
||||
channelID: this.message.channelID,
|
||||
messageID: this.message.id,
|
||||
guildID: this.message.guildID ?? undefined,
|
||||
failIfNotExists: false
|
||||
},
|
||||
allowedMentions: {
|
||||
repliedUser: false
|
||||
}
|
||||
};
|
||||
} else if (options.type === "application") {
|
||||
this.interaction = options.interaction;
|
||||
this.args = [];
|
||||
this.channel = options.interaction.channel;
|
||||
this.guild = options.interaction.guild;
|
||||
this.author = this.member = options.interaction.guildID ? options.interaction.member : options.interaction.user;
|
||||
if (options.interaction.data.options) {
|
||||
this.options = options.interaction.data.options.raw.reduce((obj, item) => {
|
||||
obj[item.name] = item.value;
|
||||
return obj;
|
||||
}, {});
|
||||
this.optionsArray = options.interaction.data.options.raw;
|
||||
} else {
|
||||
this.options = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async run() {
|
||||
return "It works!";
|
||||
}
|
||||
|
||||
async acknowledge() {
|
||||
if (this.type === "classic") {
|
||||
const channel = this.channel ?? await this.client.rest.channels.get(this.message.channelID);
|
||||
await channel.sendTyping();
|
||||
} else if (!this.interaction.acknowledged) {
|
||||
await this.interaction.defer();
|
||||
}
|
||||
}
|
||||
|
||||
static init() {
|
||||
return this;
|
||||
}
|
||||
|
||||
static description = "No description found";
|
||||
static aliases = [];
|
||||
static arguments = [];
|
||||
static flags = [];
|
||||
static slashAllowed = true;
|
||||
static directAllowed = true;
|
||||
static adminOnly = false;
|
||||
}
|
||||
|
||||
class Command {
|
||||
success = true;
|
||||
constructor(matrixClient, options) {
|
||||
this.client = matrixClient;
|
||||
this.origOptions = options;
|
||||
this.type = options.type;
|
||||
this.args = options.args;
|
||||
if (options.type === "classic") {
|
||||
this.message = options.message;
|
||||
this.channel = options.message.room_id;
|
||||
this.guild = options.message.guild;
|
||||
this.author = options.message.sender;
|
||||
this.member = options.message.member;
|
||||
this.content = options.content;
|
||||
this.options = options.specialArgs;
|
||||
this.reference = {
|
||||
messageReference: {
|
||||
channelID: this.message.channelID,
|
||||
messageID: this.message.id,
|
||||
guildID: this.message.guildID ?? undefined,
|
||||
failIfNotExists: false
|
||||
},
|
||||
allowedMentions: {
|
||||
repliedUser: false
|
||||
}
|
||||
};
|
||||
} else if (options.type === "application") {
|
||||
this.interaction = options.interaction;
|
||||
this.args = [];
|
||||
this.channel = options.interaction.channel;
|
||||
this.guild = options.interaction.guild;
|
||||
this.author = this.member = options.interaction.guildID ? options.interaction.member : options.interaction.user;
|
||||
if (options.interaction.data.options) {
|
||||
this.options = options.interaction.data.options.raw.reduce((obj, item) => {
|
||||
obj[item.name] = item.value;
|
||||
return obj;
|
||||
}, {});
|
||||
this.optionsArray = options.interaction.data.options.raw;
|
||||
} else {
|
||||
this.options = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async run() {
|
||||
return "It works!";
|
||||
}
|
||||
|
||||
async acknowledge() {
|
||||
if (this.type === "classic") {
|
||||
const channel = this.channel;
|
||||
await this.client.sendTyping(channel, true, 5);
|
||||
} else if (!this.interaction.acknowledged) {
|
||||
await this.interaction.defer();
|
||||
}
|
||||
}
|
||||
|
||||
static init() {
|
||||
return this;
|
||||
}
|
||||
|
||||
static description = "No description found";
|
||||
static aliases = [];
|
||||
static arguments = [];
|
||||
static flags = [];
|
||||
static slashAllowed = true;
|
||||
static directAllowed = true;
|
||||
static adminOnly = false;
|
||||
}
|
||||
|
||||
export default Command;
|
|
@ -1,153 +1,160 @@
|
|||
import Command from "./command.js";
|
||||
import imageDetect from "../utils/imagedetect.js";
|
||||
import { runImageJob } from "../utils/image.js";
|
||||
import { runningCommands } from "../utils/collections.js";
|
||||
import { readFileSync } from "fs";
|
||||
const { emotes } = JSON.parse(readFileSync(new URL("../config/messages.json", import.meta.url)));
|
||||
import { random } from "../utils/misc.js";
|
||||
import { selectedImages } from "../utils/collections.js";
|
||||
|
||||
class ImageCommand extends Command {
|
||||
async criteria() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async run() {
|
||||
this.success = false;
|
||||
const timestamp = this.type === "classic" ? this.message.createdAt : Math.floor((this.interaction.id / 4194304) + 1420070400000);
|
||||
// check if this command has already been run in this channel with the same arguments, and we are awaiting its result
|
||||
// if so, don't re-run it
|
||||
if (runningCommands.has(this.author.id) && (new Date(runningCommands.get(this.author.id)) - new Date(timestamp)) < 5000) {
|
||||
return "Please slow down a bit.";
|
||||
}
|
||||
// before awaiting the command result, add this command to the set of running commands
|
||||
runningCommands.set(this.author.id, timestamp);
|
||||
|
||||
const imageParams = {
|
||||
cmd: this.constructor.command,
|
||||
params: {
|
||||
togif: !!this.options.togif
|
||||
},
|
||||
id: (this.interaction ?? this.message).id
|
||||
};
|
||||
|
||||
if (this.type === "application") await this.acknowledge();
|
||||
|
||||
if (this.constructor.requiresImage) {
|
||||
try {
|
||||
const selection = selectedImages.get(this.author.id);
|
||||
const image = selection ?? await imageDetect(this.client, this.message, this.interaction, this.options, true);
|
||||
if (selection) selectedImages.delete(this.author.id);
|
||||
if (image === undefined) {
|
||||
runningCommands.delete(this.author.id);
|
||||
return `${this.constructor.noImage} (Tip: try right-clicking/holding on a message and press Apps -> Select Image, then try again.)`;
|
||||
} else if (image.type === "large") {
|
||||
runningCommands.delete(this.author.id);
|
||||
return "That image is too large (>= 25MB)! Try using a smaller image.";
|
||||
} else if (image.type === "tenorlimit") {
|
||||
runningCommands.delete(this.author.id);
|
||||
return "I've been rate-limited by Tenor. Please try uploading your GIF elsewhere.";
|
||||
}
|
||||
imageParams.path = image.path;
|
||||
imageParams.params.type = image.type;
|
||||
imageParams.url = image.url; // technically not required but can be useful for text filtering
|
||||
imageParams.name = image.name;
|
||||
if (this.constructor.requiresGIF) imageParams.onlyGIF = true;
|
||||
} catch (e) {
|
||||
runningCommands.delete(this.author.id);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.constructor.requiresText) {
|
||||
const text = this.options.text ?? this.args.join(" ").trim();
|
||||
if (text.length === 0 || !await this.criteria(text, imageParams.url)) {
|
||||
runningCommands.delete(this.author.id);
|
||||
return this.constructor.noText;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.params === "function") {
|
||||
Object.assign(imageParams.params, this.params(imageParams.url, imageParams.name));
|
||||
} else if (typeof this.params === "object") {
|
||||
Object.assign(imageParams.params, this.params);
|
||||
}
|
||||
|
||||
let status;
|
||||
if (imageParams.params.type === "image/gif" && this.type === "classic") {
|
||||
status = await this.processMessage(this.message.channel ?? await this.client.rest.channels.get(this.message.channelID));
|
||||
}
|
||||
|
||||
try {
|
||||
const { buffer, type } = await runImageJob(imageParams);
|
||||
if (type === "nogif" && this.constructor.requiresGIF) {
|
||||
return "That isn't a GIF!";
|
||||
}
|
||||
this.success = true;
|
||||
return {
|
||||
contents: buffer,
|
||||
name: `${this.constructor.command}.${type}`
|
||||
};
|
||||
} catch (e) {
|
||||
if (e === "Request ended prematurely due to a closed connection") return "This image job couldn't be completed because the server it was running on went down. Try running your command again.";
|
||||
if (e === "Job timed out" || e === "Timeout") return "The image is taking too long to process (>=15 minutes), so the job was cancelled. Try using a smaller image.";
|
||||
if (e === "No available servers") return "I can't seem to contact the image servers, they might be down or still trying to start up. Please wait a little bit.";
|
||||
throw e;
|
||||
} finally {
|
||||
try {
|
||||
if (status) await status.delete();
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
runningCommands.delete(this.author.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
processMessage(channel) {
|
||||
return channel.createMessage({
|
||||
content: `${random(emotes) || process.env.PROCESSING_EMOJI || "<a:processing:479351417102925854>"} Processing... This might take a while`
|
||||
});
|
||||
}
|
||||
|
||||
static init() {
|
||||
this.flags = [];
|
||||
if (this.requiresText || this.textOptional) {
|
||||
this.flags.push({
|
||||
name: "text",
|
||||
type: 3,
|
||||
description: "The text to put on the image",
|
||||
required: !this.textOptional
|
||||
});
|
||||
}
|
||||
if (this.requiresImage) {
|
||||
this.flags.push({
|
||||
name: "image",
|
||||
type: 11,
|
||||
description: "An image/GIF attachment"
|
||||
}, {
|
||||
name: "link",
|
||||
type: 3,
|
||||
description: "An image/GIF URL"
|
||||
});
|
||||
}
|
||||
this.flags.push({
|
||||
name: "togif",
|
||||
type: 5,
|
||||
description: "Force GIF output"
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
static allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto", "times", "comic sans ms"];
|
||||
|
||||
static requiresImage = true;
|
||||
static requiresText = false;
|
||||
static textOptional = false;
|
||||
static requiresGIF = false;
|
||||
static noImage = "You need to provide an image/GIF!";
|
||||
static noText = "You need to provide some text!";
|
||||
static command = "";
|
||||
}
|
||||
|
||||
export default ImageCommand;
|
||||
import Command from "./command.js";
|
||||
import imageDetect from "../utils/imagedetect.js";
|
||||
import { runImageJob } from "../utils/image.js";
|
||||
import { runningCommands } from "../utils/collections.js";
|
||||
import { readFileSync } from "fs";
|
||||
const { emotes } = JSON.parse(readFileSync(new URL("../config/messages.json", import.meta.url)));
|
||||
import { random } from "../utils/misc.js";
|
||||
import { selectedImages } from "../utils/collections.js";
|
||||
|
||||
class ImageCommand extends Command {
|
||||
async criteria() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async run() {
|
||||
this.success = false;
|
||||
const timestamp = this.type === "classic" ? this.message.createdAt : Math.floor((this.interaction.id / 4194304) + 1420070400000);
|
||||
// check if this command has already been run in this channel with the same arguments, and we are awaiting its result
|
||||
// if so, don't re-run it
|
||||
if (runningCommands.has(this.author) && (new Date(runningCommands.get(this.author)) - new Date(timestamp)) < 5000) {
|
||||
return "Please slow down a bit.";
|
||||
}
|
||||
// before awaiting the command result, add this command to the set of running commands
|
||||
runningCommands.set(this.author, timestamp);
|
||||
|
||||
const imageParams = {
|
||||
cmd: this.constructor.command,
|
||||
params: {
|
||||
togif: !!this.options.togif
|
||||
},
|
||||
id: this.message.event_id
|
||||
};
|
||||
|
||||
// if (this.type === "application") await this.acknowledge();
|
||||
|
||||
if (this.constructor.requiresImage) {
|
||||
try {
|
||||
const selection = selectedImages.get(this.author);
|
||||
const image = await imageDetect(this.client, this.message, this.interaction, this.options, true);
|
||||
if (selection) selectedImages.delete(this.author);
|
||||
if (image === undefined) {
|
||||
runningCommands.delete(this.author);
|
||||
return `${this.constructor.noImage} (Tip: try right-clicking/holding on a message and press Apps -> Select Image, then try again.)`;
|
||||
} else if (image.type === "large") {
|
||||
runningCommands.delete(this.author);
|
||||
return "That image is too large (>= 25MB)! Try using a smaller image.";
|
||||
} else if (image.type === "tenorlimit") {
|
||||
runningCommands.delete(this.author);
|
||||
return "I've been rate-limited by Tenor. Please try uploading your GIF elsewhere.";
|
||||
}
|
||||
imageParams.path = image.path;
|
||||
imageParams.params.type = image.type;
|
||||
imageParams.url = image.url; // technically not required but can be useful for text filtering
|
||||
imageParams.name = image.name;
|
||||
if (this.constructor.requiresGIF) imageParams.onlyGIF = true;
|
||||
} catch (e) {
|
||||
runningCommands.delete(this.author);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.constructor.requiresText) {
|
||||
const text = this.options.text ?? this.args.join(" ").trim();
|
||||
if (text.length === 0 || !await this.criteria(text, imageParams.url)) {
|
||||
runningCommands.delete(this.author);
|
||||
return this.constructor.noText;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.params === "function") {
|
||||
Object.assign(imageParams.params, this.params(imageParams.url, imageParams.name));
|
||||
} else if (typeof this.params === "object") {
|
||||
Object.assign(imageParams.params, this.params);
|
||||
}
|
||||
|
||||
let status;
|
||||
if (imageParams.params.type === "image/gif" && this.type === "classic") {
|
||||
status = await this.processMessage(this.message.room_id ?? await this.client.rest.channels.get(this.message.room_id));
|
||||
}
|
||||
|
||||
try {
|
||||
const { buffer, type } = await runImageJob(imageParams);
|
||||
if (type === "nogif" && this.constructor.requiresGIF) {
|
||||
return "That isn't a GIF!";
|
||||
}
|
||||
this.success = true;
|
||||
return {
|
||||
contents: buffer,
|
||||
name: `${this.constructor.command}.${type}`
|
||||
};
|
||||
} catch (e) {
|
||||
if (e === "Request ended prematurely due to a closed connection") return "This image job couldn't be completed because the server it was running on went down. Try running your command again.";
|
||||
if (e === "Job timed out" || e === "Timeout") return "The image is taking too long to process (>=15 minutes), so the job was cancelled. Try using a smaller image.";
|
||||
if (e === "No available servers") return "I can't seem to contact the image servers, they might be down or still trying to start up. Please wait a little bit.";
|
||||
throw e;
|
||||
} finally {
|
||||
try {
|
||||
if (status) await status.delete();
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
runningCommands.delete(this.author);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
processMessage(channel) {
|
||||
this.client.send
|
||||
const content = {
|
||||
body: "Processing... This might take a while",
|
||||
msgtype: "m.text",
|
||||
};
|
||||
this.client.sendEvent(channel, "m.room.message", content, "", (err, res) => {
|
||||
// console.log(res)
|
||||
logger.log("error", err)
|
||||
return res
|
||||
});
|
||||
}
|
||||
|
||||
static init() {
|
||||
this.flags = [];
|
||||
if (this.requiresText || this.textOptional) {
|
||||
this.flags.push({
|
||||
name: "text",
|
||||
type: 3,
|
||||
description: "The text to put on the image",
|
||||
required: !this.textOptional
|
||||
});
|
||||
}
|
||||
if (this.requiresImage) {
|
||||
this.flags.push({
|
||||
name: "image",
|
||||
type: 11,
|
||||
description: "An image/GIF attachment"
|
||||
}, {
|
||||
name: "link",
|
||||
type: 3,
|
||||
description: "An image/GIF URL"
|
||||
});
|
||||
}
|
||||
this.flags.push({
|
||||
name: "togif",
|
||||
type: 5,
|
||||
description: "Force GIF output"
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
static allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto", "times", "comic sans ms"];
|
||||
|
||||
static requiresImage = true;
|
||||
static requiresText = false;
|
||||
static textOptional = false;
|
||||
static requiresGIF = false;
|
||||
static noImage = "You need to provide an image/GIF!";
|
||||
static noText = "You need to provide some text!";
|
||||
static command = "";
|
||||
}
|
||||
|
||||
export default ImageCommand;
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import Command from "./command.js";
|
||||
import { players, queues } from "../utils/soundplayer.js";
|
||||
|
||||
class MusicCommand extends Command {
|
||||
constructor(client, options) {
|
||||
super(client, options);
|
||||
if (this.guild) {
|
||||
this.connection = players.get(this.guild.id);
|
||||
this.queue = queues.get(this.guild.id);
|
||||
}
|
||||
}
|
||||
|
||||
static slashAllowed = false;
|
||||
static directAllowed = false;
|
||||
}
|
||||
|
||||
export default MusicCommand;
|
||||
import Command from "./command.js";
|
||||
import { players, queues } from "../utils/soundplayer.js";
|
||||
|
||||
class MusicCommand extends Command {
|
||||
constructor(client, options) {
|
||||
super(client, options);
|
||||
if (this.guild) {
|
||||
this.connection = players.get(this.guild.id);
|
||||
this.queue = queues.get(this.guild.id);
|
||||
}
|
||||
}
|
||||
|
||||
static slashAllowed = false;
|
||||
static directAllowed = false;
|
||||
}
|
||||
|
||||
export default MusicCommand;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import Command from "./command.js";
|
||||
import { play } from "../utils/soundplayer.js";
|
||||
|
||||
// only exists to sort the various soundboard commands
|
||||
class SoundboardCommand extends Command {
|
||||
async run() {
|
||||
return play(this.client, this.constructor.file, { channel: this.channel, author: this.author, member: this.member, type: this.type, interaction: this.interaction });
|
||||
}
|
||||
|
||||
static slashAllowed = false;
|
||||
static directAllowed = false;
|
||||
}
|
||||
|
||||
export default SoundboardCommand;
|
||||
import Command from "./command.js";
|
||||
import { play } from "../utils/soundplayer.js";
|
||||
|
||||
// only exists to sort the various soundboard commands
|
||||
class SoundboardCommand extends Command {
|
||||
async run() {
|
||||
return play(this.client, this.constructor.file, { channel: this.channel, author: this.author, member: this.member, type: this.type, interaction: this.interaction });
|
||||
}
|
||||
|
||||
static slashAllowed = false;
|
||||
static directAllowed = false;
|
||||
}
|
||||
|
||||
export default SoundboardCommand;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue