zappyzep/src/plugins/BotControl.ts

235 lines
7.4 KiB
TypeScript
Raw Normal View History

import { decorators as d, IPluginOptions } from "knub";
import child_process from "child_process";
import { GuildChannel, Message, TextChannel } from "eris";
import moment from "moment-timezone";
import { createChunkedMessage, errorMessage, noop, sorter, successMessage, tNullable } from "../utils";
import { ReactionRolesPlugin } from "./ReactionRoles";
import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { GuildArchives } from "../data/GuildArchives";
import { GlobalZeppelinPlugin } from "./GlobalZeppelinPlugin";
import * as t from "io-ts";
let activeReload: [string, string] = null;
2018-07-01 03:35:51 +03:00
const ConfigSchema = t.type({
can_use: t.boolean,
owners: t.array(t.string),
update_cmd: tNullable(t.string),
});
type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
2018-07-01 03:35:51 +03:00
/**
* A global plugin that allows bot owners to control the bot
*/
export class BotControlPlugin extends GlobalZeppelinPlugin<TConfigSchema> {
2019-02-08 21:04:04 +02:00
public static pluginName = "bot_control";
2019-08-22 02:58:32 +03:00
public static configSchema = ConfigSchema;
protected archives: GuildArchives;
2019-08-22 01:22:26 +03:00
public static getStaticDefaultOptions() {
2018-07-01 03:35:51 +03:00
return {
config: {
can_use: false,
owners: [],
update_cmd: null,
},
overrides: [
{
level: ">=100",
config: {
can_use: true,
},
},
],
2018-07-01 03:35:51 +03:00
};
}
protected getMemberLevel(member) {
return this.isOwner(member.id) ? 100 : 0;
}
async onLoad() {
this.archives = new GuildArchives(0);
if (activeReload) {
const [guildId, channelId] = activeReload;
activeReload = null;
const guild = this.bot.guilds.get(guildId);
if (guild) {
const channel = guild.channels.get(channelId);
if (channel instanceof TextChannel) {
channel.createMessage(successMessage("Global plugins reloaded!"));
}
}
}
}
@d.command("bot_full_update")
@d.permission("can_use")
async fullUpdateCmd(msg: Message) {
const updateCmd = this.getConfig().update_cmd;
2018-07-01 03:35:51 +03:00
if (!updateCmd) {
msg.channel.createMessage(errorMessage("Update command not specified!"));
2018-07-01 03:35:51 +03:00
return;
}
msg.channel.createMessage("Updating...");
const updater = child_process.exec(updateCmd, { cwd: process.cwd() });
updater.stderr.on("data", data => {
// tslint:disable-next-line
console.error(data);
});
}
@d.command("bot_reload_global_plugins")
@d.permission("can_use")
async reloadGlobalPluginsCmd(msg: Message) {
if (activeReload) return;
if (msg.channel) {
activeReload = [(msg.channel as GuildChannel).guild.id, msg.channel.id];
await msg.channel.createMessage("Reloading global plugins...");
}
2018-07-01 03:35:51 +03:00
this.knub.reloadAllGlobalPlugins();
2018-07-01 03:35:51 +03:00
}
2019-02-08 21:04:04 +02:00
@d.command("perf")
@d.permission("can_use")
2019-02-08 21:04:04 +02:00
async perfCmd(msg: Message) {
const perfItems = this.knub.getPerformanceDebugItems();
if (perfItems.length) {
const content = "```" + perfItems.join("\n") + "```";
msg.channel.createMessage(content);
} else {
msg.channel.createMessage(errorMessage("No performance data"));
}
}
@d.command("refresh_reaction_roles_globally")
@d.permission("can_use")
async refreshAllReactionRolesCmd(msg: Message) {
const guilds = this.knub.getLoadedGuilds();
for (const guild of guilds) {
if (guild.loadedPlugins.has("reaction_roles")) {
2019-03-07 20:50:53 +02:00
const rrPlugin = (guild.loadedPlugins.get("reaction_roles") as unknown) as ReactionRolesPlugin;
rrPlugin.runAutoRefresh().catch(noop);
}
}
}
@d.command("guilds")
@d.permission("can_use")
async serversCmd(msg: Message) {
const joinedGuilds = Array.from(this.bot.guilds.values());
const loadedGuilds = this.knub.getLoadedGuilds();
const loadedGuildsMap = loadedGuilds.reduce((map, guildData) => map.set(guildData.id, guildData), new Map());
joinedGuilds.sort(sorter(g => g.name.toLowerCase()));
const longestId = joinedGuilds.reduce((longest, guild) => Math.max(longest, guild.id.length), 0);
const lines = joinedGuilds.map(g => {
const paddedId = g.id.padEnd(longestId, " ");
return `\`${paddedId}\` **${g.name}** (${loadedGuildsMap.has(g.id) ? "initialized" : "not initialized"}) (${
g.memberCount
} members)`;
});
createChunkedMessage(msg.channel, lines.join("\n"));
}
@d.command("leave_guild", "<guildId:string>")
@d.permission("can_use")
async leaveGuildCmd(msg: Message, args: { guildId: string }) {
if (!this.bot.guilds.has(args.guildId)) {
msg.channel.createMessage(errorMessage("I am not in that guild"));
return;
}
const guildToLeave = this.bot.guilds.get(args.guildId);
const guildName = guildToLeave.name;
try {
await this.bot.leaveGuild(args.guildId);
} catch (e) {
msg.channel.createMessage(errorMessage(`Failed to leave guild: ${e.message}`));
return;
}
msg.channel.createMessage(successMessage(`Left guild **${guildName}**`));
}
@d.command("reload_guild", "<guildId:string>")
@d.permission("can_use")
async reloadGuildCmd(msg: Message, args: { guildId: string }) {
if (!this.bot.guilds.has(args.guildId)) {
msg.channel.createMessage(errorMessage("I am not in that guild"));
return;
}
try {
await this.knub.reloadGuild(args.guildId);
} catch (e) {
msg.channel.createMessage(errorMessage(`Failed to reload guild: ${e.message}`));
return;
}
const guild = this.bot.guilds.get(args.guildId);
msg.channel.createMessage(successMessage(`Reloaded guild **${guild.name}**`));
}
@d.command("reload_all_guilds")
@d.permission("can_use")
async reloadAllGuilds(msg: Message) {
const failedReloads: Map<string, string> = new Map();
let reloadCount = 0;
const loadedGuilds = this.knub.getLoadedGuilds();
for (const guildData of loadedGuilds) {
try {
await this.knub.reloadGuild(guildData.id);
reloadCount++;
} catch (e) {
failedReloads.set(guildData.id, e.message);
}
}
if (failedReloads.size) {
const errorLines = Array.from(failedReloads.entries()).map(([guildId, err]) => {
const guild = this.bot.guilds.get(guildId);
const guildName = guild ? guild.name : "Unknown";
return `${guildName} (${guildId}): ${err}`;
});
createChunkedMessage(msg.channel, `Reloaded ${reloadCount} guild(s). Errors:\n${errorLines.join("\n")}`);
} else {
msg.channel.createMessage(successMessage(`Reloaded ${reloadCount} guild(s)`));
}
}
@d.command("show_plugin_config", "<guildId:string> <pluginName:string>")
@d.permission("can_use")
async showPluginConfig(msg: Message, args: { guildId: string; pluginName: string }) {
const guildData = this.knub.getGuildData(args.guildId);
if (!guildData) {
msg.channel.createMessage(errorMessage(`Guild not loaded`));
return;
}
const pluginInstance = guildData.loadedPlugins.get(args.pluginName);
if (!pluginInstance) {
msg.channel.createMessage(errorMessage(`Plugin not loaded`));
return;
}
if (!(pluginInstance instanceof ZeppelinPlugin)) {
msg.channel.createMessage(errorMessage(`Plugin is not a Zeppelin plugin`));
return;
}
const opts = pluginInstance.getRuntimeOptions();
const archiveId = await this.archives.create(JSON.stringify(opts, null, 2), moment().add(15, "minutes"));
msg.channel.createMessage(this.archives.getUrl(this.knub.getGlobalConfig().url, archiveId));
}
2018-07-01 03:35:51 +03:00
}