From 0ee942728c6a353c5f9dff63c09e31bda6e43716 Mon Sep 17 00:00:00 2001 From: Dark <7890309+DarkView@users.noreply.github.com> Date: Tue, 28 Jan 2020 23:17:09 +0100 Subject: [PATCH 01/10] Updated Warn Threshold to work with latest release --- backend/src/plugins/Cases.ts | 15 ++++++++++ backend/src/plugins/ModActions.ts | 49 ++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/backend/src/plugins/Cases.ts b/backend/src/plugins/Cases.ts index ac5da024..bc44d22c 100644 --- a/backend/src/plugins/Cases.ts +++ b/backend/src/plugins/Cases.ts @@ -244,6 +244,21 @@ export class CasesPlugin extends ZeppelinPlugin { return { embed }; } + public async getCaseTypeAmountForUserId(userID: string, type: CaseTypes): Promise { + const cases = (await this.cases.getByUserId(userID)).filter(c => !c.is_hidden); + let typeAmount = 0; + + if (cases.length > 0) { + cases.forEach(singleCase => { + if (singleCase.type === type.valueOf()) { + typeAmount++; + } + }); + } + + return typeAmount; + } + /** * A helper for posting to the case log channel. * Returns silently if the case log channel isn't specified or is invalid. diff --git a/backend/src/plugins/ModActions.ts b/backend/src/plugins/ModActions.ts index 74042b9f..18226b33 100644 --- a/backend/src/plugins/ModActions.ts +++ b/backend/src/plugins/ModActions.ts @@ -46,6 +46,8 @@ const ConfigSchema = t.type({ ban_message: tNullable(t.string), alert_on_rejoin: t.boolean, alert_channel: tNullable(t.string), + warn_notify_threshold: t.number, + warn_notify_message: t.string, can_note: t.boolean, can_warn: t.boolean, can_mute: t.boolean, @@ -146,6 +148,9 @@ export class ModActionsPlugin extends ZeppelinPlugin { ban_message: "You have been banned from the {guildName} server. Reason given: {reason}", alert_on_rejoin: false, alert_channel: null, + warn_notify_threshold: 1, + warn_notify_message: + "The user already has **{priorWarnings}** warnings!\n Please check their prior cases and assess whether or not to warn anyways.\n Proceed with the warning?", can_note: false, can_warn: false, @@ -197,10 +202,7 @@ export class ModActionsPlugin extends ZeppelinPlugin { } clearIgnoredEvent(type: IgnoredEventType, userId: any) { - this.ignoredEvents.splice( - this.ignoredEvents.findIndex(info => type === info.type && userId === info.userId), - 1, - ); + this.ignoredEvents.splice(this.ignoredEvents.findIndex(info => type === info.type && userId === info.userId), 1); } formatReasonWithAttachments(reason: string, attachments: Attachment[]) { @@ -328,7 +330,9 @@ export class ModActionsPlugin extends ZeppelinPlugin { if (actions.length) { const alertChannel: any = this.guild.channels.get(alertChannelId); alertChannel.send( - `<@!${member.id}> (${member.user.username}#${member.user.discriminator} \`${member.id}\`) joined with ${actions.length} prior record(s)`, + `<@!${member.id}> (${member.user.username}#${member.user.discriminator} \`${member.id}\`) joined with ${ + actions.length + } prior record(s)`, ); } } @@ -349,7 +353,9 @@ export class ModActionsPlugin extends ZeppelinPlugin { const existingCaseForThisEntry = await this.cases.findByAuditLogId(kickAuditLogEntry.id); if (existingCaseForThisEntry) { logger.warn( - `Tried to create duplicate case for audit log entry ${kickAuditLogEntry.id}, existing case id ${existingCaseForThisEntry.id}`, + `Tried to create duplicate case for audit log entry ${kickAuditLogEntry.id}, existing case id ${ + existingCaseForThisEntry.id + }`, ); } else { const casesPlugin = this.getPlugin("cases"); @@ -605,6 +611,21 @@ export class ModActionsPlugin extends ZeppelinPlugin { const config = this.getConfig(); const reason = this.formatReasonWithAttachments(args.reason, msg.attachments); + const casesPlugin = this.getPlugin("cases"); + const priorWarnAmount = await casesPlugin.getCaseTypeAmountForUserId(memberToWarn.id, CaseTypes.Warn); + if (priorWarnAmount >= config.warn_notify_threshold) { + const tooManyWarningsMsg = await msg.channel.createMessage( + config.warn_notify_message.replace("{priorWarnings}", `${priorWarnAmount}`), + ); + + const reply = await waitForReaction(this.bot, tooManyWarningsMsg, ["✅", "❌"]); + tooManyWarningsMsg.delete(); + if (!reply || reply.name === "❌") { + msg.channel.createMessage(errorMessage("Warn cancelled by moderator")); + return; + } + } + const warnMessage = config.warn_message.replace("{guildName}", this.guild.name).replace("{reason}", reason); const warnResult = await this.warnMember( memberToWarn, @@ -626,7 +647,9 @@ export class ModActionsPlugin extends ZeppelinPlugin { this.sendSuccessMessage( msg.channel, - `Warned **${memberToWarn.user.username}#${memberToWarn.user.discriminator}** (Case #${warnResult.case.case_number})${messageResultText}`, + `Warned **${memberToWarn.user.username}#${memberToWarn.user.discriminator}** (Case #${ + warnResult.case.case_number + })${messageResultText}`, ); } @@ -1013,7 +1036,9 @@ export class ModActionsPlugin extends ZeppelinPlugin { } // Confirm the action to the moderator - let response = `Kicked **${memberToKick.user.username}#${memberToKick.user.discriminator}** (Case #${kickResult.case.case_number})`; + let response = `Kicked **${memberToKick.user.username}#${memberToKick.user.discriminator}** (Case #${ + kickResult.case.case_number + })`; if (kickResult.notifyResult.text) response += ` (${kickResult.notifyResult.text})`; this.sendSuccessMessage(msg.channel, response); @@ -1074,7 +1099,9 @@ export class ModActionsPlugin extends ZeppelinPlugin { } // Confirm the action to the moderator - let response = `Banned **${memberToBan.user.username}#${memberToBan.user.discriminator}** (Case #${banResult.case.case_number})`; + let response = `Banned **${memberToBan.user.username}#${memberToBan.user.discriminator}** (Case #${ + banResult.case.case_number + })`; if (banResult.notifyResult.text) response += ` (${banResult.notifyResult.text})`; this.sendSuccessMessage(msg.channel, response); @@ -1159,7 +1186,9 @@ export class ModActionsPlugin extends ZeppelinPlugin { // Confirm the action to the moderator this.sendSuccessMessage( msg.channel, - `Softbanned **${memberToSoftban.user.username}#${memberToSoftban.user.discriminator}** (Case #${createdCase.case_number})`, + `Softbanned **${memberToSoftban.user.username}#${memberToSoftban.user.discriminator}** (Case #${ + createdCase.case_number + })`, ); // Log the action From 22982b8113357c4db148a71ebb7b5929daf17f1f Mon Sep 17 00:00:00 2001 From: roflmaoqwerty <36663568+roflmaoqwerty@users.noreply.github.com> Date: Thu, 20 Feb 2020 22:57:35 +1100 Subject: [PATCH 02/10] added a limit of 2048 to the resize command so it cant cripple the bot --- backend/src/plugins/Utility.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/plugins/Utility.ts b/backend/src/plugins/Utility.ts index e998d756..ffd085a5 100644 --- a/backend/src/plugins/Utility.ts +++ b/backend/src/plugins/Utility.ts @@ -1539,6 +1539,7 @@ export class UtilityPlugin extends ZeppelinPlugin { async jumboCmd(msg: Message, args: { emoji: string }) { // Get emoji url const config = this.getConfig(); + const size = config.jumbo_size > 2048 ? 2048 : config.jumbo_size; const emojiRegex = new RegExp(`(<.*:).*:(\\d+)`); const results = emojiRegex.exec(args.emoji); let extention = ".png"; @@ -1551,7 +1552,7 @@ export class UtilityPlugin extends ZeppelinPlugin { } url += `${results[2]}${extention}`; if (extention === ".png") { - const image = await this.resizeBuffer(await this.getBufferFromUrl(url), config.jumbo_size, config.jumbo_size); + const image = await this.resizeBuffer(await this.getBufferFromUrl(url), size, size); file = { name: `emoji${extention}`, file: image, @@ -1567,11 +1568,11 @@ export class UtilityPlugin extends ZeppelinPlugin { let url = CDN_URL + `/${twemoji.convert.toCodePoint(args.emoji)}.svg`; let image; try { - image = await this.resizeBuffer(await this.getBufferFromUrl(url), config.jumbo_size, config.jumbo_size); + image = await this.resizeBuffer(await this.getBufferFromUrl(url), size, size); } catch { if (url.toLocaleLowerCase().endsWith("fe0f.svg")) { url = url.slice(0, url.lastIndexOf("-fe0f")) + ".svg"; - image = await this.resizeBuffer(await this.getBufferFromUrl(url), config.jumbo_size, config.jumbo_size); + image = await this.resizeBuffer(await this.getBufferFromUrl(url), size, size); } } file = { From 2c63509084e0ca883f709937f10107814871c497 Mon Sep 17 00:00:00 2001 From: Dark <7890309+DarkView@users.noreply.github.com> Date: Fri, 20 Mar 2020 18:04:37 +0100 Subject: [PATCH 03/10] Added ability to white or blacklist attachment filetype in automod Intended to allow certain channels (i.e. bug reporting ones) to only allow .log files with uploads enabled, making it impossible to upload mp4s to troll or similar --- backend/src/plugins/Automod/Automod.ts | 57 ++++++++++++++++++++++++++ backend/src/plugins/Automod/types.ts | 15 +++++++ 2 files changed, 72 insertions(+) diff --git a/backend/src/plugins/Automod/Automod.ts b/backend/src/plugins/Automod/Automod.ts index 609cdb83..d6b91400 100644 --- a/backend/src/plugins/Automod/Automod.ts +++ b/backend/src/plugins/Automod/Automod.ts @@ -60,10 +60,12 @@ import { TMatchWordsTrigger, TMemberJoinTrigger, TRule, + TMatchAttachmentTypeTrigger, } from "./types"; import { pluginInfo } from "./info"; import { ERRORS, RecoverablePluginError } from "../../RecoverablePluginError"; import Timeout = NodeJS.Timeout; +import { StrictValidationError } from "src/validatorUtils"; const unactioned = (action: TextRecentAction | OtherRecentAction) => !action.actioned; @@ -116,6 +118,19 @@ const defaultMatchLinksTrigger: Partial = { match_custom_status: false, }; +const defaultMatchAttachmentTypeTrigger: Partial = { + filetype_blacklist: [], + blacklist_enabled: false, + filetype_whitelist: [], + whitelist_enabled: false, + match_messages: true, + match_embeds: true, + match_visible_names: false, + match_usernames: false, + match_nicknames: false, + match_custom_status: false, +}; + const defaultTextSpamTrigger: Partial> = { per_channel: true, }; @@ -130,6 +145,7 @@ const defaultTriggers = { match_regex: defaultMatchRegexTrigger, match_invites: defaultMatchInvitesTrigger, match_links: defaultMatchLinksTrigger, + match_attachment_type: defaultMatchAttachmentTypeTrigger, message_spam: defaultTextSpamTrigger, mention_spam: defaultTextSpamTrigger, link_spam: defaultTextSpamTrigger, @@ -249,6 +265,21 @@ export class AutomodPlugin extends ZeppelinPlugin`, + ]); + } else if (!white && !black) { + throw new StrictValidationError([ + `Must have either blacklist or whitelist enabled at rule <${rule.name}/match_attachment_type>`, + ]); + } + } } } @@ -457,6 +488,23 @@ export class AutomodPlugin extends ZeppelinPlugin { + return this.evaluateMatchAttachmentTypeTrigger(trigger.match_attachment_type, msg); + }); + if (match) return { ...match, trigger: "match_attachment_type" } as TextTriggerMatchResult; + } + if (trigger.message_spam) { const match = this.matchTextSpamTrigger(RecentActionType.Message, trigger.message_spam, msg); if (match) return { ...match, rule, trigger: "message_spam" }; @@ -1319,6 +1374,8 @@ export class AutomodPlugin extends ZeppelinPlugin; +export const MatchAttachmentTypeTrigger = t.type({ + filetype_blacklist: t.array(t.string), + blacklist_enabled: t.boolean, + filetype_whitelist: t.array(t.string), + whitelist_enabled: t.boolean, + match_messages: t.boolean, + match_embeds: t.boolean, + match_visible_names: t.boolean, + match_usernames: t.boolean, + match_nicknames: t.boolean, + match_custom_status: t.boolean, +}); +export type TMatchAttachmentTypeTrigger = t.TypeOf; + export const BaseSpamTrigger = t.type({ amount: t.number, within: t.string, @@ -280,6 +294,7 @@ export const Rule = t.type({ match_regex: tNullable(MatchRegexTrigger), match_invites: tNullable(MatchInvitesTrigger), match_links: tNullable(MatchLinksTrigger), + match_attachment_type: tNullable(MatchAttachmentTypeTrigger), message_spam: tNullable(MessageSpamTrigger), mention_spam: tNullable(MentionSpamTrigger), link_spam: tNullable(LinkSpamTrigger), From 5afe3ce3fe18e8044d7e1effd8d30e37f97a48d0 Mon Sep 17 00:00:00 2001 From: Dark <7890309+DarkView@users.noreply.github.com> Date: Sun, 2 Feb 2020 17:01:03 +0100 Subject: [PATCH 04/10] Made Alerts infinite until timed out, added -active and changed cmd sigm --- backend/src/data/GuildVCAlerts.ts | 3 +- backend/src/data/entities/VCAlert.ts | 2 + ...0654617890-AddActiveFollowsToLocateUser.ts | 18 ++ backend/src/plugins/LocateUser.ts | 163 +++++++++++++++--- 4 files changed, 159 insertions(+), 27 deletions(-) create mode 100644 backend/src/migrations/1580654617890-AddActiveFollowsToLocateUser.ts diff --git a/backend/src/data/GuildVCAlerts.ts b/backend/src/data/GuildVCAlerts.ts index 9da16dec..6acc35e9 100644 --- a/backend/src/data/GuildVCAlerts.ts +++ b/backend/src/data/GuildVCAlerts.ts @@ -50,7 +50,7 @@ export class GuildVCAlerts extends BaseGuildRepository { }); } - async add(requestorId: string, userId: string, channelId: string, expiresAt: string, body: string) { + async add(requestorId: string, userId: string, channelId: string, expiresAt: string, body: string, active: boolean) { await this.allAlerts.insert({ guild_id: this.guildId, requestor_id: requestorId, @@ -58,6 +58,7 @@ export class GuildVCAlerts extends BaseGuildRepository { channel_id: channelId, expires_at: expiresAt, body, + active, }); } } diff --git a/backend/src/data/entities/VCAlert.ts b/backend/src/data/entities/VCAlert.ts index 4bdf965e..e627afa6 100644 --- a/backend/src/data/entities/VCAlert.ts +++ b/backend/src/data/entities/VCAlert.ts @@ -17,4 +17,6 @@ export class VCAlert { @Column() expires_at: string; @Column() body: string; + + @Column() active: boolean; } diff --git a/backend/src/migrations/1580654617890-AddActiveFollowsToLocateUser.ts b/backend/src/migrations/1580654617890-AddActiveFollowsToLocateUser.ts new file mode 100644 index 00000000..430b10de --- /dev/null +++ b/backend/src/migrations/1580654617890-AddActiveFollowsToLocateUser.ts @@ -0,0 +1,18 @@ +import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; + +export class AddActiveFollowsToLocateUser1580654617890 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.addColumn( + "vc_alerts", + new TableColumn({ + name: "active", + type: "boolean", + isNullable: false, + }), + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropColumn("vc_alerts", "active"); + } +} diff --git a/backend/src/plugins/LocateUser.ts b/backend/src/plugins/LocateUser.ts index c79ff368..c0b8790c 100644 --- a/backend/src/plugins/LocateUser.ts +++ b/backend/src/plugins/LocateUser.ts @@ -1,10 +1,10 @@ import { decorators as d, IPluginOptions, getInviteLink, logger } from "knub"; -import { trimPluginDescription, ZeppelinPlugin } from "./ZeppelinPlugin"; +import { trimPluginDescription, ZeppelinPlugin, CommandInfo } from "./ZeppelinPlugin"; import humanizeDuration from "humanize-duration"; -import { Message, Member, Guild, TextableChannel, VoiceChannel, Channel, User } from "eris"; +import { Message, Member, Guild, TextableChannel, VoiceChannel, Channel, User, Command } from "eris"; import { GuildVCAlerts } from "../data/GuildVCAlerts"; import moment from "moment-timezone"; -import { resolveMember, sorter, createChunkedMessage, errorMessage, successMessage, MINUTES } from "../utils"; +import { resolveMember, sorter, createChunkedMessage, MINUTES, SECONDS } from "../utils"; import * as t from "io-ts"; const ConfigSchema = t.type({ @@ -13,7 +13,7 @@ const ConfigSchema = t.type({ }); type TConfigSchema = t.TypeOf; -const ALERT_LOOP_TIME = 30 * 1000; +const ALERT_LOOP_TIME = 30 * SECONDS; export class LocatePlugin extends ZeppelinPlugin { public static pluginName = "locate_user"; @@ -29,8 +29,9 @@ export class LocatePlugin extends ZeppelinPlugin { }; private alerts: GuildVCAlerts; - private outdatedAlertsTimeout; + private outdatedAlertsTimeout: NodeJS.Timeout; private usersWithAlerts: string[] = []; + private unloaded = false; public static getStaticDefaultOptions(): IPluginOptions { return { @@ -56,6 +57,11 @@ export class LocatePlugin extends ZeppelinPlugin { this.fillActiveAlertsList(); } + onUnload() { + clearTimeout(this.outdatedAlertsTimeout); + this.unloaded = true; + } + async outdatedAlertsLoop() { const outdatedAlerts = await this.alerts.getOutdatedAlerts(); @@ -64,7 +70,9 @@ export class LocatePlugin extends ZeppelinPlugin { await this.removeUserIdFromActiveAlerts(alert.user_id); } - this.outdatedAlertsTimeout = setTimeout(() => this.outdatedAlertsLoop(), ALERT_LOOP_TIME); + if (!this.unloaded) { + this.outdatedAlertsTimeout = setTimeout(() => this.outdatedAlertsLoop(), ALERT_LOOP_TIME); + } } async fillActiveAlertsList() { @@ -80,8 +88,12 @@ export class LocatePlugin extends ZeppelinPlugin { @d.command("where", "", { aliases: ["w"], extra: { - info: { + info: { description: "Posts an instant invite to the voice channel that `` is in", + basicUsage: "!where @Dark", + parameterDescriptions: { + member: "The member that we want to find", + }, }, }, }) @@ -91,32 +103,94 @@ export class LocatePlugin extends ZeppelinPlugin { sendWhere(this.guild, member, msg.channel, `${msg.member.mention} |`); } - @d.command("vcalert", " ", { - overloads: [" ", ""], + @d.command("vcalert", " [reminder:string$]", { aliases: ["vca"], + options: [ + { + name: "duration", + shortcut: "d", + type: "delay", + }, + { + name: "active", + shortcut: "a", + isSwitch: true, + }, + ], extra: { - info: { + info: { description: "Sets up an alert that notifies you any time `` switches or joins voice channels", + basicUsage: "!vca @Dark", + examples: trimPluginDescription(` + To get an alert for 1 hour: + \`!vca 108552944961454080 -d 1h\` + + To get an alert for 2 hours and 30 minutes with the reminder "Earrape": + \`!vca 108552944961454080 -d 2h30m Earrape\` or \`!vca 108552944961454080 Earrape -d 1h\` + + To get an alert for 3 days and be moved to the channel: + \`!vca 108552944961454080 -d 3d -a\` + `), + optionDescriptions: { + duration: "How long the alert shall be active. The alert will be automatically deleted after this time", + active: " A switch that, when true, will move you to the channel the user joined", + }, + parameterDescriptions: { + member: "The server member we want to set as the alerts target", + reminder: "Any text that will be displayed every time the alert triggers", + }, }, }, }) @d.permission("can_alert") - async vcalertCmd(msg: Message, args: { member: Member; duration?: number; reminder?: string }) { + async vcalertCmd(msg: Message, args: { member: Member; reminder?: string; duration?: number; active?: boolean }) { const time = args.duration || 10 * MINUTES; const alertTime = moment().add(time, "millisecond"); const body = args.reminder || "None"; + const active = args.active || false; - this.alerts.add(msg.author.id, args.member.id, msg.channel.id, alertTime.format("YYYY-MM-DD HH:mm:ss"), body); + if (time < 30 * SECONDS) { + this.sendErrorMessage(msg.channel, "Sorry, but the minimum duration for an alert is 30 seconds!"); + return; + } + + this.alerts.add( + msg.author.id, + args.member.id, + msg.channel.id, + alertTime.format("YYYY-MM-DD HH:mm:ss"), + body, + active, + ); if (!this.usersWithAlerts.includes(args.member.id)) { this.usersWithAlerts.push(args.member.id); } - msg.channel.createMessage( - `If ${args.member.mention} joins or switches VC in the next ${humanizeDuration(time)} i will notify you`, - ); + if (active) { + this.sendSuccessMessage( + msg.channel, + `Every time ${args.member.mention} joins or switches VC in the next ${humanizeDuration( + time, + )} i will notify and move you.\nPlease make sure to be in a voice channel, otherwise i cannot move you!`, + ); + } else { + this.sendSuccessMessage( + msg.channel, + `Every time ${args.member.mention} joins or switches VC in the next ${humanizeDuration( + time, + )} i will notify you`, + ); + } } - @d.command("vcalerts") + @d.command("vcalerts", [], { + aliases: ["vca"], + extra: { + info: { + description: "Displays all of your active alerts ordered by expiration time", + }, + }, + }) @d.permission("can_alert") async listVcalertCmd(msg: Message) { const alerts = await this.alerts.getAlertsByRequestorId(msg.member.id); @@ -130,22 +204,29 @@ export class LocatePlugin extends ZeppelinPlugin { const lines = Array.from(alerts.entries()).map(([i, alert]) => { const num = i + 1; const paddedNum = num.toString().padStart(longestNum, " "); - return `\`${paddedNum}.\` \`${alert.expires_at}\` Member: <@!${alert.user_id}> Reminder: \`${alert.body}\``; + return `\`${paddedNum}.\` \`${alert.expires_at}\` Target: <@!${alert.user_id}> Reminder: \`${ + alert.body + }\` Active: ${alert.active.valueOf()}`; }); createChunkedMessage(msg.channel, lines.join("\n")); } @d.command("vcalerts delete", "", { - aliases: ["vcalerts d"], + aliases: ["vcalerts d", "vca d"], + extra: { + info: { + description: + "Deletes the alert at the position .\nThe value needed for can be found using `!vcalerts`", + }, + }, }) @d.permission("can_alert") async deleteVcalertCmd(msg: Message, args: { num: number }) { const alerts = await this.alerts.getAlertsByRequestorId(msg.member.id); alerts.sort(sorter("expires_at")); - const lastNum = alerts.length + 1; - if (args.num > lastNum || args.num < 0) { - msg.channel.createMessage(errorMessage("Unknown alert")); + if (args.num > alerts.length || args.num < 0) { + this.sendErrorMessage(msg.channel, "Unknown alert!"); return; } @@ -159,7 +240,6 @@ export class LocatePlugin extends ZeppelinPlugin { async userJoinedVC(member: Member, channel: Channel) { if (this.usersWithAlerts.includes(member.id)) { this.sendAlerts(member.id); - await this.removeUserIdFromActiveAlerts(member.id); } } @@ -167,10 +247,22 @@ export class LocatePlugin extends ZeppelinPlugin { async userSwitchedVC(member: Member, newChannel: Channel, oldChannel: Channel) { if (this.usersWithAlerts.includes(member.id)) { this.sendAlerts(member.id); - await this.removeUserIdFromActiveAlerts(member.id); } } + @d.event("voiceChannelLeave") + async userLeftVC(member: Member, channel: Channel) { + const triggeredAlerts = await this.alerts.getAlertsByUserId(member.id); + const voiceChannel = channel as VoiceChannel; + + triggeredAlerts.forEach(alert => { + const txtChannel = this.bot.getChannel(alert.channel_id) as TextableChannel; + txtChannel.createMessage( + `🔴 <@!${alert.requestor_id}> the user <@!${alert.user_id}> disconnected out of \`${voiceChannel.name}\``, + ); + }); + } + @d.event("guildBanAdd") async onGuildBanAdd(_, user: User) { const alerts = await this.alerts.getAlertsByUserId(user.id); @@ -185,8 +277,11 @@ export class LocatePlugin extends ZeppelinPlugin { triggeredAlerts.forEach(alert => { const prepend = `<@!${alert.requestor_id}>, an alert requested by you has triggered!\nReminder: \`${alert.body}\`\n`; - sendWhere(this.guild, member, this.bot.getChannel(alert.channel_id) as TextableChannel, prepend); - this.alerts.delete(alert.id); + const txtChannel = this.bot.getChannel(alert.channel_id) as TextableChannel; + sendWhere(this.guild, member, txtChannel, prepend); + if (alert.active) { + this.moveMember(alert.requestor_id, member, txtChannel); + } }); } @@ -196,6 +291,22 @@ export class LocatePlugin extends ZeppelinPlugin { this.usersWithAlerts.splice(index, 1); } } + + async moveMember(toMoveID: string, target: Member, errorChannel: TextableChannel) { + const modMember: Member = await this.bot.getRESTGuildMember(this.guildId, toMoveID); + if (modMember.voiceState.channelID != null) { + try { + await modMember.edit({ + channelID: target.voiceState.channelID, + }); + } catch (e) { + this.sendErrorMessage(errorChannel, "Failed to move you. Are you in a voice channel?"); + return; + } + } else { + this.sendErrorMessage(errorChannel, "Failed to move you. Are you in a voice channel?"); + } + } } export async function sendWhere(guild: Guild, member: Member, channel: TextableChannel, prepend: string) { @@ -212,7 +323,7 @@ export async function sendWhere(guild: Guild, member: Member, channel: TextableC return; } channel.createMessage( - prepend + ` ${member.mention} is in the following channel: ${voice.name} ${getInviteLink(invite)}`, + prepend + ` ${member.mention} is in the following channel: \`${voice.name}\` ${getInviteLink(invite)}`, ); } } From b787ec146afbf07aecc6ccb949396617cbec2a0e Mon Sep 17 00:00:00 2001 From: Dark <7890309+DarkView@users.noreply.github.com> Date: Sun, 16 Feb 2020 17:13:58 +0100 Subject: [PATCH 05/10] Fix spacing before this gets pulled --- backend/src/plugins/LocateUser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/plugins/LocateUser.ts b/backend/src/plugins/LocateUser.ts index c0b8790c..8d124fca 100644 --- a/backend/src/plugins/LocateUser.ts +++ b/backend/src/plugins/LocateUser.ts @@ -100,7 +100,7 @@ export class LocatePlugin extends ZeppelinPlugin { @d.permission("can_where") async whereCmd(msg: Message, args: { member: Member }) { const member = await resolveMember(this.bot, this.guild, args.member.id); - sendWhere(this.guild, member, msg.channel, `${msg.member.mention} |`); + sendWhere(this.guild, member, msg.channel, `${msg.member.mention} | `); } @d.command("vcalert", " [reminder:string$]", { From e6952aee2da065bad1d2a466d6836228d50d4f7e Mon Sep 17 00:00:00 2001 From: Dark <7890309+DarkView@users.noreply.github.com> Date: Thu, 27 Feb 2020 23:32:46 +0100 Subject: [PATCH 06/10] Change vcalert to follow because vcalert is stupid and alias: f is free --- backend/src/plugins/LocateUser.ts | 44 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/backend/src/plugins/LocateUser.ts b/backend/src/plugins/LocateUser.ts index 8d124fca..a4027409 100644 --- a/backend/src/plugins/LocateUser.ts +++ b/backend/src/plugins/LocateUser.ts @@ -1,7 +1,7 @@ import { decorators as d, IPluginOptions, getInviteLink, logger } from "knub"; import { trimPluginDescription, ZeppelinPlugin, CommandInfo } from "./ZeppelinPlugin"; import humanizeDuration from "humanize-duration"; -import { Message, Member, Guild, TextableChannel, VoiceChannel, Channel, User, Command } from "eris"; +import { Message, Member, Guild, TextableChannel, VoiceChannel, Channel, User } from "eris"; import { GuildVCAlerts } from "../data/GuildVCAlerts"; import moment from "moment-timezone"; import { resolveMember, sorter, createChunkedMessage, MINUTES, SECONDS } from "../utils"; @@ -103,8 +103,8 @@ export class LocatePlugin extends ZeppelinPlugin { sendWhere(this.guild, member, msg.channel, `${msg.member.mention} | `); } - @d.command("vcalert", " [reminder:string$]", { - aliases: ["vca"], + @d.command("follow", " [reminder:string$]", { + aliases: ["f", "vcalert", "vca"], options: [ { name: "duration", @@ -120,20 +120,22 @@ export class LocatePlugin extends ZeppelinPlugin { extra: { info: { description: "Sets up an alert that notifies you any time `` switches or joins voice channels", - basicUsage: "!vca @Dark", + basicUsage: "!f @Dark", examples: trimPluginDescription(` To get an alert for 1 hour: - \`!vca 108552944961454080 -d 1h\` + \`!f 108552944961454080 -d 1h\` To get an alert for 2 hours and 30 minutes with the reminder "Earrape": - \`!vca 108552944961454080 -d 2h30m Earrape\` or \`!vca 108552944961454080 Earrape -d 1h\` + \`!f 108552944961454080 -d 2h30m Earrape\` + Note: The duration must be specified before the reminder, otherwise it will be part of it To get an alert for 3 days and be moved to the channel: - \`!vca 108552944961454080 -d 3d -a\` + \`!f 108552944961454080 -d 3d -a\` + Note: As with the duration, active must be specified before the rminder, otherwise it will be part of it `), optionDescriptions: { duration: "How long the alert shall be active. The alert will be automatically deleted after this time", - active: " A switch that, when true, will move you to the channel the user joined", + active: "A switch that, when true, will move you to the channel the user joined", }, parameterDescriptions: { member: "The server member we want to set as the alerts target", @@ -143,7 +145,7 @@ export class LocatePlugin extends ZeppelinPlugin { }, }) @d.permission("can_alert") - async vcalertCmd(msg: Message, args: { member: Member; reminder?: string; duration?: number; active?: boolean }) { + async followCmd(msg: Message, args: { member: Member; reminder?: string; duration?: number; active?: boolean }) { const time = args.duration || 10 * MINUTES; const alertTime = moment().add(time, "millisecond"); const body = args.reminder || "None"; @@ -154,7 +156,7 @@ export class LocatePlugin extends ZeppelinPlugin { return; } - this.alerts.add( + await this.alerts.add( msg.author.id, args.member.id, msg.channel.id, @@ -183,8 +185,8 @@ export class LocatePlugin extends ZeppelinPlugin { } } - @d.command("vcalerts", [], { - aliases: ["vca"], + @d.command("follows", [], { + aliases: ["fs", "vcalerts", "vca"], extra: { info: { description: "Displays all of your active alerts ordered by expiration time", @@ -192,7 +194,7 @@ export class LocatePlugin extends ZeppelinPlugin { }, }) @d.permission("can_alert") - async listVcalertCmd(msg: Message) { + async listFollowCmd(msg: Message) { const alerts = await this.alerts.getAlertsByRequestorId(msg.member.id); if (alerts.length === 0) { this.sendErrorMessage(msg.channel, "You have no active alerts!"); @@ -204,28 +206,28 @@ export class LocatePlugin extends ZeppelinPlugin { const lines = Array.from(alerts.entries()).map(([i, alert]) => { const num = i + 1; const paddedNum = num.toString().padStart(longestNum, " "); - return `\`${paddedNum}.\` \`${alert.expires_at}\` Target: <@!${alert.user_id}> Reminder: \`${ + return `\`${paddedNum}.\` \`${alert.expires_at}\` **Target:** <@!${alert.user_id}> **Reminder:** \`${ alert.body - }\` Active: ${alert.active.valueOf()}`; + }\` **Active:** ${alert.active.valueOf()}`; }); - createChunkedMessage(msg.channel, lines.join("\n")); + await createChunkedMessage(msg.channel, lines.join("\n")); } - @d.command("vcalerts delete", "", { - aliases: ["vcalerts d", "vca d"], + @d.command("follows delete", "", { + aliases: ["fs d", "vcalerts delete", "vcalerts d", "vca d"], extra: { info: { description: - "Deletes the alert at the position .\nThe value needed for can be found using `!vcalerts`", + "Deletes the alert at the position .\nThe value needed for can be found using `!follows` (`!fs`)", }, }, }) @d.permission("can_alert") - async deleteVcalertCmd(msg: Message, args: { num: number }) { + async deleteFollowCmd(msg: Message, args: { num: number }) { const alerts = await this.alerts.getAlertsByRequestorId(msg.member.id); alerts.sort(sorter("expires_at")); - if (args.num > alerts.length || args.num < 0) { + if (args.num > alerts.length || args.num <= 0) { this.sendErrorMessage(msg.channel, "Unknown alert!"); return; } From d7e9b1f94ddfc33874daf70e5fa53115ee176dbe Mon Sep 17 00:00:00 2001 From: Dark <7890309+DarkView@users.noreply.github.com> Date: Fri, 6 Mar 2020 15:00:18 +0100 Subject: [PATCH 07/10] Fix newline not working and changed from @ to userid in basic usage --- backend/src/plugins/LocateUser.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/plugins/LocateUser.ts b/backend/src/plugins/LocateUser.ts index a4027409..156fd494 100644 --- a/backend/src/plugins/LocateUser.ts +++ b/backend/src/plugins/LocateUser.ts @@ -90,7 +90,7 @@ export class LocatePlugin extends ZeppelinPlugin { extra: { info: { description: "Posts an instant invite to the voice channel that `` is in", - basicUsage: "!where @Dark", + basicUsage: "!w 108552944961454080", parameterDescriptions: { member: "The member that we want to find", }, @@ -120,18 +120,18 @@ export class LocatePlugin extends ZeppelinPlugin { extra: { info: { description: "Sets up an alert that notifies you any time `` switches or joins voice channels", - basicUsage: "!f @Dark", + basicUsage: "!f 108552944961454080", examples: trimPluginDescription(` To get an alert for 1 hour: \`!f 108552944961454080 -d 1h\` To get an alert for 2 hours and 30 minutes with the reminder "Earrape": - \`!f 108552944961454080 -d 2h30m Earrape\` - Note: The duration must be specified before the reminder, otherwise it will be part of it + \`!f 108552944961454080 -d 2h30m Earrape\` + *Note: The duration must be specified before the reminder, otherwise it will be part of it* To get an alert for 3 days and be moved to the channel: - \`!f 108552944961454080 -d 3d -a\` - Note: As with the duration, active must be specified before the rminder, otherwise it will be part of it + \`!f 108552944961454080 -d 3d -a\` + *Note: As with the duration, active must be specified before the rminder, otherwise it will be part of it* `), optionDescriptions: { duration: "How long the alert shall be active. The alert will be automatically deleted after this time", From 80389cccf9d628006e12be0bcf6ceddeb119079d Mon Sep 17 00:00:00 2001 From: Dark <7890309+DarkView@users.noreply.github.com> Date: Fri, 6 Mar 2020 15:03:06 +0100 Subject: [PATCH 08/10] Fix spacing issue for aliases. / was right behind the alias --- dashboard/src/components/docs/Plugin.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/src/components/docs/Plugin.vue b/dashboard/src/components/docs/Plugin.vue index dccbb5a5..431f9146 100644 --- a/dashboard/src/components/docs/Plugin.vue +++ b/dashboard/src/components/docs/Plugin.vue @@ -38,7 +38,7 @@ v-bind:ref="getCommandSlug(command.trigger)" v-bind:class="{target: targetCommandId === getCommandSlug(command.trigger)}">

!{{ command.trigger }} - / !{{ alias }} + / !{{ alias }}

Date: Tue, 10 Mar 2020 00:49:41 +0100 Subject: [PATCH 09/10] Enable checking for emoji, asset text and details when using -ss --- backend/src/plugins/Utility.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/src/plugins/Utility.ts b/backend/src/plugins/Utility.ts index ffd085a5..bbcd7d6e 100644 --- a/backend/src/plugins/Utility.ts +++ b/backend/src/plugins/Utility.ts @@ -387,6 +387,13 @@ export class UtilityPlugin extends ZeppelinPlugin { if (member.game) { if (member.game.name && member.game.name.match(queryRegex)) return true; if (member.game.state && member.game.state.match(queryRegex)) return true; + if (member.game.details && member.game.details.match(queryRegex)) return true; + if ( + member.game.assets && + (member.game.assets.small_text.match(queryRegex) || member.game.assets.large_text.match(queryRegex)) + ) + return true; + if (member.game.emoji && member.game.emoji.name.match(queryRegex)) return true; } return false; }); From 76e371854f2bdf3df18bc6feae76505a9440e6a6 Mon Sep 17 00:00:00 2001 From: roflmaoqwerty <36663568+roflmaoqwerty@users.noreply.github.com> Date: Thu, 20 Feb 2020 23:30:52 +1100 Subject: [PATCH 10/10] moved new emoji to beginning of join log --- backend/src/data/DefaultLogMessages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/data/DefaultLogMessages.json b/backend/src/data/DefaultLogMessages.json index e36e5e53..5633c8e3 100644 --- a/backend/src/data/DefaultLogMessages.json +++ b/backend/src/data/DefaultLogMessages.json @@ -10,7 +10,7 @@ "MEMBER_UNBAN": "🔓 User (`{userId}`) was unbanned by {userMention(mod)}", "MEMBER_FORCEBAN": "🔨 User (`{userId}`) was forcebanned by {userMention(mod)}", "MEMBER_SOFTBAN": "🔨 {userMention(member)} was softbanned by {userMention(mod)}", - "MEMBER_JOIN": "📥 {userMention(member)} joined{new} (created {account_age} ago)", + "MEMBER_JOIN": "📥 {new} {userMention(member)} joined (created {account_age} ago)", "MEMBER_LEAVE": "📤 {userMention(member)} left the server", "MEMBER_ROLE_ADD": "🔑 {userMention(member)}: role(s) **{roles}** added by {userMention(mod)}", "MEMBER_ROLE_REMOVE": "🔑 {userMention(member)}: role(s) **{roles}** removed by {userMention(mod)}",