mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-22 01:29:22 +00:00
139 lines
4.1 KiB
TypeScript
139 lines
4.1 KiB
TypeScript
import logger from "../logger";
|
|
import fs from "fs";
|
|
import * as Skins from "../data/skins";
|
|
import { TWEET_BOT_CHANNEL_ID } from "../config";
|
|
import { Client } from "discord.js";
|
|
import _temp from "temp";
|
|
import sharp from "sharp";
|
|
import { getTwitterClient } from "../twitter";
|
|
import SkinModel from "../data/SkinModel";
|
|
import UserContext from "../data/UserContext";
|
|
import TweetModel from "../data/TweetModel";
|
|
const temp = _temp.track();
|
|
|
|
export async function tweet(
|
|
discordClient: Client,
|
|
anything: string | null
|
|
): Promise<string | undefined> {
|
|
const ctx = new UserContext();
|
|
const tweetBotChannel = await discordClient.channels.fetch(
|
|
TWEET_BOT_CHANNEL_ID
|
|
);
|
|
if (tweetBotChannel == null) {
|
|
throw new Error("Could not connect to the #tweet-bot channel");
|
|
}
|
|
let tweetableSkin: null | SkinModel = null;
|
|
if (anything != null) {
|
|
tweetableSkin = await SkinModel.fromAnything(ctx, anything);
|
|
if (tweetableSkin == null) {
|
|
// @ts-ignore
|
|
await tweetBotChannel.send(
|
|
`Oops! Could not find a skin matching ${anything}`
|
|
);
|
|
return;
|
|
}
|
|
const tweetStatus = await tweetableSkin.getTweetStatus();
|
|
if (tweetStatus == "TWEETED") {
|
|
// @ts-ignore
|
|
// await tweetBotChannel.send(`Oops! This skin has alraedy been tweeted.`);
|
|
// return;
|
|
}
|
|
if (tweetStatus == "REJECTED" || tweetStatus == "NSFW") {
|
|
// @ts-ignore
|
|
await tweetBotChannel.send(`Oops! Can't tweet a rejected skin.`);
|
|
return;
|
|
}
|
|
} else {
|
|
const toTweet = await Skins.getSkinToTweet();
|
|
tweetableSkin =
|
|
toTweet == null ? null : await SkinModel.fromMd5(ctx, toTweet.md5);
|
|
}
|
|
if (tweetableSkin == null) {
|
|
// @ts-ignore
|
|
await tweetBotChannel.send(
|
|
"Oops! I ran out of skins to tweet. Could someone please `!review` some more?"
|
|
);
|
|
logger.info("Could not find a skin to tweet");
|
|
return;
|
|
}
|
|
|
|
const filename = await tweetableSkin.getFileName();
|
|
const md5 = tweetableSkin.getMd5();
|
|
|
|
if (filename == null) {
|
|
throw new Error(`Could not find filename for skin with hash ${md5}`);
|
|
}
|
|
|
|
let tweetId;
|
|
try {
|
|
tweetId = await sendTweet(tweetableSkin);
|
|
} catch (e) {
|
|
console.error(e);
|
|
// @ts-ignore
|
|
await tweetBotChannel.send(`Oops. Tweeting crashed: ${e.message}`);
|
|
return;
|
|
}
|
|
await Skins.markAsTweeted(tweetableSkin.getMd5(), tweetId);
|
|
|
|
const tweet = await TweetModel.fromTweetId(ctx, tweetId);
|
|
if (tweet == null) {
|
|
throw new Error(`Could not locate tweet with ID "${tweetId}"`);
|
|
}
|
|
// @ts-ignore
|
|
await tweetBotChannel.send(tweet?.getUrl());
|
|
const remainingSkinCount = await Skins.getTweetableSkinCount();
|
|
if (remainingSkinCount < 10) {
|
|
// @ts-ignore
|
|
await tweetBotChannel.send(
|
|
`Only ${remainingSkinCount} approved skins left. Could someone please \`!review\` some more?`
|
|
);
|
|
}
|
|
logger.info("Tweeted a skin", { md5, filename, tweetId });
|
|
return md5;
|
|
}
|
|
|
|
async function getResizedScreenshot(md5: string): Promise<Buffer> {
|
|
const screenshot = await Skins.getScreenshotBuffer(md5);
|
|
const { width, height } = await sharp(screenshot).metadata();
|
|
|
|
const image = await sharp(screenshot)
|
|
.resize(width * 2, height * 2, {
|
|
kernel: sharp.kernel.nearest,
|
|
})
|
|
.toBuffer();
|
|
|
|
return image;
|
|
}
|
|
|
|
async function sendTweet(skin: SkinModel): Promise<string> {
|
|
const screenshotBuffer = await getResizedScreenshot(skin.getMd5());
|
|
const filename = await skin.getFileName();
|
|
const tempFile = temp.path({ suffix: ".png" });
|
|
fs.writeFileSync(tempFile, screenshotBuffer);
|
|
const t = getTwitterClient();
|
|
|
|
const promise: Promise<{ media_id_string: string }> = new Promise(
|
|
(resolve, reject) => {
|
|
t.postMediaChunked({ file_path: tempFile }, (err, data) => {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
resolve(data as { media_id_string: string });
|
|
});
|
|
}
|
|
);
|
|
const { media_id_string } = await promise;
|
|
|
|
const params = {
|
|
status: `${filename}\n\n${skin.getMuseumUrl()}`,
|
|
media_ids: [media_id_string],
|
|
};
|
|
|
|
const result = await t.post("statuses/update", params);
|
|
const id = result.data.id_str;
|
|
if (id == null) {
|
|
throw new Error(`Could not get id`);
|
|
}
|
|
return id;
|
|
}
|