2021-08-19 14:19:14 +00:00
|
|
|
import fetch from "node-fetch";
|
|
|
|
import { getType } from "./image.js";
|
|
|
|
import { exec } from "child_process";
|
|
|
|
import { promisify } from "util";
|
|
|
|
const execPromise = promisify(exec);
|
2019-09-13 20:02:41 +00:00
|
|
|
|
2020-11-20 21:16:52 +00:00
|
|
|
const tenorURLs = [
|
|
|
|
"tenor.com",
|
|
|
|
"www.tenor.com"
|
|
|
|
];
|
|
|
|
const giphyURLs = [
|
|
|
|
"giphy.com",
|
2022-03-06 05:06:20 +00:00
|
|
|
"www.giphy.com",
|
|
|
|
"i.giphy.com"
|
2020-11-20 21:16:52 +00:00
|
|
|
];
|
2021-04-21 22:37:05 +00:00
|
|
|
const giphyMediaURLs = [ // there could be more of these
|
2021-05-06 21:12:44 +00:00
|
|
|
"media.giphy.com",
|
2021-04-21 22:37:05 +00:00
|
|
|
"media0.giphy.com",
|
|
|
|
"media1.giphy.com",
|
|
|
|
"media2.giphy.com",
|
|
|
|
"media3.giphy.com",
|
|
|
|
"media4.giphy.com"
|
|
|
|
];
|
2020-11-20 21:16:52 +00:00
|
|
|
const imgurURLs = [
|
|
|
|
"imgur.com",
|
|
|
|
"www.imgur.com",
|
|
|
|
"i.imgur.com"
|
|
|
|
];
|
2021-01-18 02:40:52 +00:00
|
|
|
const gfycatURLs = [
|
|
|
|
"gfycat.com",
|
|
|
|
"www.gfycat.com",
|
|
|
|
"thumbs.gfycat.com",
|
|
|
|
"giant.gfycat.com"
|
|
|
|
];
|
2020-11-20 21:16:52 +00:00
|
|
|
|
2021-06-29 19:10:22 +00:00
|
|
|
const imageFormats = ["image/jpeg", "image/png", "image/webp", "image/gif", "large"];
|
2021-04-19 14:31:39 +00:00
|
|
|
const videoFormats = ["video/mp4", "video/webm", "video/mov"];
|
2021-01-05 02:53:34 +00:00
|
|
|
|
2020-10-18 21:53:35 +00:00
|
|
|
// gets the proper image paths
|
2021-06-28 22:59:05 +00:00
|
|
|
const getImage = async (image, image2, video, extraReturnTypes, gifv = false) => {
|
2019-09-13 20:02:41 +00:00
|
|
|
try {
|
2020-10-19 23:48:43 +00:00
|
|
|
const payload = {
|
|
|
|
url: image2,
|
|
|
|
path: image
|
|
|
|
};
|
2020-10-18 21:53:35 +00:00
|
|
|
if (gifv) {
|
2021-04-19 14:31:39 +00:00
|
|
|
const host = new URL(image2).host;
|
2020-11-20 21:16:52 +00:00
|
|
|
if (tenorURLs.includes(host)) {
|
2021-04-19 16:04:24 +00:00
|
|
|
// Tenor doesn't let us access a raw GIF without going through their API,
|
|
|
|
// so we use that if there's a key in the config and fall back to using the MP4 if there isn't
|
|
|
|
// Note that MP4 conversion requires an ImageMagick build that supports MPEG decoding
|
2020-11-05 21:40:18 +00:00
|
|
|
if (process.env.TENOR !== "") {
|
2021-05-14 14:31:12 +00:00
|
|
|
const data = await fetch(`https://g.tenor.com/v1/gifs?ids=${image2.split("-").pop()}&media_filter=minimal&limit=1&key=${process.env.TENOR}`);
|
2021-06-29 19:10:22 +00:00
|
|
|
if (data.status === 429) {
|
|
|
|
if (extraReturnTypes) {
|
|
|
|
payload.type = "tenorlimit";
|
|
|
|
return payload;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2020-11-05 21:40:18 +00:00
|
|
|
const json = await data.json();
|
2022-01-15 05:26:38 +00:00
|
|
|
if (json.error) throw Error(json.error);
|
2020-11-05 21:40:18 +00:00
|
|
|
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];
|
|
|
|
}
|
2020-11-20 21:16:52 +00:00
|
|
|
} else if (giphyURLs.includes(host)) {
|
2021-04-19 16:04:24 +00:00
|
|
|
// Can result in an HTML page instead of a GIF
|
2022-01-26 21:24:10 +00:00
|
|
|
payload.path = `https://media0.giphy.com/media/${image2.split("/")[4].split("-").pop()}/giphy.gif`;
|
2021-04-21 22:37:05 +00:00
|
|
|
} else if (giphyMediaURLs.includes(host)) {
|
|
|
|
payload.path = `https://media0.giphy.com/media/${image2.split("/")[4]}/giphy.gif`;
|
2020-11-20 21:16:52 +00:00
|
|
|
} else if (imgurURLs.includes(host)) {
|
2021-09-13 18:05:22 +00:00
|
|
|
// Seems that Imgur has a possibility of making GIFs static
|
2020-10-19 23:48:43 +00:00
|
|
|
payload.path = image.replace(".mp4", ".gif");
|
2021-01-18 02:40:52 +00:00
|
|
|
} else if (gfycatURLs.includes(host)) {
|
2021-04-19 16:04:24 +00:00
|
|
|
// iirc Gfycat also seems to sometimes make GIFs static
|
2021-01-18 02:40:52 +00:00
|
|
|
payload.path = `https://thumbs.gfycat.com/${image.split("/").pop().split(".mp4")[0]}-size_restricted.gif`;
|
2020-10-18 21:53:35 +00:00
|
|
|
}
|
2020-10-20 01:24:53 +00:00
|
|
|
payload.type = "image/gif";
|
2021-04-19 14:31:39 +00:00
|
|
|
} else if (video) {
|
2021-06-28 22:59:05 +00:00
|
|
|
payload.type = await getType(payload.path, extraReturnTypes);
|
2021-04-20 01:15:32 +00:00
|
|
|
if (!payload.type || (!videoFormats.includes(payload.type) && !imageFormats.includes(payload.type))) return;
|
2021-01-05 02:53:34 +00:00
|
|
|
} else {
|
2021-06-28 22:59:05 +00:00
|
|
|
payload.type = await getType(payload.path, extraReturnTypes);
|
2021-04-19 14:31:39 +00:00
|
|
|
if (!payload.type || !imageFormats.includes(payload.type)) return;
|
2019-09-13 20:02:41 +00:00
|
|
|
}
|
2020-10-18 21:53:35 +00:00
|
|
|
return payload;
|
2019-09-13 20:02:41 +00:00
|
|
|
} catch (error) {
|
2020-07-17 00:53:44 +00:00
|
|
|
if (error.name === "AbortError") {
|
|
|
|
throw Error("Timed out");
|
|
|
|
} else {
|
|
|
|
throw error;
|
|
|
|
}
|
2019-09-13 20:02:41 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-10-05 21:48:26 +00:00
|
|
|
const checkImages = async (message, extraReturnTypes, video, sticker) => {
|
2020-08-16 16:48:37 +00:00
|
|
|
let type;
|
2021-10-05 21:48:26 +00:00
|
|
|
if (sticker && message.stickerItems) {
|
|
|
|
type = message.stickerItems[0];
|
|
|
|
} else {
|
|
|
|
// first check the embeds
|
|
|
|
if (message.embeds.length !== 0) {
|
|
|
|
// embeds can vary in types, we check for tenor gifs first
|
|
|
|
if (message.embeds[0].type === "gifv") {
|
|
|
|
type = await getImage(message.embeds[0].video.url, message.embeds[0].url, video, extraReturnTypes, true);
|
|
|
|
// then we check for other image types
|
|
|
|
} else if ((message.embeds[0].type === "video" || message.embeds[0].type === "image") && message.embeds[0].thumbnail) {
|
2021-06-28 22:59:05 +00:00
|
|
|
type = await getImage(message.embeds[0].thumbnail.proxy_url, message.embeds[0].thumbnail.url, video, extraReturnTypes);
|
2021-10-05 21:48:26 +00:00
|
|
|
// finally we check both possible image fields for "generic" embeds
|
2022-01-15 16:47:41 +00:00
|
|
|
} else if (message.embeds[0].type === "rich" || message.embeds[0].type === "article") {
|
2021-10-05 21:48:26 +00:00
|
|
|
if (message.embeds[0].thumbnail) {
|
|
|
|
type = await getImage(message.embeds[0].thumbnail.proxy_url, message.embeds[0].thumbnail.url, video, extraReturnTypes);
|
|
|
|
} else if (message.embeds[0].image) {
|
|
|
|
type = await getImage(message.embeds[0].image.proxy_url, message.embeds[0].image.url, video, extraReturnTypes);
|
|
|
|
}
|
2020-10-18 21:53:35 +00:00
|
|
|
}
|
2021-10-05 21:48:26 +00:00
|
|
|
// then check the attachments
|
|
|
|
} else if (message.attachments.length !== 0 && message.attachments[0].width) {
|
|
|
|
type = await getImage(message.attachments[0].proxy_url, message.attachments[0].url, video);
|
2020-08-16 16:48:37 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-14 14:31:12 +00:00
|
|
|
// if the return value exists then return it
|
2022-01-15 05:26:38 +00:00
|
|
|
return type ?? false;
|
2020-08-16 16:48:37 +00:00
|
|
|
};
|
|
|
|
|
2019-09-13 20:02:41 +00:00
|
|
|
// this checks for the latest message containing an image and returns the url of the image
|
2021-10-05 21:48:26 +00:00
|
|
|
export default async (client, cmdMessage, extraReturnTypes = false, video = false, sticker = false) => {
|
2021-04-20 03:06:40 +00:00
|
|
|
// we start by checking if the message is a reply to another message
|
2020-11-26 15:31:24 +00:00
|
|
|
if (cmdMessage.messageReference) {
|
2021-04-30 17:31:53 +00:00
|
|
|
const replyMessage = await client.getMessage(cmdMessage.messageReference.channelID, cmdMessage.messageReference.messageID).catch(() => undefined);
|
2020-12-26 18:17:10 +00:00
|
|
|
if (replyMessage) {
|
2021-10-05 21:48:26 +00:00
|
|
|
const replyResult = await checkImages(replyMessage, extraReturnTypes, video, sticker);
|
2020-12-26 18:17:10 +00:00
|
|
|
if (replyResult !== false) return replyResult;
|
2020-11-26 15:31:24 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-20 03:06:40 +00:00
|
|
|
// then we check the current message
|
2021-10-05 21:48:26 +00:00
|
|
|
const result = await checkImages(cmdMessage, extraReturnTypes, video, sticker);
|
2021-04-20 03:06:40 +00:00
|
|
|
if (result !== false) return result;
|
2020-11-26 15:31:24 +00:00
|
|
|
// if there aren't any replies then iterate over the last few messages in the channel
|
2021-04-30 17:31:53 +00:00
|
|
|
const messages = await client.getMessages(cmdMessage.channel.id);
|
2019-09-13 20:02:41 +00:00
|
|
|
// iterate over each message
|
|
|
|
for (const message of messages) {
|
2021-10-05 21:48:26 +00:00
|
|
|
const result = await checkImages(message, extraReturnTypes, video, sticker);
|
2020-08-16 16:48:37 +00:00
|
|
|
if (result === false) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
return result;
|
2019-09-13 20:02:41 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-26 17:29:13 +00:00
|
|
|
};
|