Post: add support for !posting files

This commit is contained in:
Dragory 2019-01-15 03:04:47 +02:00
parent ee3a64cafa
commit d3c3b65db6
4 changed files with 93 additions and 8 deletions

19
package-lock.json generated
View file

@ -39,6 +39,12 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.0.tgz",
"integrity": "sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ=="
},
"@types/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=",
"dev": true
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@ -2932,6 +2938,11 @@
}
}
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@ -3726,6 +3737,14 @@
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.203.1.tgz",
"integrity": "sha512-7MUlYyGJ6rSitEZ3r1Q1QNV8uSIzapS8SmmhSusBuIc7uIxPPwsKllEP0GRp1NS6Ik6F+fRZvnjDWm3ecv2hDw=="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"requires": {
"os-tmpdir": "~1.0.2"
}
},
"to-object-path": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",

View file

@ -43,6 +43,7 @@
"mysql": "^2.16.0",
"reflect-metadata": "^0.1.12",
"tlds": "^1.203.1",
"tmp": "0.0.33",
"ts-node": "^3.3.0",
"typeorm": "^0.2.8",
"typescript": "^3.2.2",
@ -50,6 +51,7 @@
},
"devDependencies": {
"@types/node": "^10.12.0",
"@types/tmp": "0.0.33",
"husky": "^0.14.3",
"lint-staged": "^7.2.0",
"nodemon": "^1.17.5",

View file

@ -1,11 +1,13 @@
import { Plugin, decorators as d } from "knub";
import { Channel, Message, TextChannel } from "eris";
import { errorMessage } from "../utils";
import { errorMessage, downloadFile } from "../utils";
import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { ISavedMessageData } from "../data/entities/SavedMessage";
import fs from "fs";
const fsp = fs.promises;
export class PostPlugin extends Plugin {
public static pluginName = 'post';
public static pluginName = "post";
protected savedMessages: GuildSavedMessages;
@ -33,18 +35,39 @@ export class PostPlugin extends Plugin {
}
/**
* Post a message as the bot to the specified channel
* COMMAND: Post a message as the bot to the specified channel
*/
@d.command("post", "<channel:channel> <content:string$>")
@d.command("post", "<channel:channel> [content:string$]")
@d.permission("post")
async postCmd(msg: Message, args: { channel: Channel; content: string }) {
async postCmd(msg: Message, args: { channel: Channel; content?: string }) {
if (!(args.channel instanceof TextChannel)) {
msg.channel.createMessage(errorMessage("Channel is not a text channel"));
return;
}
const createdMsg = await args.channel.createMessage(args.content);
const content = args.content || undefined;
let downloadedAttachment;
let file;
if (msg.attachments.length) {
downloadedAttachment = await downloadFile(msg.attachments[0].url);
file = {
name: msg.attachments[0].filename,
file: await fsp.readFile(downloadedAttachment.path)
};
}
if (content == null && file == null) {
msg.channel.createMessage(errorMessage("Text content or attachment required"));
return;
}
const createdMsg = await args.channel.createMessage(content, file);
await this.savedMessages.setPermanent(createdMsg.id);
if (downloadedAttachment) {
downloadedAttachment.deleteFn();
}
}
/**

View file

@ -1,9 +1,15 @@
import at = require("lodash.at");
import at from "lodash.at";
import { Emoji, Guild, GuildAuditLogEntry, TextableChannel } from "eris";
import url from "url";
import tlds from "tlds";
import emojiRegex from "emoji-regex";
import fs from "fs";
const fsp = fs.promises;
import https from "https";
import tmp from "tmp";
/**
* Turns a "delay string" such as "1h30m" to milliseconds
* @param {String} str
@ -287,6 +293,41 @@ export async function createChunkedMessage(channel: TextableChannel, messageText
}
}
/**
* Downloads the file from the given URL to a temporary file, with retry support
*/
export function downloadFile(attachmentUrl: string, retries = 3): Promise<{ path: string; deleteFn: () => void }> {
return new Promise(resolve => {
tmp.file((err, path, fd, deleteFn) => {
if (err) throw err;
const writeStream = fs.createWriteStream(path);
https
.get(attachmentUrl, res => {
res.pipe(writeStream);
writeStream.on("finish", () => {
writeStream.end();
resolve({
path,
deleteFn
});
});
})
.on("error", httpsErr => {
fsp.unlink(path);
if (retries === 0) {
throw httpsErr;
} else {
console.warn("File download failed, retrying. Error given:", httpsErr.message);
resolve(downloadFile(attachmentUrl, retries - 1));
}
});
});
});
}
export function noop() {
// IT'S LITERALLY NOTHING
}