mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-24 18:47:44 +00:00
Stuff :P
This commit is contained in:
parent
a0ec8a4f82
commit
a8b33f23c8
8 changed files with 148 additions and 20 deletions
16
experiments/skin-database/tasks/migrate.ts
Normal file
16
experiments/skin-database/tasks/migrate.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const db = require("../db");
|
||||
const iaItems = db.get("internetArchiveItems");
|
||||
const skins = db.get("skins");
|
||||
|
||||
async function main() {
|
||||
const item = await iaItems.findOne({
|
||||
migrated: { $exists: false },
|
||||
md5: { $exists: true },
|
||||
});
|
||||
|
||||
skins.update({ md5: item.md5 });
|
||||
|
||||
console.log(item);
|
||||
}
|
||||
|
||||
export default main;
|
||||
|
|
@ -4,6 +4,7 @@ import fs from "fs";
|
|||
import { argv } from "yargs";
|
||||
import fetchInternetArchiveMetadata from "./tasks/fetchInternetArchiveMetadata";
|
||||
import ensureInternetArchiveItemsIndexByMd5 from "./tasks/ensureInternetArchiveItemsIndexByMd5";
|
||||
import migrate from "./tasks/migrate";
|
||||
import logger from "./logger";
|
||||
import DiscordWinstonTransport from "./DiscordWinstonTransport";
|
||||
import * as Skins from "./data/skins";
|
||||
|
|
@ -36,7 +37,7 @@ async function main() {
|
|||
break;
|
||||
|
||||
case "tweet":
|
||||
await tweet(client);
|
||||
await tweet(client, null);
|
||||
break;
|
||||
case "fetch-metadata":
|
||||
console.log("Going to download metadata from the Internet Archive");
|
||||
|
|
@ -52,7 +53,8 @@ async function main() {
|
|||
break;
|
||||
}
|
||||
case "reconcile": {
|
||||
await Skins.reconcile();
|
||||
console.log("Reconcile");
|
||||
console.log(await Skins.getSkinToArchive());
|
||||
break;
|
||||
}
|
||||
case "skin": {
|
||||
|
|
@ -67,6 +69,10 @@ async function main() {
|
|||
console.log(await addSkinFromBuffer(buffer, filePath, "cli-user"));
|
||||
break;
|
||||
}
|
||||
case "migrate": {
|
||||
await migrate();
|
||||
}
|
||||
|
||||
default:
|
||||
console.log(`Unknown command ${argv._[0]}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ function getSkinRecord(skin: DBSkinRecord): SkinRecord {
|
|||
filePaths,
|
||||
imageHash,
|
||||
uploader,
|
||||
tweeted,
|
||||
rejected,
|
||||
approved,
|
||||
nsfw,
|
||||
} = skin;
|
||||
const fileNames = filePaths.map((p) => path.basename(p));
|
||||
const skinUrl = `https://s3.amazonaws.com/webamp-uploaded-skins/skins/${md5}.wsz`;
|
||||
|
|
@ -52,6 +56,10 @@ function getSkinRecord(skin: DBSkinRecord): SkinRecord {
|
|||
readmeText,
|
||||
imageHash,
|
||||
uploader,
|
||||
tweeted,
|
||||
rejected,
|
||||
approved,
|
||||
nsfw,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -163,6 +171,10 @@ export async function getMd5sMatchingImageHash(imageHash: string) {
|
|||
return skins.find({ imageHash }, { md5: 1 });
|
||||
}
|
||||
|
||||
export async function getUnarchived() {
|
||||
return skins.find({ itemName: null }, { md5: 1 });
|
||||
}
|
||||
|
||||
export function getInternetArchiveUrl(itemName: string | null): string | null {
|
||||
return itemName == null ? null : `https://archive.org/details/${itemName}`;
|
||||
}
|
||||
|
|
@ -180,6 +192,10 @@ export async function markAsTweeted(md5: string): Promise<void> {
|
|||
return S3.markAsTweeted(md5);
|
||||
}
|
||||
|
||||
export async function markAsNSFW(md5: string): Promise<void> {
|
||||
await skins.findOneAndUpdate({ md5 }, { $set: { nsfw: true } });
|
||||
}
|
||||
|
||||
export async function getStatus(md5: string): Promise<TweetStatus> {
|
||||
const skin = await skins.findOne({ md5 });
|
||||
if (skin.tweeted) {
|
||||
|
|
@ -204,6 +220,27 @@ export async function reject(md5: string): Promise<void> {
|
|||
return S3.reject(md5);
|
||||
}
|
||||
|
||||
export async function getSkinToArchive(): Promise<{
|
||||
filename: string | null;
|
||||
md5: string;
|
||||
}> {
|
||||
const reviewable = await skins.aggregate([
|
||||
{ $match: CLASSIC_QUERY },
|
||||
{
|
||||
$lookup: {
|
||||
from: "internetArchiveItems",
|
||||
localField: "md5",
|
||||
foreignField: "md5",
|
||||
as: "archive",
|
||||
},
|
||||
},
|
||||
{ $count: "5" },
|
||||
]);
|
||||
const skin = reviewable[0];
|
||||
const { canonicalFilename, md5 } = getSkinRecord(skin);
|
||||
return { filename: canonicalFilename, md5 };
|
||||
}
|
||||
|
||||
export async function getSkinToReview(): Promise<{
|
||||
filename: string | null;
|
||||
md5: string;
|
||||
|
|
@ -217,10 +254,7 @@ export async function getSkinToReview(): Promise<{
|
|||
return { filename: canonicalFilename, md5 };
|
||||
}
|
||||
|
||||
export async function getSkinToTweet(): Promise<{
|
||||
filename: string | null;
|
||||
md5: string;
|
||||
} | null> {
|
||||
export async function getSkinToTweet(): Promise<SkinRecord | null> {
|
||||
const tweetables = await skins.aggregate([
|
||||
{ $match: TWEETABLE_QUERY },
|
||||
{ $sample: { size: 1 } },
|
||||
|
|
@ -229,8 +263,7 @@ export async function getSkinToTweet(): Promise<{
|
|||
if (skin == null) {
|
||||
return null;
|
||||
}
|
||||
const { canonicalFilename, md5 } = getSkinRecord(skin);
|
||||
return { filename: canonicalFilename, md5 };
|
||||
return getSkinRecord(skin);
|
||||
}
|
||||
|
||||
export async function getStats(): Promise<{
|
||||
|
|
|
|||
|
|
@ -3,19 +3,20 @@ import { Message } from "discord.js";
|
|||
import { tweet } from "../../tasks/tweet";
|
||||
import { CAPTBARITONE_USER_ID } from "../../config";
|
||||
|
||||
async function handler(message: Message): Promise<void> {
|
||||
async function handler(message: Message, args: [string]) {
|
||||
const [anything] = args;
|
||||
if (message.author.id !== CAPTBARITONE_USER_ID) {
|
||||
await message.channel.send(
|
||||
`Currently only @captbaritone can use this command`
|
||||
);
|
||||
return;
|
||||
}
|
||||
await tweet(message.client);
|
||||
await tweet(message.client, anything || null);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
command: "tweet",
|
||||
handler,
|
||||
usage: "",
|
||||
usage: "[<md5>]",
|
||||
description: "Tweet an approved skin",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const filter = (reaction: MessageReaction): boolean => {
|
|||
|
||||
return (
|
||||
hasNonBot &&
|
||||
["👍", "👎", "👏", "😔"].some((name) => reaction.emoji.name === name)
|
||||
["👍", "👎", "👏", "😔", "🔞"].some((name) => reaction.emoji.name === name)
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -52,6 +52,7 @@ export async function postSkin({
|
|||
internetArchiveUrl,
|
||||
internetArchiveItemName,
|
||||
readmeText,
|
||||
nsfw,
|
||||
} = skin;
|
||||
const title = _title ? _title(canonicalFilename) : canonicalFilename;
|
||||
|
||||
|
|
@ -59,8 +60,18 @@ export async function postSkin({
|
|||
.setTitle(title)
|
||||
.addField("Try Online", `[webamp.org](${webampUrl})`, true)
|
||||
.addField("Download", `[${canonicalFilename}](${skinUrl})`, true)
|
||||
.addField("Md5", md5, true)
|
||||
.setImage(screenshotUrl);
|
||||
.addField("Md5", md5, true);
|
||||
|
||||
if (nsfw) {
|
||||
embed.addField("NSFW", `🔞`, true);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
if (nsfw && !(dest.type === "text" && dest.nsfw)) {
|
||||
embed.addField("Screenshot", `[Screenshot](${screenshotUrl})`, true);
|
||||
} else {
|
||||
embed.setImage(screenshotUrl);
|
||||
}
|
||||
|
||||
if (readmeText) {
|
||||
// Trim the readme since Discord will reject it otherwise.
|
||||
|
|
@ -93,7 +104,9 @@ export async function postSkin({
|
|||
embed.addField("Tweet Status", `[Tweeted](${tweetUrl}) ${likes}🐦`, true);
|
||||
} else {
|
||||
if (tweetStatus === "UNREVIEWED") {
|
||||
embed.setFooter("React with 👍 or 👎 to approve or deny");
|
||||
embed.setFooter(
|
||||
"React with 👍 or 👎 to approve or deny or 🔞 to mark NSFW"
|
||||
);
|
||||
}
|
||||
embed.addField("Tweet Status", getPrettyTwitterStatus(tweetStatus), true);
|
||||
}
|
||||
|
|
@ -110,8 +123,9 @@ export async function postSkin({
|
|||
if (tweetStatus === "TWEETED") {
|
||||
return;
|
||||
}
|
||||
await Promise.all([msg.react("👍"), msg.react("👎")]);
|
||||
|
||||
// Don't await
|
||||
Promise.all([msg.react("👍"), msg.react("👎"), msg.react("🔞")]);
|
||||
// TODO: Timeout at some point
|
||||
await msg.awaitReactions(filter, { max: 1 }).then(async (collected) => {
|
||||
const vote = collected.first();
|
||||
|
|
@ -135,6 +149,23 @@ export async function postSkin({
|
|||
);
|
||||
msg.react("❌");
|
||||
break;
|
||||
case "🔞":
|
||||
logger.info(`${user.username} marked ${md5} as NSFW`);
|
||||
await Skins.markAsNSFW(md5);
|
||||
await msg.channel.send(
|
||||
`${canonicalFilename} was marked as NSFW by ${user.username}`
|
||||
);
|
||||
await Skins.reject(md5);
|
||||
logger.info(`${user.username} rejected ${md5}`);
|
||||
await msg.channel.send(
|
||||
`${canonicalFilename} was rejected by ${user.username}`
|
||||
);
|
||||
msg.react("❌");
|
||||
break;
|
||||
default:
|
||||
logger.alert(
|
||||
`Unknown skin reaction by ${user.username} on ${md5}: ${vote.emoji.name}`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@
|
|||
"start": "tsc && node dist/index.js",
|
||||
"tweet": "tsc && ./dist/cli.js tweet",
|
||||
"fetch-metadata": "tsc && .dist/cli.js fetch-metadata",
|
||||
"bot": "tsc && node ./dist/discord-bot/index.js"
|
||||
"bot": "tsc && node ./dist/discord-bot/index.js",
|
||||
"cli": "tsc && node ./dist/cli.js"
|
||||
},
|
||||
"prettier": {},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import * as Skins from "../data/skins";
|
|||
import { TWEET_BOT_CHANNEL_ID } from "../config";
|
||||
import { spawn } from "child_process";
|
||||
import { Client } from "discord.js";
|
||||
import { SkinRecord } from "../types";
|
||||
|
||||
function spawnPromise(command: string, args: string[]): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
@ -31,12 +32,43 @@ function spawnPromise(command: string, args: string[]): Promise<string> {
|
|||
});
|
||||
}
|
||||
|
||||
export async function tweet(discordClient: Client) {
|
||||
export async function tweet(discordClient: Client, anything: string | null) {
|
||||
const tweetBotChannel = discordClient.channels.get(TWEET_BOT_CHANNEL_ID);
|
||||
if (tweetBotChannel == null) {
|
||||
throw new Error("Could not connect to the #tweet-bot channel");
|
||||
}
|
||||
const tweetableSkin = await Skins.getSkinToTweet();
|
||||
let tweetableSkin: null | SkinRecord = null;
|
||||
if (anything != null) {
|
||||
const _md5 = await Skins.getMd5ByAnything(anything);
|
||||
if (_md5 == null) {
|
||||
// @ts-ignore
|
||||
await tweetBotChannel.send(
|
||||
`Oops! Could not find a skin matching ${anything}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
tweetableSkin = await Skins.getSkinByMd5(_md5);
|
||||
if (tweetableSkin == null) {
|
||||
// @ts-ignore
|
||||
await tweetBotChannel.send(
|
||||
`Oops! Could not find a skin matching the md5 hash ${_md5}`
|
||||
);
|
||||
logger.info(`Could not find a skin matching hash ${_md5}`);
|
||||
return;
|
||||
}
|
||||
if (tweetableSkin.tweeted) {
|
||||
// @ts-ignore
|
||||
await tweetBotChannel.send(`Oops! This skin has alraedy been tweeted.`);
|
||||
return;
|
||||
}
|
||||
if (tweetableSkin.rejected) {
|
||||
// @ts-ignore
|
||||
await tweetBotChannel.send(`Oops! Can't tweet a rejected skin.`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
tweetableSkin = await Skins.getSkinToTweet();
|
||||
}
|
||||
if (tweetableSkin == null) {
|
||||
// @ts-ignore
|
||||
await tweetBotChannel.send(
|
||||
|
|
@ -46,7 +78,7 @@ export async function tweet(discordClient: Client) {
|
|||
return;
|
||||
}
|
||||
|
||||
const { md5, filename } = tweetableSkin;
|
||||
const { md5, canonicalFilename: filename } = tweetableSkin;
|
||||
if (filename == null) {
|
||||
throw new Error(`Could not find filename for skin with hash ${md5}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ export type DBSkinRecord = {
|
|||
filePaths: string[];
|
||||
imageHash?: string;
|
||||
uploader?: string;
|
||||
tweeted?: boolean;
|
||||
rejected?: boolean;
|
||||
approved?: boolean;
|
||||
nsfw?: boolean;
|
||||
};
|
||||
|
||||
export type DBIARecord = {
|
||||
|
|
@ -30,4 +34,8 @@ export type SkinRecord = {
|
|||
skinUrl: string;
|
||||
canonicalFilename: string | null;
|
||||
webampUrl: string;
|
||||
tweeted?: boolean;
|
||||
rejected?: boolean;
|
||||
approved?: boolean;
|
||||
nsfw?: boolean;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue