Migrate ChannelArchiver to new Plugin structure, isOwner -> pluginUtils
This commit is contained in:
parent
a3d0ec03d9
commit
90ee4ad909
6 changed files with 174 additions and 0 deletions
|
@ -66,3 +66,13 @@ export function getBaseUrl(pluginData: PluginData<any>) {
|
|||
const knub = pluginData.getKnubInstance() as TZeppelinKnub;
|
||||
return knub.getGlobalConfig().url;
|
||||
}
|
||||
|
||||
export function isOwner(pluginData: PluginData<any>, userId: string) {
|
||||
const knub = pluginData.getKnubInstance() as TZeppelinKnub;
|
||||
const owners = knub.getGlobalConfig().owners;
|
||||
if (!owners) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return owners.includes(userId);
|
||||
}
|
||||
|
|
16
backend/src/plugins/ChannelArchiver/ChannelArchiverPlugin.ts
Normal file
16
backend/src/plugins/ChannelArchiver/ChannelArchiverPlugin.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { zeppelinPlugin } from "../ZeppelinPluginBlueprint";
|
||||
import { ChannelArchiverPluginType } from "./types";
|
||||
import { ArchiveChannelCmd } from "./commands/ArchiveChannelCmd";
|
||||
|
||||
export const ChannelArchiverPlugin = zeppelinPlugin<ChannelArchiverPluginType>()("channel_archiver", {
|
||||
showInDocs: false,
|
||||
|
||||
// prettier-ignore
|
||||
commands: [
|
||||
ArchiveChannelCmd,
|
||||
],
|
||||
|
||||
onLoad(pluginData) {
|
||||
const { state, guild } = pluginData;
|
||||
},
|
||||
});
|
|
@ -0,0 +1,110 @@
|
|||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||
import { channelArchiverCmd } from "../types";
|
||||
import { isOwner, sendErrorMessage } from "src/pluginUtils";
|
||||
import { confirm, SECONDS, noop } from "src/utils";
|
||||
import moment from "moment-timezone";
|
||||
import { rehostAttachment } from "../rehostAttachment";
|
||||
|
||||
const MAX_ARCHIVED_MESSAGES = 5000;
|
||||
const MAX_MESSAGES_PER_FETCH = 100;
|
||||
const PROGRESS_UPDATE_INTERVAL = 5 * SECONDS;
|
||||
|
||||
export const ArchiveChannelCmd = channelArchiverCmd({
|
||||
trigger: "archive_channel",
|
||||
permission: null,
|
||||
|
||||
config: {
|
||||
preFilters: [
|
||||
(command, context) => {
|
||||
return isOwner(context.pluginData, context.message.author.id);
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
signature: {
|
||||
channel: ct.textChannel(),
|
||||
|
||||
"attachment-channel": ct.textChannel({ option: true }),
|
||||
messages: ct.number({ option: true }),
|
||||
},
|
||||
|
||||
async run({ message: msg, args, pluginData }) {
|
||||
if (!args["attachment-channel"]) {
|
||||
const confirmed = await confirm(
|
||||
pluginData.client,
|
||||
msg.channel,
|
||||
msg.author.id,
|
||||
"No `-attachment-channel` specified. Continue? Attachments will not be available in the log if their message is deleted.",
|
||||
);
|
||||
if (!confirmed) {
|
||||
sendErrorMessage(pluginData, msg.channel, "Canceled");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const maxMessagesToArchive = args.messages ? Math.min(args.messages, MAX_ARCHIVED_MESSAGES) : MAX_ARCHIVED_MESSAGES;
|
||||
if (maxMessagesToArchive <= 0) return;
|
||||
|
||||
const archiveLines = [];
|
||||
let archivedMessages = 0;
|
||||
let previousId;
|
||||
|
||||
const startTime = Date.now();
|
||||
const progressMsg = await msg.channel.createMessage("Creating archive...");
|
||||
const progressUpdateInterval = setInterval(() => {
|
||||
const secondsSinceStart = Math.round((Date.now() - startTime) / 1000);
|
||||
progressMsg
|
||||
.edit(`Creating archive...\n**Status:** ${archivedMessages} messages archived in ${secondsSinceStart} seconds`)
|
||||
.catch(() => clearInterval(progressUpdateInterval));
|
||||
}, PROGRESS_UPDATE_INTERVAL);
|
||||
|
||||
while (archivedMessages < maxMessagesToArchive) {
|
||||
const messagesToFetch = Math.min(MAX_MESSAGES_PER_FETCH, maxMessagesToArchive - archivedMessages);
|
||||
const messages = await args.channel.getMessages(messagesToFetch, previousId);
|
||||
if (messages.length === 0) break;
|
||||
|
||||
for (const message of messages) {
|
||||
const ts = moment.utc(message.timestamp).format("YYYY-MM-DD HH:mm:ss");
|
||||
let content = `[${ts}] [${message.author.id}] [${message.author.username}#${
|
||||
message.author.discriminator
|
||||
}]: ${message.content || "<no text content>"}`;
|
||||
|
||||
if (message.attachments.length) {
|
||||
if (args["attachment-channel"]) {
|
||||
const rehostedAttachmentUrl = await rehostAttachment(message.attachments[0], args["attachment-channel"]);
|
||||
content += `\n-- Attachment: ${rehostedAttachmentUrl}`;
|
||||
} else {
|
||||
content += `\n-- Attachment: ${message.attachments[0].url}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (message.reactions && Object.keys(message.reactions).length > 0) {
|
||||
const reactionCounts = [];
|
||||
for (const [emoji, info] of Object.entries(message.reactions)) {
|
||||
reactionCounts.push(`${info.count}x ${emoji}`);
|
||||
}
|
||||
content += `\n-- Reactions: ${reactionCounts.join(", ")}`;
|
||||
}
|
||||
|
||||
archiveLines.push(content);
|
||||
previousId = message.id;
|
||||
archivedMessages++;
|
||||
}
|
||||
}
|
||||
|
||||
clearInterval(progressUpdateInterval);
|
||||
|
||||
archiveLines.reverse();
|
||||
|
||||
const nowTs = moment().format("YYYY-MM-DD HH:mm:ss");
|
||||
|
||||
let result = `Archived ${archiveLines.length} messages from #${args.channel.name} at ${nowTs}`;
|
||||
result += `\n\n${archiveLines.join("\n")}\n`;
|
||||
|
||||
progressMsg.delete().catch(noop);
|
||||
msg.channel.createMessage("Archive created!", {
|
||||
file: Buffer.from(result),
|
||||
name: `archive-${args.channel.name}-${moment().format("YYYY-MM-DD-HH-mm-ss")}.txt`,
|
||||
});
|
||||
},
|
||||
});
|
29
backend/src/plugins/ChannelArchiver/rehostAttachment.ts
Normal file
29
backend/src/plugins/ChannelArchiver/rehostAttachment.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { Attachment, TextChannel } from "eris";
|
||||
import { downloadFile } from "src/utils";
|
||||
import fs from "fs";
|
||||
const fsp = fs.promises;
|
||||
|
||||
const MAX_ATTACHMENT_REHOST_SIZE = 1024 * 1024 * 8;
|
||||
|
||||
export async function rehostAttachment(attachment: Attachment, targetChannel: TextChannel): Promise<string> {
|
||||
if (attachment.size > MAX_ATTACHMENT_REHOST_SIZE) {
|
||||
return "Attachment too big to rehost";
|
||||
}
|
||||
|
||||
let downloaded;
|
||||
try {
|
||||
downloaded = await downloadFile(attachment.url, 3);
|
||||
} catch (e) {
|
||||
return "Failed to download attachment after 3 tries";
|
||||
}
|
||||
|
||||
try {
|
||||
const rehostMessage = await targetChannel.createMessage(`Rehost of attachment ${attachment.id}`, {
|
||||
name: attachment.filename,
|
||||
file: await fsp.readFile(downloaded.path),
|
||||
});
|
||||
return rehostMessage.attachments[0].url;
|
||||
} catch (e) {
|
||||
return "Failed to rehost attachment";
|
||||
}
|
||||
}
|
7
backend/src/plugins/ChannelArchiver/types.ts
Normal file
7
backend/src/plugins/ChannelArchiver/types.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { BasePluginType, command } from "knub";
|
||||
|
||||
export interface ChannelArchiverPluginType extends BasePluginType {
|
||||
state: {};
|
||||
}
|
||||
|
||||
export const channelArchiverCmd = command<ChannelArchiverPluginType>();
|
|
@ -13,10 +13,12 @@ import { GuildConfigReloaderPlugin } from "./GuildConfigReloader/GuildConfigRelo
|
|||
import { CasesPlugin } from "./Cases/CasesPlugin";
|
||||
import { MutesPlugin } from "./Mutes/MutesPlugin";
|
||||
import { TagsPlugin } from "./Tags/TagsPlugin";
|
||||
import { ChannelArchiverPlugin } from "./ChannelArchiver/ChannelArchiverPlugin";
|
||||
|
||||
// prettier-ignore
|
||||
export const guildPlugins: Array<ZeppelinPluginBlueprint<any>> = [
|
||||
AutoReactionsPlugin,
|
||||
ChannelArchiverPlugin,
|
||||
LocateUserPlugin,
|
||||
PersistPlugin,
|
||||
PingableRolesPlugin,
|
||||
|
|
Loading…
Add table
Reference in a new issue