Type !archive

This commit is contained in:
Jordan Eldredge 2020-04-25 00:50:13 -04:00
parent 89366e84ac
commit f4ba178d40
6 changed files with 139 additions and 104 deletions

View file

@ -0,0 +1,46 @@
import * as Skins from "./data/skins";
import fs from "fs";
import md5Buffer from "md5";
import * as S3 from "./s3";
import Shooter from "./shooter";
import _temp from "temp";
import * as Analyser from "./analyser";
const temp = _temp.track();
// TODO
// Extract the readme
// Extract the emails
// Upload to Internet Archive
// Store the Internet Archive item name
// Construct IA Webamp UR
type Result =
| { md5: string; status: "FOUND" }
| { md5: string; status: "ADDED"; averageColor: string };
export async function addSkinFromBuffer(
buffer: Buffer,
filePath: string,
uploader: string
): Promise<Result> {
const md5 = md5Buffer(buffer);
const skin = await Skins.getSkinByMd5(md5);
if (skin != null) {
return { md5, status: "FOUND" };
}
const tempFile = temp.path({ suffix: ".wsz" });
fs.writeFileSync(tempFile, buffer);
const tempScreenshotPath = temp.path({ suffix: ".png" });
await Shooter.withShooter((shooter) =>
shooter.takeScreenshot(tempFile, tempScreenshotPath, {
minify: true,
})
);
const averageColor = await Analyser.getColor(tempScreenshotPath);
await S3.putScreenshot(md5, fs.readFileSync(tempScreenshotPath));
await S3.putSkin(md5, buffer);
await Skins.addSkin({ md5, filePath, uploader, averageColor });
return { md5, status: "ADDED", averageColor };
}

View file

@ -0,0 +1,18 @@
// Functions for deriving information from skins
import { exec } from "child_process";
import shellescape from "shell-escape";
export function getColor(imgPath: string): Promise<string> {
return new Promise((resolve, reject) => {
const excapedImgPath = shellescape([imgPath]);
const command = `convert ${excapedImgPath} -scale 1x1\! -format '%[pixel:u]' info:-`;
exec(command, (error, stdout) => {
if (error !== null) {
reject(error);
return;
}
resolve(stdout.slice(1));
});
});
}

View file

@ -1,98 +0,0 @@
const fs = require("fs");
const fetch = require("node-fetch");
const md5Buffer = require("md5");
const Skins = require("../../data/skins");
const Utils = require("../utils");
const S3 = require("../../s3");
const Shooter = require("../../shooter");
const temp = require("temp").track();
async function handler(message) {
const { attachments } = message;
if (attachments.length < 1) {
await message.channel.send("Could not archive. No attachment found.");
return;
}
const files = await Promise.all(
attachments.map(async (attachment) => {
const { filename, url } = attachment;
const response = await fetch(url);
console.log("got response");
const buffer = await response.buffer();
console.log("got buffer");
const md5 = md5Buffer(buffer);
console.log("got md5", md5);
return { filename, buffer, md5 };
})
);
// TODO
// Extract the readme
// Extract the emails
// Extract the average color
// Upload to Internet Archive
// Store the Internet Archive item name
// Construct IA Webamp URL
const shooter = new Shooter("https://webamp.org");
try {
for (const file of files) {
const skin = await Skins.getSkinByMd5(file.md5);
if (skin != null) {
await message.channel.send(`This skin is already in our collection.`);
await Utils.postSkin({
md5: file.md5,
dest: message.channel,
});
} else {
await message.channel.send(
`Thanks! ${file.filename} is a brand new skin. 👏 Uploading to our server and taking a screenshot...`
);
const tempFile = temp.path({ suffix: ".wsz" });
fs.writeFileSync(tempFile, file.buffer);
const tempScreenshotPath = temp.path({ suffix: ".png" });
try {
await shooter.takeScreenshot(tempFile, tempScreenshotPath, {
minify: true,
});
} catch (e) {
console.error(e);
await message.channel.send(
`Something went wrong taking the screenshot of ${file.filename}. Sorry.`
);
continue;
}
await S3.putScreenshot(file.md5, fs.readFileSync(tempScreenshotPath));
await S3.putSkin(file.md5, file.buffer);
await Skins.addSkin({
md5: file.md5,
filePath: file.filename,
uploader: message.author.username,
});
await message.channel.send(
`Done! ${file.filename} has been queued for archiving. In the mean time, here's a screenshot and a link to it on Webamp.`
);
await Utils.postSkin({
md5: file.md5,
dest: message.channel,
});
}
}
} catch (e) {
console.error(e);
message.channel.send(
"There was an error archiving your skin. Please ping @captbaritone."
);
}
shooter.dispose();
}
module.exports = {
command: "archive",
usage: "",
description:
"Queues the accompanying uploaded skin for inclusion in the Archive",
handler,
};

View file

@ -0,0 +1,55 @@
import { Message } from "discord.js";
import fetch from "node-fetch";
import * as Utils from "../utils";
import { addSkinFromBuffer } from "../../addSkin";
async function handler(message: Message) {
const { attachments } = message;
if (attachments.array().length < 1) {
await message.channel.send("Could not archive. No attachment found.");
return;
}
const files = await Promise.all(
attachments.map(async (attachment) => {
const { filename, url } = attachment;
const response = await fetch(url);
console.log("got response");
const buffer = await response.buffer();
console.log("got buffer");
return { filename, buffer };
})
);
for (const file of files) {
let result;
try {
result = await addSkinFromBuffer(
file.buffer,
file.filename,
message.author.username
);
} catch (e) {
console.error(e);
message.channel.send(
"There was an error archiving your skin. Please ping @captbaritone."
);
throw e;
}
if (result.status === "FOUND") {
await message.channel.send(`This skin is already in our collection.`);
} else if (result.status === "ADDED") {
await message.channel.send(
`Thanks! ${file.filename} is a brand new skin. 👏 It has been queued for archiving. In the mean time, here's a screenshot and a link to it on Webamp.`
);
}
await Utils.postSkin({ md5: result.md5, dest: message.channel });
}
}
module.exports = {
command: "archive",
usage: "",
description:
"Queues the accompanying uploaded skin for inclusion in the Archive",
handler,
};

View file

@ -1,4 +1,11 @@
import { RichEmbed, User, MessageReaction, Channel } from "discord.js";
import {
RichEmbed,
User,
MessageReaction,
TextChannel,
DMChannel,
GroupDMChannel,
} from "discord.js";
import rgbHex from "rgb-hex";
import * as Skins from "../data/skins";
import logger from "../logger";
@ -17,14 +24,14 @@ const filter = (reaction: MessageReaction): boolean => {
);
};
async function postSkin({
export async function postSkin({
md5,
title: _title,
dest,
}: {
md5: string;
title: (filename: string | null) => string;
dest: Channel;
title?: (filename: string | null) => string;
dest: TextChannel | DMChannel | GroupDMChannel;
}) {
const skin = await Skins.getSkinByMd5(md5);
if (skin == null) {
@ -141,5 +148,3 @@ function getPrettyTwitterStatus(status: TweetStatus): string {
return "Tweeted 🐦";
}
}
module.exports = { postSkin };

View file

@ -15,6 +15,15 @@ class Shooter {
this._url = url;
}
static async withShooter(cb) {
const shooter = new Shooter("https://webamp.org");
try {
await cb(shooter);
} finally {
shooter.dispose();
}
}
async init() {
this._browser = await puppeteer.launch();
this._page = await this._browser.newPage();