mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-16 14:11:50 +00:00
Merge branch 'master' of github.com:Dragory/ZeppelinBot
This commit is contained in:
commit
53daff498b
11 changed files with 286 additions and 37 deletions
|
@ -10,7 +10,7 @@
|
||||||
"MEMBER_UNBAN": "🔓 User (`{userId}`) was unbanned by {userMention(mod)}",
|
"MEMBER_UNBAN": "🔓 User (`{userId}`) was unbanned by {userMention(mod)}",
|
||||||
"MEMBER_FORCEBAN": "🔨 User (`{userId}`) was forcebanned by {userMention(mod)}",
|
"MEMBER_FORCEBAN": "🔨 User (`{userId}`) was forcebanned by {userMention(mod)}",
|
||||||
"MEMBER_SOFTBAN": "🔨 {userMention(member)} was softbanned 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_LEAVE": "📤 {userMention(member)} left the server",
|
||||||
"MEMBER_ROLE_ADD": "🔑 {userMention(member)}: role(s) **{roles}** added by {userMention(mod)}",
|
"MEMBER_ROLE_ADD": "🔑 {userMention(member)}: role(s) **{roles}** added by {userMention(mod)}",
|
||||||
"MEMBER_ROLE_REMOVE": "🔑 {userMention(member)}: role(s) **{roles}** removed by {userMention(mod)}",
|
"MEMBER_ROLE_REMOVE": "🔑 {userMention(member)}: role(s) **{roles}** removed by {userMention(mod)}",
|
||||||
|
|
|
@ -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({
|
await this.allAlerts.insert({
|
||||||
guild_id: this.guildId,
|
guild_id: this.guildId,
|
||||||
requestor_id: requestorId,
|
requestor_id: requestorId,
|
||||||
|
@ -58,6 +58,7 @@ export class GuildVCAlerts extends BaseGuildRepository {
|
||||||
channel_id: channelId,
|
channel_id: channelId,
|
||||||
expires_at: expiresAt,
|
expires_at: expiresAt,
|
||||||
body,
|
body,
|
||||||
|
active,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,4 +17,6 @@ export class VCAlert {
|
||||||
@Column() expires_at: string;
|
@Column() expires_at: string;
|
||||||
|
|
||||||
@Column() body: string;
|
@Column() body: string;
|
||||||
|
|
||||||
|
@Column() active: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
|
||||||
|
|
||||||
|
export class AddActiveFollowsToLocateUser1580654617890 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.addColumn(
|
||||||
|
"vc_alerts",
|
||||||
|
new TableColumn({
|
||||||
|
name: "active",
|
||||||
|
type: "boolean",
|
||||||
|
isNullable: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.dropColumn("vc_alerts", "active");
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,10 +60,12 @@ import {
|
||||||
TMatchWordsTrigger,
|
TMatchWordsTrigger,
|
||||||
TMemberJoinTrigger,
|
TMemberJoinTrigger,
|
||||||
TRule,
|
TRule,
|
||||||
|
TMatchAttachmentTypeTrigger,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { pluginInfo } from "./info";
|
import { pluginInfo } from "./info";
|
||||||
import { ERRORS, RecoverablePluginError } from "../../RecoverablePluginError";
|
import { ERRORS, RecoverablePluginError } from "../../RecoverablePluginError";
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
import { StrictValidationError } from "src/validatorUtils";
|
||||||
|
|
||||||
const unactioned = (action: TextRecentAction | OtherRecentAction) => !action.actioned;
|
const unactioned = (action: TextRecentAction | OtherRecentAction) => !action.actioned;
|
||||||
|
|
||||||
|
@ -116,6 +118,19 @@ const defaultMatchLinksTrigger: Partial<TMatchLinksTrigger> = {
|
||||||
match_custom_status: false,
|
match_custom_status: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultMatchAttachmentTypeTrigger: Partial<TMatchAttachmentTypeTrigger> = {
|
||||||
|
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<t.TypeOf<typeof BaseTextSpamTrigger>> = {
|
const defaultTextSpamTrigger: Partial<t.TypeOf<typeof BaseTextSpamTrigger>> = {
|
||||||
per_channel: true,
|
per_channel: true,
|
||||||
};
|
};
|
||||||
|
@ -130,6 +145,7 @@ const defaultTriggers = {
|
||||||
match_regex: defaultMatchRegexTrigger,
|
match_regex: defaultMatchRegexTrigger,
|
||||||
match_invites: defaultMatchInvitesTrigger,
|
match_invites: defaultMatchInvitesTrigger,
|
||||||
match_links: defaultMatchLinksTrigger,
|
match_links: defaultMatchLinksTrigger,
|
||||||
|
match_attachment_type: defaultMatchAttachmentTypeTrigger,
|
||||||
message_spam: defaultTextSpamTrigger,
|
message_spam: defaultTextSpamTrigger,
|
||||||
mention_spam: defaultTextSpamTrigger,
|
mention_spam: defaultTextSpamTrigger,
|
||||||
link_spam: defaultTextSpamTrigger,
|
link_spam: defaultTextSpamTrigger,
|
||||||
|
@ -249,6 +265,21 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema, ICustomOverride
|
||||||
trigger[defaultTriggerName] = configUtils.mergeConfig({}, defaultTrigger, trigger[defaultTriggerName]);
|
trigger[defaultTriggerName] = configUtils.mergeConfig({}, defaultTrigger, trigger[defaultTriggerName]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trigger.match_attachment_type) {
|
||||||
|
const white = trigger.match_attachment_type.whitelist_enabled;
|
||||||
|
const black = trigger.match_attachment_type.blacklist_enabled;
|
||||||
|
|
||||||
|
if (white && black) {
|
||||||
|
throw new StrictValidationError([
|
||||||
|
`Cannot have both blacklist and whitelist enabled at rule <${rule.name}/match_attachment_type>`,
|
||||||
|
]);
|
||||||
|
} 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<TConfigSchema, ICustomOverride
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected evaluateMatchAttachmentTypeTrigger(trigger: TMatchAttachmentTypeTrigger, msg: SavedMessage): null | string {
|
||||||
|
if (!msg.data.attachments) return null;
|
||||||
|
const attachments: any[] = msg.data.attachments;
|
||||||
|
|
||||||
|
for (const attachment of attachments) {
|
||||||
|
const attachment_type = attachment.filename.split(`.`).pop();
|
||||||
|
if (trigger.blacklist_enabled && trigger.filetype_blacklist.includes(attachment_type)) {
|
||||||
|
return `${attachment_type} - blacklisted`;
|
||||||
|
}
|
||||||
|
if (trigger.whitelist_enabled && !trigger.filetype_whitelist.includes(attachment_type)) {
|
||||||
|
return `${attachment_type} - not whitelisted`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected matchTextSpamTrigger(
|
protected matchTextSpamTrigger(
|
||||||
recentActionType: RecentActionType,
|
recentActionType: RecentActionType,
|
||||||
trigger: TBaseTextSpamTrigger,
|
trigger: TBaseTextSpamTrigger,
|
||||||
|
@ -626,6 +674,13 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema, ICustomOverride
|
||||||
if (match) return { ...match, trigger: "match_links" } as TextTriggerMatchResult;
|
if (match) return { ...match, trigger: "match_links" } as TextTriggerMatchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trigger.match_attachment_type) {
|
||||||
|
const match = await this.matchMultipleTextTypesOnMessage(trigger.match_attachment_type, msg, str => {
|
||||||
|
return this.evaluateMatchAttachmentTypeTrigger(trigger.match_attachment_type, msg);
|
||||||
|
});
|
||||||
|
if (match) return { ...match, trigger: "match_attachment_type" } as TextTriggerMatchResult;
|
||||||
|
}
|
||||||
|
|
||||||
if (trigger.message_spam) {
|
if (trigger.message_spam) {
|
||||||
const match = this.matchTextSpamTrigger(RecentActionType.Message, trigger.message_spam, msg);
|
const match = this.matchTextSpamTrigger(RecentActionType.Message, trigger.message_spam, msg);
|
||||||
if (match) return { ...match, rule, trigger: "message_spam" };
|
if (match) return { ...match, rule, trigger: "message_spam" };
|
||||||
|
@ -1319,6 +1374,8 @@ export class AutomodPlugin extends ZeppelinPlugin<TConfigSchema, ICustomOverride
|
||||||
return `invite code \`${disableInlineCode(matchResult.matchedValue)}\``;
|
return `invite code \`${disableInlineCode(matchResult.matchedValue)}\``;
|
||||||
} else if (matchResult.trigger === "match_links") {
|
} else if (matchResult.trigger === "match_links") {
|
||||||
return `link \`${disableInlineCode(matchResult.matchedValue)}\``;
|
return `link \`${disableInlineCode(matchResult.matchedValue)}\``;
|
||||||
|
} else if (matchResult.trigger === "match_attachment_type") {
|
||||||
|
return `attachment type \`${disableInlineCode(matchResult.matchedValue)}\``;
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeof matchResult.matchedValue === "string" ? `\`${disableInlineCode(matchResult.matchedValue)}\`` : null;
|
return typeof matchResult.matchedValue === "string" ? `\`${disableInlineCode(matchResult.matchedValue)}\`` : null;
|
||||||
|
|
|
@ -179,6 +179,20 @@ export const MatchLinksTrigger = t.type({
|
||||||
});
|
});
|
||||||
export type TMatchLinksTrigger = t.TypeOf<typeof MatchLinksTrigger>;
|
export type TMatchLinksTrigger = t.TypeOf<typeof MatchLinksTrigger>;
|
||||||
|
|
||||||
|
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<typeof MatchAttachmentTypeTrigger>;
|
||||||
|
|
||||||
export const BaseSpamTrigger = t.type({
|
export const BaseSpamTrigger = t.type({
|
||||||
amount: t.number,
|
amount: t.number,
|
||||||
within: t.string,
|
within: t.string,
|
||||||
|
@ -280,6 +294,7 @@ export const Rule = t.type({
|
||||||
match_regex: tNullable(MatchRegexTrigger),
|
match_regex: tNullable(MatchRegexTrigger),
|
||||||
match_invites: tNullable(MatchInvitesTrigger),
|
match_invites: tNullable(MatchInvitesTrigger),
|
||||||
match_links: tNullable(MatchLinksTrigger),
|
match_links: tNullable(MatchLinksTrigger),
|
||||||
|
match_attachment_type: tNullable(MatchAttachmentTypeTrigger),
|
||||||
message_spam: tNullable(MessageSpamTrigger),
|
message_spam: tNullable(MessageSpamTrigger),
|
||||||
mention_spam: tNullable(MentionSpamTrigger),
|
mention_spam: tNullable(MentionSpamTrigger),
|
||||||
link_spam: tNullable(LinkSpamTrigger),
|
link_spam: tNullable(LinkSpamTrigger),
|
||||||
|
|
|
@ -245,6 +245,21 @@ export class CasesPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
return { embed };
|
return { embed };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getCaseTypeAmountForUserId(userID: string, type: CaseTypes): Promise<number> {
|
||||||
|
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.
|
* A helper for posting to the case log channel.
|
||||||
* Returns silently if the case log channel isn't specified or is invalid.
|
* Returns silently if the case log channel isn't specified or is invalid.
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { decorators as d, IPluginOptions, getInviteLink, logger } from "knub";
|
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 humanizeDuration from "humanize-duration";
|
||||||
import { Message, Member, Guild, TextableChannel, VoiceChannel, Channel, User } from "eris";
|
import { Message, Member, Guild, TextableChannel, VoiceChannel, Channel, User } from "eris";
|
||||||
import { GuildVCAlerts } from "../data/GuildVCAlerts";
|
import { GuildVCAlerts } from "../data/GuildVCAlerts";
|
||||||
import moment from "moment-timezone";
|
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";
|
import * as t from "io-ts";
|
||||||
|
|
||||||
const ConfigSchema = t.type({
|
const ConfigSchema = t.type({
|
||||||
|
@ -13,7 +13,7 @@ const ConfigSchema = t.type({
|
||||||
});
|
});
|
||||||
type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||||
|
|
||||||
const ALERT_LOOP_TIME = 30 * 1000;
|
const ALERT_LOOP_TIME = 30 * SECONDS;
|
||||||
|
|
||||||
export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
public static pluginName = "locate_user";
|
public static pluginName = "locate_user";
|
||||||
|
@ -29,8 +29,9 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private alerts: GuildVCAlerts;
|
private alerts: GuildVCAlerts;
|
||||||
private outdatedAlertsTimeout;
|
private outdatedAlertsTimeout: NodeJS.Timeout;
|
||||||
private usersWithAlerts: string[] = [];
|
private usersWithAlerts: string[] = [];
|
||||||
|
private unloaded = false;
|
||||||
|
|
||||||
public static getStaticDefaultOptions(): IPluginOptions<TConfigSchema> {
|
public static getStaticDefaultOptions(): IPluginOptions<TConfigSchema> {
|
||||||
return {
|
return {
|
||||||
|
@ -56,6 +57,11 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
this.fillActiveAlertsList();
|
this.fillActiveAlertsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onUnload() {
|
||||||
|
clearTimeout(this.outdatedAlertsTimeout);
|
||||||
|
this.unloaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
async outdatedAlertsLoop() {
|
async outdatedAlertsLoop() {
|
||||||
const outdatedAlerts = await this.alerts.getOutdatedAlerts();
|
const outdatedAlerts = await this.alerts.getOutdatedAlerts();
|
||||||
|
|
||||||
|
@ -64,7 +70,9 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
await this.removeUserIdFromActiveAlerts(alert.user_id);
|
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() {
|
async fillActiveAlertsList() {
|
||||||
|
@ -80,45 +88,113 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
@d.command("where", "<member:resolvedMember>", {
|
@d.command("where", "<member:resolvedMember>", {
|
||||||
aliases: ["w"],
|
aliases: ["w"],
|
||||||
extra: {
|
extra: {
|
||||||
info: {
|
info: <CommandInfo>{
|
||||||
description: "Posts an instant invite to the voice channel that `<member>` is in",
|
description: "Posts an instant invite to the voice channel that `<member>` is in",
|
||||||
|
basicUsage: "!w 108552944961454080",
|
||||||
|
parameterDescriptions: {
|
||||||
|
member: "The member that we want to find",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@d.permission("can_where")
|
@d.permission("can_where")
|
||||||
async whereCmd(msg: Message, args: { member: Member }) {
|
async whereCmd(msg: Message, args: { member: Member }) {
|
||||||
const member = await resolveMember(this.bot, this.guild, args.member.id);
|
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", "<member:resolvedMember> <duration:delay> <reminder:string$>", {
|
@d.command("follow", "<member:resolvedMember> [reminder:string$]", {
|
||||||
overloads: ["<member:resolvedMember> <duration:delay>", "<member:resolvedMember>"],
|
aliases: ["f", "vcalert", "vca"],
|
||||||
aliases: ["vca"],
|
options: [
|
||||||
|
{
|
||||||
|
name: "duration",
|
||||||
|
shortcut: "d",
|
||||||
|
type: "delay",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "active",
|
||||||
|
shortcut: "a",
|
||||||
|
isSwitch: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
extra: {
|
extra: {
|
||||||
info: {
|
info: <CommandInfo>{
|
||||||
description: "Sets up an alert that notifies you any time `<member>` switches or joins voice channels",
|
description: "Sets up an alert that notifies you any time `<member>` switches or joins voice channels",
|
||||||
|
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*
|
||||||
|
|
||||||
|
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*
|
||||||
|
`),
|
||||||
|
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")
|
@d.permission("can_alert")
|
||||||
async vcalertCmd(msg: Message, args: { member: Member; duration?: number; reminder?: string }) {
|
async followCmd(msg: Message, args: { member: Member; reminder?: string; duration?: number; active?: boolean }) {
|
||||||
const time = args.duration || 10 * MINUTES;
|
const time = args.duration || 10 * MINUTES;
|
||||||
const alertTime = moment().add(time, "millisecond");
|
const alertTime = moment().add(time, "millisecond");
|
||||||
const body = args.reminder || "None";
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
await 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)) {
|
if (!this.usersWithAlerts.includes(args.member.id)) {
|
||||||
this.usersWithAlerts.push(args.member.id);
|
this.usersWithAlerts.push(args.member.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.channel.createMessage(
|
if (active) {
|
||||||
`If ${args.member.mention} joins or switches VC in the next ${humanizeDuration(time)} i will notify you`,
|
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("follows", [], {
|
||||||
|
aliases: ["fs", "vcalerts", "vca"],
|
||||||
|
extra: {
|
||||||
|
info: <CommandInfo>{
|
||||||
|
description: "Displays all of your active alerts ordered by expiration time",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@d.permission("can_alert")
|
@d.permission("can_alert")
|
||||||
async listVcalertCmd(msg: Message) {
|
async listFollowCmd(msg: Message) {
|
||||||
const alerts = await this.alerts.getAlertsByRequestorId(msg.member.id);
|
const alerts = await this.alerts.getAlertsByRequestorId(msg.member.id);
|
||||||
if (alerts.length === 0) {
|
if (alerts.length === 0) {
|
||||||
this.sendErrorMessage(msg.channel, "You have no active alerts!");
|
this.sendErrorMessage(msg.channel, "You have no active alerts!");
|
||||||
|
@ -130,22 +206,29 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
const lines = Array.from(alerts.entries()).map(([i, alert]) => {
|
const lines = Array.from(alerts.entries()).map(([i, alert]) => {
|
||||||
const num = i + 1;
|
const num = i + 1;
|
||||||
const paddedNum = num.toString().padStart(longestNum, " ");
|
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"));
|
await createChunkedMessage(msg.channel, lines.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@d.command("vcalerts delete", "<num:number>", {
|
@d.command("follows delete", "<num:number>", {
|
||||||
aliases: ["vcalerts d"],
|
aliases: ["fs d", "vcalerts delete", "vcalerts d", "vca d"],
|
||||||
|
extra: {
|
||||||
|
info: <CommandInfo>{
|
||||||
|
description:
|
||||||
|
"Deletes the alert at the position <num>.\nThe value needed for <num> can be found using `!follows` (`!fs`)",
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
@d.permission("can_alert")
|
@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);
|
const alerts = await this.alerts.getAlertsByRequestorId(msg.member.id);
|
||||||
alerts.sort(sorter("expires_at"));
|
alerts.sort(sorter("expires_at"));
|
||||||
const lastNum = alerts.length + 1;
|
|
||||||
|
|
||||||
if (args.num > lastNum || args.num < 0) {
|
if (args.num > alerts.length || args.num <= 0) {
|
||||||
msg.channel.createMessage(errorMessage("Unknown alert"));
|
this.sendErrorMessage(msg.channel, "Unknown alert!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +242,6 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
async userJoinedVC(member: Member, channel: Channel) {
|
async userJoinedVC(member: Member, channel: Channel) {
|
||||||
if (this.usersWithAlerts.includes(member.id)) {
|
if (this.usersWithAlerts.includes(member.id)) {
|
||||||
this.sendAlerts(member.id);
|
this.sendAlerts(member.id);
|
||||||
await this.removeUserIdFromActiveAlerts(member.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,10 +249,22 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
async userSwitchedVC(member: Member, newChannel: Channel, oldChannel: Channel) {
|
async userSwitchedVC(member: Member, newChannel: Channel, oldChannel: Channel) {
|
||||||
if (this.usersWithAlerts.includes(member.id)) {
|
if (this.usersWithAlerts.includes(member.id)) {
|
||||||
this.sendAlerts(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")
|
@d.event("guildBanAdd")
|
||||||
async onGuildBanAdd(_, user: User) {
|
async onGuildBanAdd(_, user: User) {
|
||||||
const alerts = await this.alerts.getAlertsByUserId(user.id);
|
const alerts = await this.alerts.getAlertsByUserId(user.id);
|
||||||
|
@ -185,8 +279,11 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
|
|
||||||
triggeredAlerts.forEach(alert => {
|
triggeredAlerts.forEach(alert => {
|
||||||
const prepend = `<@!${alert.requestor_id}>, an alert requested by you has triggered!\nReminder: \`${alert.body}\`\n`;
|
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);
|
const txtChannel = this.bot.getChannel(alert.channel_id) as TextableChannel;
|
||||||
this.alerts.delete(alert.id);
|
sendWhere(this.guild, member, txtChannel, prepend);
|
||||||
|
if (alert.active) {
|
||||||
|
this.moveMember(alert.requestor_id, member, txtChannel);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,6 +293,22 @@ export class LocatePlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
this.usersWithAlerts.splice(index, 1);
|
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) {
|
export async function sendWhere(guild: Guild, member: Member, channel: TextableChannel, prepend: string) {
|
||||||
|
@ -212,7 +325,7 @@ export async function sendWhere(guild: Guild, member: Member, channel: TextableC
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
channel.createMessage(
|
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)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ const ConfigSchema = t.type({
|
||||||
ban_message: tNullable(t.string),
|
ban_message: tNullable(t.string),
|
||||||
alert_on_rejoin: t.boolean,
|
alert_on_rejoin: t.boolean,
|
||||||
alert_channel: tNullable(t.string),
|
alert_channel: tNullable(t.string),
|
||||||
|
warn_notify_threshold: t.number,
|
||||||
|
warn_notify_message: t.string,
|
||||||
can_note: t.boolean,
|
can_note: t.boolean,
|
||||||
can_warn: t.boolean,
|
can_warn: t.boolean,
|
||||||
can_mute: t.boolean,
|
can_mute: t.boolean,
|
||||||
|
@ -162,6 +164,9 @@ export class ModActionsPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
ban_message: "You have been banned from the {guildName} server. Reason given: {reason}",
|
ban_message: "You have been banned from the {guildName} server. Reason given: {reason}",
|
||||||
alert_on_rejoin: false,
|
alert_on_rejoin: false,
|
||||||
alert_channel: null,
|
alert_channel: null,
|
||||||
|
warn_notify_threshold: 5,
|
||||||
|
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_note: false,
|
||||||
can_warn: false,
|
can_warn: false,
|
||||||
|
@ -671,6 +676,21 @@ export class ModActionsPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
const config = this.getConfig();
|
const config = this.getConfig();
|
||||||
const reason = this.formatReasonWithAttachments(args.reason, msg.attachments);
|
const reason = this.formatReasonWithAttachments(args.reason, msg.attachments);
|
||||||
|
|
||||||
|
const casesPlugin = this.getPlugin<CasesPlugin>("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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let contactMethods;
|
let contactMethods;
|
||||||
try {
|
try {
|
||||||
contactMethods = this.readContactMethodsFromArgs(args);
|
contactMethods = this.readContactMethodsFromArgs(args);
|
||||||
|
|
|
@ -387,6 +387,13 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
if (member.game) {
|
if (member.game) {
|
||||||
if (member.game.name && member.game.name.match(queryRegex)) return true;
|
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.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;
|
return false;
|
||||||
});
|
});
|
||||||
|
@ -1539,6 +1546,7 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
async jumboCmd(msg: Message, args: { emoji: string }) {
|
async jumboCmd(msg: Message, args: { emoji: string }) {
|
||||||
// Get emoji url
|
// Get emoji url
|
||||||
const config = this.getConfig();
|
const config = this.getConfig();
|
||||||
|
const size = config.jumbo_size > 2048 ? 2048 : config.jumbo_size;
|
||||||
const emojiRegex = new RegExp(`(<.*:).*:(\\d+)`);
|
const emojiRegex = new RegExp(`(<.*:).*:(\\d+)`);
|
||||||
const results = emojiRegex.exec(args.emoji);
|
const results = emojiRegex.exec(args.emoji);
|
||||||
let extention = ".png";
|
let extention = ".png";
|
||||||
|
@ -1551,7 +1559,7 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
}
|
}
|
||||||
url += `${results[2]}${extention}`;
|
url += `${results[2]}${extention}`;
|
||||||
if (extention === ".png") {
|
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 = {
|
file = {
|
||||||
name: `emoji${extention}`,
|
name: `emoji${extention}`,
|
||||||
file: image,
|
file: image,
|
||||||
|
@ -1567,11 +1575,11 @@ export class UtilityPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||||
let url = CDN_URL + `/${twemoji.convert.toCodePoint(args.emoji)}.svg`;
|
let url = CDN_URL + `/${twemoji.convert.toCodePoint(args.emoji)}.svg`;
|
||||||
let image;
|
let image;
|
||||||
try {
|
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 {
|
} catch {
|
||||||
if (url.toLocaleLowerCase().endsWith("fe0f.svg")) {
|
if (url.toLocaleLowerCase().endsWith("fe0f.svg")) {
|
||||||
url = url.slice(0, url.lastIndexOf("-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 = {
|
file = {
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
v-bind:ref="getCommandSlug(command.trigger)" v-bind:class="{target: targetCommandId === getCommandSlug(command.trigger)}">
|
v-bind:ref="getCommandSlug(command.trigger)" v-bind:class="{target: targetCommandId === getCommandSlug(command.trigger)}">
|
||||||
<h3 class="text-xl mb-0">
|
<h3 class="text-xl mb-0">
|
||||||
!{{ command.trigger }}
|
!{{ command.trigger }}
|
||||||
<span v-for="alias in command.config.aliases"> <span class="text-gray-600">/</span> !{{ alias }}</span>
|
<span v-for="alias in command.config.aliases"> <span class="text-gray-600">/</span> !{{ alias }} </span>
|
||||||
</h3>
|
</h3>
|
||||||
<MarkdownBlock v-if="command.config.extra.info && command.config.extra.info.description"
|
<MarkdownBlock v-if="command.config.extra.info && command.config.extra.info.description"
|
||||||
:content="command.config.extra.info.description"
|
:content="command.config.extra.info.description"
|
||||||
|
|
Loading…
Add table
Reference in a new issue