3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-03-15 05:41:51 +00:00

Turn on strict TS compilation. Fix up and tweak types accordingly.

This commit is contained in:
Dragory 2020-11-09 20:03:57 +02:00
parent 690955a399
commit 629002b8d9
No known key found for this signature in database
GPG key ID: 5F387BA66DF8AAC1
172 changed files with 720 additions and 534 deletions

View file

@ -34,7 +34,7 @@ export class Queue {
return; return;
} }
const fn = this.queue.shift(); const fn = this.queue.shift()!;
new Promise(resolve => { new Promise(resolve => {
// Either fn() completes or the timeout is reached // Either fn() completes or the timeout is reached
fn().then(resolve); fn().then(resolve);

View file

@ -16,7 +16,7 @@ export class QueuedEventEmitter {
this.listeners.set(eventName, []); this.listeners.set(eventName, []);
} }
this.listeners.get(eventName).push(listener); this.listeners.get(eventName)!.push(listener);
return listener; return listener;
} }
@ -25,7 +25,7 @@ export class QueuedEventEmitter {
return; return;
} }
const listeners = this.listeners.get(eventName); const listeners = this.listeners.get(eventName)!;
listeners.splice(listeners.indexOf(listener), 1); listeners.splice(listeners.indexOf(listener), 1);
} }

View file

@ -47,8 +47,8 @@ export interface RegExpRunner {
* Repeatedly failing regexes are put on a cooldown where requests to execute them are ignored. * Repeatedly failing regexes are put on a cooldown where requests to execute them are ignored.
*/ */
export class RegExpRunner extends EventEmitter { export class RegExpRunner extends EventEmitter {
private _worker: RegExpWorker; private _worker: RegExpWorker | null;
private _failedTimesInterval: Timeout; private readonly _failedTimesInterval: Timeout;
private cooldown: CooldownManager; private cooldown: CooldownManager;
private failedTimes: Map<string, number>; private failedTimes: Map<string, number>;
@ -90,13 +90,13 @@ export class RegExpRunner extends EventEmitter {
if (isTimeoutError(e)) { if (isTimeoutError(e)) {
if (this.failedTimes.has(regex.source)) { if (this.failedTimes.has(regex.source)) {
// Regex has failed before, increment fail counter // Regex has failed before, increment fail counter
this.failedTimes.set(regex.source, this.failedTimes.get(regex.source) + 1); this.failedTimes.set(regex.source, this.failedTimes.get(regex.source)! + 1);
} else { } else {
// This is the first time this regex failed, init fail counter // This is the first time this regex failed, init fail counter
this.failedTimes.set(regex.source, 1); this.failedTimes.set(regex.source, 1);
} }
if (this.failedTimes.get(regex.source) >= REGEX_FAIL_TO_COOLDOWN_COUNT) { if (this.failedTimes.has(regex.source) && this.failedTimes.get(regex.source)! >= REGEX_FAIL_TO_COOLDOWN_COUNT) {
// Regex has failed too many times, set it on cooldown // Regex has failed too many times, set it on cooldown
this.cooldown.setCooldown(regex.source, REGEX_FAIL_COOLDOWN); this.cooldown.setCooldown(regex.source, REGEX_FAIL_COOLDOWN);
this.failedTimes.delete(regex.source); this.failedTimes.delete(regex.source);

View file

@ -13,7 +13,10 @@ export class SimpleCache<T = any> {
constructor(retentionTime: number, maxItems?: number) { constructor(retentionTime: number, maxItems?: number) {
this.retentionTime = retentionTime; this.retentionTime = retentionTime;
this.maxItems = maxItems;
if (maxItems) {
this.maxItems = maxItems;
}
this.store = new Map(); this.store = new Map();
} }
@ -48,7 +51,7 @@ export class SimpleCache<T = any> {
} }
} }
get(key: string): T { get(key: string): T | null {
const info = this.store.get(key); const info = this.store.get(key);
if (!info) return null; if (!info) return null;

View file

@ -147,7 +147,7 @@ export function initAuth(app: express.Express) {
res.json({ valid: true }); res.json({ valid: true });
}); });
app.post("/auth/logout", ...apiTokenAuthHandlers(), async (req: Request, res: Response) => { app.post("/auth/logout", ...apiTokenAuthHandlers(), async (req: Request, res: Response) => {
await apiLogins.expireApiKey(req.user.apiKey); await apiLogins.expireApiKey(req.user!.apiKey);
return ok(res); return ok(res);
}); });
} }

View file

@ -19,12 +19,12 @@ export function initGuildsAPI(app: express.Express) {
guildRouter.use(...apiTokenAuthHandlers()); guildRouter.use(...apiTokenAuthHandlers());
guildRouter.get("/available", async (req: Request, res: Response) => { guildRouter.get("/available", async (req: Request, res: Response) => {
const guilds = await allowedGuilds.getForApiUser(req.user.userId); const guilds = await allowedGuilds.getForApiUser(req.user!.userId);
res.json(guilds); res.json(guilds);
}); });
guildRouter.get("/:guildId", async (req: Request, res: Response) => { guildRouter.get("/:guildId", async (req: Request, res: Response) => {
if (!(await hasGuildPermission(req.user.userId, req.params.guildId, ApiPermissions.ViewGuild))) { if (!(await hasGuildPermission(req.user!.userId, req.params.guildId, ApiPermissions.ViewGuild))) {
return unauthorized(res); return unauthorized(res);
} }
@ -34,7 +34,7 @@ export function initGuildsAPI(app: express.Express) {
guildRouter.post("/:guildId/check-permission", async (req: Request, res: Response) => { guildRouter.post("/:guildId/check-permission", async (req: Request, res: Response) => {
const permission = req.body.permission; const permission = req.body.permission;
const hasPermission = await hasGuildPermission(req.user.userId, req.params.guildId, permission); const hasPermission = await hasGuildPermission(req.user!.userId, req.params.guildId, permission);
res.json({ result: hasPermission }); res.json({ result: hasPermission });
}); });
@ -54,7 +54,7 @@ export function initGuildsAPI(app: express.Express) {
config = config.trim() + "\n"; // Normalize start/end whitespace in the config config = config.trim() + "\n"; // Normalize start/end whitespace in the config
const currentConfig = await configs.getActiveByKey(`guild-${req.params.guildId}`); const currentConfig = await configs.getActiveByKey(`guild-${req.params.guildId}`);
if (config === currentConfig.config) { if (currentConfig && config === currentConfig.config) {
return ok(res); return ok(res);
} }
@ -81,7 +81,7 @@ export function initGuildsAPI(app: express.Express) {
return res.status(422).json({ errors: [error] }); return res.status(422).json({ errors: [error] });
} }
await configs.saveNewRevision(`guild-${req.params.guildId}`, config, req.user.userId); await configs.saveNewRevision(`guild-${req.params.guildId}`, config, req.user!.userId);
ok(res); ok(res);
}); });

View file

@ -24,7 +24,7 @@ export const hasGuildPermission = async (userId: string, guildId: string, permis
*/ */
export function requireGuildPermission(permission: ApiPermissions) { export function requireGuildPermission(permission: ApiPermissions) {
return async (req: Request, res: Response, next) => { return async (req: Request, res: Response, next) => {
if (!(await hasGuildPermission(req.user.userId, req.params.guildId, permission))) { if (!(await hasGuildPermission(req.user!.userId, req.params.guildId, permission))) {
return unauthorized(res); return unauthorized(res);
} }

View file

@ -7,7 +7,7 @@ export function initStaff(app: express.Express) {
staffRouter.use(...apiTokenAuthHandlers()); staffRouter.use(...apiTokenAuthHandlers());
staffRouter.get("/status", (req: Request, res: Response) => { staffRouter.get("/status", (req: Request, res: Response) => {
const userIsStaff = isStaff(req.user.userId); const userIsStaff = isStaff(req.user!.userId);
res.json({ isStaff: userIsStaff }); res.json({ isStaff: userIsStaff });
}); });

View file

@ -48,7 +48,9 @@ export const commandTypes = {
}, },
async resolvedMember(value, context: CommandContext<any>) { async resolvedMember(value, context: CommandContext<any>) {
if (!(context.message.channel instanceof GuildChannel)) return null; if (!(context.message.channel instanceof GuildChannel)) {
throw new TypeConversionError(`Cannot resolve member for non-guild channels`);
}
const result = await resolveMember(context.pluginData.client, context.message.channel.guild, value); const result = await resolveMember(context.pluginData.client, context.message.channel.guild, value);
if (result == null) { if (result == null) {
@ -110,7 +112,7 @@ export const commandTypeHelpers = {
delay: createTypeHelper<number>(commandTypes.delay), delay: createTypeHelper<number>(commandTypes.delay),
resolvedUser: createTypeHelper<Promise<User>>(commandTypes.resolvedUser), resolvedUser: createTypeHelper<Promise<User>>(commandTypes.resolvedUser),
resolvedUserLoose: createTypeHelper<Promise<User | UnknownUser>>(commandTypes.resolvedUserLoose), resolvedUserLoose: createTypeHelper<Promise<User | UnknownUser>>(commandTypes.resolvedUserLoose),
resolvedMember: createTypeHelper<Promise<Member | null>>(commandTypes.resolvedMember), resolvedMember: createTypeHelper<Promise<Member>>(commandTypes.resolvedMember),
messageTarget: createTypeHelper<Promise<MessageTarget>>(commandTypes.messageTarget), messageTarget: createTypeHelper<Promise<MessageTarget>>(commandTypes.messageTarget),
anyId: createTypeHelper<Promise<string>>(commandTypes.anyId), anyId: createTypeHelper<Promise<string>>(commandTypes.anyId),
regex: createTypeHelper<RegExp>(commandTypes.regex), regex: createTypeHelper<RegExp>(commandTypes.regex),

View file

@ -34,10 +34,10 @@ export async function validateGuildConfig(config: any): Promise<string | null> {
return `Invalid options specified for plugin ${pluginName}`; return `Invalid options specified for plugin ${pluginName}`;
} }
const plugin = pluginNameToPlugin.get(pluginName); const plugin = pluginNameToPlugin.get(pluginName)!;
try { try {
const mergedOptions = configUtils.mergeConfig(plugin.defaultOptions || {}, pluginOptions); const mergedOptions = configUtils.mergeConfig(plugin.defaultOptions || {}, pluginOptions);
await plugin.configPreprocessor(mergedOptions as PluginOptions<any>); await plugin.configPreprocessor?.(mergedOptions as PluginOptions<any>);
} catch (err) { } catch (err) {
if (err instanceof ConfigValidationError || err instanceof StrictValidationError) { if (err instanceof ConfigValidationError || err instanceof StrictValidationError) {
return `${pluginName}: ${err.message}`; return `${pluginName}: ${err.message}`;

View file

@ -37,7 +37,7 @@ export class GuildArchives extends BaseGuildRepository {
.execute(); .execute();
} }
async find(id: string): Promise<ArchiveEntry> { async find(id: string): Promise<ArchiveEntry | undefined> {
return this.archives.findOne({ return this.archives.findOne({
where: { id }, where: { id },
relations: this.getRelations(), relations: this.getRelations(),
@ -56,7 +56,7 @@ export class GuildArchives extends BaseGuildRepository {
/** /**
* @returns ID of the created entry * @returns ID of the created entry
*/ */
async create(body: string, expiresAt: moment.Moment = null): Promise<string> { async create(body: string, expiresAt?: moment.Moment): Promise<string> {
if (!expiresAt) { if (!expiresAt) {
expiresAt = moment.utc().add(DEFAULT_EXPIRY_DAYS, "days"); expiresAt = moment.utc().add(DEFAULT_EXPIRY_DAYS, "days");
} }
@ -71,7 +71,7 @@ export class GuildArchives extends BaseGuildRepository {
} }
protected async renderLinesFromSavedMessages(savedMessages: SavedMessage[], guild: Guild) { protected async renderLinesFromSavedMessages(savedMessages: SavedMessage[], guild: Guild) {
const msgLines = []; const msgLines: string[] = [];
for (const msg of savedMessages) { for (const msg of savedMessages) {
const channel = guild.channels.get(msg.channel_id); const channel = guild.channels.get(msg.channel_id);
const user = { ...msg.data.author, id: msg.user_id }; const user = { ...msg.data.author, id: msg.user_id };
@ -88,7 +88,7 @@ export class GuildArchives extends BaseGuildRepository {
return msgLines; return msgLines;
} }
async createFromSavedMessages(savedMessages: SavedMessage[], guild: Guild, expiresAt = null) { async createFromSavedMessages(savedMessages: SavedMessage[], guild: Guild, expiresAt?: moment.Moment) {
if (expiresAt == null) { if (expiresAt == null) {
expiresAt = moment.utc().add(DEFAULT_EXPIRY_DAYS, "days"); expiresAt = moment.utc().add(DEFAULT_EXPIRY_DAYS, "days");
} }
@ -105,6 +105,10 @@ export class GuildArchives extends BaseGuildRepository {
const messagesStr = msgLines.join("\n"); const messagesStr = msgLines.join("\n");
const archive = await this.find(archiveId); const archive = await this.find(archiveId);
if (archive == null) {
throw new Error("Archive not found");
}
archive.body += "\n" + messagesStr; archive.body += "\n" + messagesStr;
await this.archives.update({ id: archiveId }, { body: archive.body }); await this.archives.update({ id: archiveId }, { body: archive.body });

View file

@ -18,7 +18,7 @@ export class GuildAutoReactions extends BaseGuildRepository {
}); });
} }
async getForChannel(channelId: string): Promise<AutoReaction> { async getForChannel(channelId: string): Promise<AutoReaction | undefined> {
return this.autoReactions.findOne({ return this.autoReactions.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,

View file

@ -29,7 +29,7 @@ export class GuildCases extends BaseGuildRepository {
}); });
} }
async find(id: number): Promise<Case> { async find(id: number): Promise<Case | undefined> {
return this.cases.findOne({ return this.cases.findOne({
relations: this.getRelations(), relations: this.getRelations(),
where: { where: {
@ -39,7 +39,7 @@ export class GuildCases extends BaseGuildRepository {
}); });
} }
async findByCaseNumber(caseNumber: number): Promise<Case> { async findByCaseNumber(caseNumber: number): Promise<Case | undefined> {
return this.cases.findOne({ return this.cases.findOne({
relations: this.getRelations(), relations: this.getRelations(),
where: { where: {
@ -49,7 +49,7 @@ export class GuildCases extends BaseGuildRepository {
}); });
} }
async findLatestByModId(modId: string): Promise<Case> { async findLatestByModId(modId: string): Promise<Case | undefined> {
return this.cases.findOne({ return this.cases.findOne({
relations: this.getRelations(), relations: this.getRelations(),
where: { where: {
@ -62,7 +62,7 @@ export class GuildCases extends BaseGuildRepository {
}); });
} }
async findByAuditLogId(auditLogId: string): Promise<Case> { async findByAuditLogId(auditLogId: string): Promise<Case | undefined> {
return this.cases.findOne({ return this.cases.findOne({
relations: this.getRelations(), relations: this.getRelations(),
where: { where: {
@ -113,7 +113,7 @@ export class GuildCases extends BaseGuildRepository {
case_number: () => `(SELECT IFNULL(MAX(case_number)+1, 1) FROM cases AS ma2 WHERE guild_id = ${this.guildId})`, case_number: () => `(SELECT IFNULL(MAX(case_number)+1, 1) FROM cases AS ma2 WHERE guild_id = ${this.guildId})`,
}); });
return this.find(result.identifiers[0].id); return (await this.find(result.identifiers[0].id))!;
} }
update(id, data) { update(id, data) {

View file

@ -17,12 +17,12 @@ export class GuildEvents extends BaseGuildRepository {
this.pluginListeners.set(pluginName, new Map()); this.pluginListeners.set(pluginName, new Map());
} }
const pluginListeners = this.pluginListeners.get(pluginName); const pluginListeners = this.pluginListeners.get(pluginName)!;
if (!pluginListeners.has(eventName)) { if (!pluginListeners.has(eventName)) {
pluginListeners.set(eventName, []); pluginListeners.set(eventName, []);
} }
const pluginEventListeners = pluginListeners.get(eventName); const pluginEventListeners = pluginListeners.get(eventName)!;
pluginEventListeners.push(fn); pluginEventListeners.push(fn);
} }

View file

@ -16,7 +16,7 @@ export class GuildLogs extends events.EventEmitter {
constructor(guildId) { constructor(guildId) {
if (guildInstances.has(guildId)) { if (guildInstances.has(guildId)) {
// Return existing instance for this guild if one exists // Return existing instance for this guild if one exists
return guildInstances.get(guildId); return guildInstances.get(guildId)!;
} }
super(); super();
@ -27,7 +27,7 @@ export class GuildLogs extends events.EventEmitter {
guildInstances.set(guildId, this); guildInstances.set(guildId, this);
} }
log(type: LogType, data: any, ignoreId = null) { log(type: LogType, data: any, ignoreId?: string) {
if (ignoreId && this.isLogIgnored(type, ignoreId)) { if (ignoreId && this.isLogIgnored(type, ignoreId)) {
this.clearIgnoredLog(type, ignoreId); this.clearIgnoredLog(type, ignoreId);
return; return;
@ -36,7 +36,7 @@ export class GuildLogs extends events.EventEmitter {
this.emit("log", { type, data }); this.emit("log", { type, data });
} }
ignoreLog(type: LogType, ignoreId: any, timeout: number = null) { ignoreLog(type: LogType, ignoreId: any, timeout?: number) {
this.ignoredLogs.push({ type, ignoreId }); this.ignoredLogs.push({ type, ignoreId });
// Clear after expiry (15sec by default) // Clear after expiry (15sec by default)

View file

@ -20,7 +20,7 @@ export class GuildMutes extends BaseGuildRepository {
.getMany(); .getMany();
} }
async findExistingMuteForUserId(userId: string): Promise<Mute> { async findExistingMuteForUserId(userId: string): Promise<Mute | undefined> {
return this.mutes.findOne({ return this.mutes.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,
@ -48,7 +48,7 @@ export class GuildMutes extends BaseGuildRepository {
expires_at: expiresAt, expires_at: expiresAt,
}); });
return this.mutes.findOne({ where: result.identifiers[0] }); return (await this.mutes.findOne({ where: result.identifiers[0] }))!;
} }
async updateExpiryTime(userId, newExpiryTime) { async updateExpiryTime(userId, newExpiryTime) {

View file

@ -39,7 +39,7 @@ export class GuildNicknameHistory extends BaseGuildRepository {
}); });
} }
getLastEntry(userId): Promise<NicknameHistoryEntry> { getLastEntry(userId): Promise<NicknameHistoryEntry | undefined> {
return this.nicknameHistory.findOne({ return this.nicknameHistory.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,

View file

@ -27,7 +27,7 @@ export class GuildPingableRoles extends BaseGuildRepository {
}); });
} }
async getByChannelAndRoleId(channelId: string, roleId: string): Promise<PingableRole> { async getByChannelAndRoleId(channelId: string, roleId: string): Promise<PingableRole | undefined> {
return this.pingableRoles.findOne({ return this.pingableRoles.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,

View file

@ -27,7 +27,7 @@ export class GuildReactionRoles extends BaseGuildRepository {
}); });
} }
async getByMessageAndEmoji(messageId: string, emoji: string): Promise<ReactionRole> { async getByMessageAndEmoji(messageId: string, emoji: string): Promise<ReactionRole | undefined> {
return this.reactionRoles.findOne({ return this.reactionRoles.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,
@ -37,7 +37,7 @@ export class GuildReactionRoles extends BaseGuildRepository {
}); });
} }
async removeFromMessage(messageId: string, emoji: string = null) { async removeFromMessage(messageId: string, emoji?: string) {
const criteria: any = { const criteria: any = {
guild_id: this.guildId, guild_id: this.guildId,
message_id: messageId, message_id: messageId,

View file

@ -96,7 +96,7 @@ export class GuildSavedMessages extends BaseGuildRepository {
.getMany(); .getMany();
} }
getUserMessagesByChannelAfterId(userId, channelId, afterId, limit = null) { getUserMessagesByChannelAfterId(userId, channelId, afterId, limit?: number) {
let query = this.messages let query = this.messages
.createQueryBuilder() .createQueryBuilder()
.where("guild_id = :guild_id", { guild_id: this.guildId }) .where("guild_id = :guild_id", { guild_id: this.guildId })
@ -241,12 +241,12 @@ export class GuildSavedMessages extends BaseGuildRepository {
} }
} }
async onceMessageAvailable(id: string, handler: (msg: SavedMessage) => any, timeout: number = 60 * 1000) { async onceMessageAvailable(id: string, handler: (msg?: SavedMessage) => any, timeout: number = 60 * 1000) {
let called = false; let called = false;
let onceEventListener; let onceEventListener;
let timeoutFn; let timeoutFn;
const callHandler = async (msg: SavedMessage) => { const callHandler = async (msg?: SavedMessage) => {
this.events.off(`create:${id}`, onceEventListener); this.events.off(`create:${id}`, onceEventListener);
clearTimeout(timeoutFn); clearTimeout(timeoutFn);
@ -259,7 +259,7 @@ export class GuildSavedMessages extends BaseGuildRepository {
onceEventListener = this.events.once(`create:${id}`, callHandler); onceEventListener = this.events.once(`create:${id}`, callHandler);
timeoutFn = setTimeout(() => { timeoutFn = setTimeout(() => {
called = true; called = true;
callHandler(null); callHandler(undefined);
}, timeout); }, timeout);
const messageInDB = await this.find(id); const messageInDB = await this.find(id);

View file

@ -14,7 +14,7 @@ export class GuildSlowmodes extends BaseGuildRepository {
this.slowmodeUsers = getRepository(SlowmodeUser); this.slowmodeUsers = getRepository(SlowmodeUser);
} }
async getChannelSlowmode(channelId): Promise<SlowmodeChannel> { async getChannelSlowmode(channelId): Promise<SlowmodeChannel | undefined> {
return this.slowmodeChannels.findOne({ return this.slowmodeChannels.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,
@ -51,7 +51,7 @@ export class GuildSlowmodes extends BaseGuildRepository {
}); });
} }
async getChannelSlowmodeUser(channelId, userId): Promise<SlowmodeUser> { async getChannelSlowmodeUser(channelId, userId): Promise<SlowmodeUser | undefined> {
return this.slowmodeUsers.findOne({ return this.slowmodeUsers.findOne({
guild_id: this.guildId, guild_id: this.guildId,
channel_id: channelId, channel_id: channelId,

View file

@ -21,7 +21,7 @@ export class GuildTags extends BaseGuildRepository {
}); });
} }
async find(tag): Promise<Tag> { async find(tag): Promise<Tag | undefined> {
return this.tags.findOne({ return this.tags.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,
@ -61,7 +61,7 @@ export class GuildTags extends BaseGuildRepository {
}); });
} }
async findResponseByCommandMessageId(messageId: string): Promise<TagResponse> { async findResponseByCommandMessageId(messageId: string): Promise<TagResponse | undefined> {
return this.tagResponses.findOne({ return this.tagResponses.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,
@ -70,7 +70,7 @@ export class GuildTags extends BaseGuildRepository {
}); });
} }
async findResponseByResponseMessageId(messageId: string): Promise<TagResponse> { async findResponseByResponseMessageId(messageId: string): Promise<TagResponse | undefined> {
return this.tagResponses.findOne({ return this.tagResponses.findOne({
where: { where: {
guild_id: this.guildId, guild_id: this.guildId,

View file

@ -39,7 +39,7 @@ export class UsernameHistory extends BaseRepository {
}); });
} }
getLastEntry(userId): Promise<UsernameHistoryEntry> { getLastEntry(userId): Promise<UsernameHistoryEntry | undefined> {
return this.usernameHistory.findOne({ return this.usernameHistory.findOne({
where: { where: {
user_id: userId, user_id: userId,

View file

@ -9,8 +9,8 @@ export class AllowedGuild {
@Column() @Column()
name: string; name: string;
@Column() @Column({ nullable: true })
icon: string; icon: string | null;
@Column() @Column()
owner_id: string; owner_id: string;

View file

@ -17,5 +17,5 @@ export class ArchiveEntry {
@Column() created_at: string; @Column() created_at: string;
@Column() expires_at: string; @Column({ nullable: true }) expires_at: string | null;
} }

View file

@ -13,27 +13,27 @@ export class Case {
@Column() user_name: string; @Column() user_name: string;
@Column() mod_id: string; @Column({ nullable: true }) mod_id: string | null;
@Column() mod_name: string; @Column({ nullable: true }) mod_name: string | null;
@Column() type: number; @Column() type: number;
@Column() audit_log_id: string; @Column({ nullable: true }) audit_log_id: string | null;
@Column() created_at: string; @Column() created_at: string;
@Column() is_hidden: boolean; @Column() is_hidden: boolean;
@Column() pp_id: string; @Column({ nullable: true }) pp_id: string | null;
@Column() pp_name: string; @Column({ nullable: true }) pp_name: string | null;
/** /**
* ID of the channel and message where this case was logged. * ID of the channel and message where this case was logged.
* Format: "channelid-messageid" * Format: "channelid-messageid"
*/ */
@Column() log_message_id: string; @Column({ nullable: true }) log_message_id: string | null;
@OneToMany( @OneToMany(
type => CaseNote, type => CaseNote,

View file

@ -12,7 +12,7 @@ export class Mute {
@Column() created_at: string; @Column() created_at: string;
@Column() expires_at: string; @Column({ nullable: true }) expires_at: string | null;
@Column() case_id: number; @Column() case_id: number;
} }

View file

@ -1,9 +1,9 @@
import { Column, Entity, PrimaryColumn } from "typeorm"; import { Column, Entity, PrimaryColumn } from "typeorm";
import { createEncryptedJsonTransformer } from "../encryptedJsonTransformer"; import { createEncryptedJsonTransformer } from "../encryptedJsonTransformer";
import { Sticker } from "eris"; import { Attachment, Sticker } from "eris";
export interface ISavedMessageData { export interface ISavedMessageData {
attachments?: object[]; attachments?: Attachment[];
author: { author: {
username: string; username: string;
discriminator: string; discriminator: string;

View file

@ -20,16 +20,16 @@ export class ScheduledPost {
@Column("simple-json") attachments: Attachment[]; @Column("simple-json") attachments: Attachment[];
@Column() post_at: string; @Column({ nullable: true }) post_at: string | null;
/** /**
* How often to post the message, in milliseconds * How often to post the message, in milliseconds
*/ */
@Column() repeat_interval: number; @Column({ nullable: true }) repeat_interval: number | null;
@Column() repeat_until: string; @Column({ nullable: true }) repeat_until: string | null;
@Column() repeat_times: number; @Column({ nullable: true }) repeat_times: number | null;
@Column() enable_mentions: boolean; @Column() enable_mentions: boolean;
} }

View file

@ -212,13 +212,13 @@ connect().then(async () => {
sendSuccessMessageFn(channel, body) { sendSuccessMessageFn(channel, body) {
const guildId = channel instanceof TextChannel ? channel.guild.id : undefined; const guildId = channel instanceof TextChannel ? channel.guild.id : undefined;
const emoji = guildId ? bot.getLoadedGuild(guildId).config.success_emoji : undefined; const emoji = guildId ? bot.getLoadedGuild(guildId)!.config.success_emoji : undefined;
channel.createMessage(successMessage(body, emoji)); channel.createMessage(successMessage(body, emoji));
}, },
sendErrorMessageFn(channel, body) { sendErrorMessageFn(channel, body) {
const guildId = channel instanceof TextChannel ? channel.guild.id : undefined; const guildId = channel instanceof TextChannel ? channel.guild.id : undefined;
const emoji = guildId ? bot.getLoadedGuild(guildId).config.error_emoji : undefined; const emoji = guildId ? bot.getLoadedGuild(guildId)!.config.error_emoji : undefined;
channel.createMessage(errorMessage(body, emoji)); channel.createMessage(errorMessage(body, emoji));
}, },
}, },

View file

@ -19,8 +19,8 @@ export class MigrateUsernamesToNewHistoryTable1556909512501 implements Migration
const migrateNextBatch = (): Promise<{ finished: boolean; migrated?: number }> => { const migrateNextBatch = (): Promise<{ finished: boolean; migrated?: number }> => {
return new Promise(async resolve => { return new Promise(async resolve => {
const toInsert = []; const toInsert: any[][] = [];
const toDelete = []; const toDelete: number[] = [];
const stream = await queryRunner.stream( const stream = await queryRunner.stream(
`SELECT * FROM name_history WHERE type=1 ORDER BY timestamp ASC LIMIT ${BATCH_SIZE}`, `SELECT * FROM name_history WHERE type=1 ORDER BY timestamp ASC LIMIT ${BATCH_SIZE}`,

View file

@ -2,7 +2,7 @@ import { MigrationInterface, QueryRunner, TableIndex } from "typeorm";
export class AddMoreIndicesToVCAlerts1562838838927 implements MigrationInterface { export class AddMoreIndicesToVCAlerts1562838838927 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> { public async up(queryRunner: QueryRunner): Promise<any> {
const table = await queryRunner.getTable("vc_alerts"); const table = (await queryRunner.getTable("vc_alerts"))!;
await table.addIndex( await table.addIndex(
new TableIndex({ new TableIndex({
columnNames: ["requestor_id"], columnNames: ["requestor_id"],
@ -16,7 +16,7 @@ export class AddMoreIndicesToVCAlerts1562838838927 implements MigrationInterface
} }
public async down(queryRunner: QueryRunner): Promise<any> { public async down(queryRunner: QueryRunner): Promise<any> {
const table = await queryRunner.getTable("vc_alerts"); const table = (await queryRunner.getTable("vc_alerts"))!;
await table.removeIndex( await table.removeIndex(
new TableIndex({ new TableIndex({
columnNames: ["requestor_id"], columnNames: ["requestor_id"],

View file

@ -6,7 +6,7 @@ export class AddTypeAndPermissionsToApiPermissions1573158035867 implements Migra
await queryRunner.dropPrimaryKey("api_permissions"); await queryRunner.dropPrimaryKey("api_permissions");
} catch (e) {} // tslint:disable-line } catch (e) {} // tslint:disable-line
const table = await queryRunner.getTable("api_permissions"); const table = (await queryRunner.getTable("api_permissions"))!;
if (table.indices.length) { if (table.indices.length) {
await queryRunner.dropIndex("api_permissions", table.indices[0]); await queryRunner.dropIndex("api_permissions", table.indices[0]);
} }

View file

@ -3,7 +3,7 @@ import pkgUp from "pkg-up";
const backendPackageJson = pkgUp.sync({ const backendPackageJson = pkgUp.sync({
cwd: __dirname, cwd: __dirname,
}); }) as string;
export const backendDir = path.dirname(backendPackageJson); export const backendDir = path.dirname(backendPackageJson);
export const rootDir = path.resolve(backendDir, ".."); export const rootDir = path.resolve(backendDir, "..");

View file

@ -32,26 +32,28 @@ export function hasPermission(pluginData: AnyPluginData<any>, permission: string
return helpers.hasPermission(config, permission); return helpers.hasPermission(config, permission);
} }
const PluginOverrideCriteriaType = t.recursion("PluginOverrideCriteriaType", () => const PluginOverrideCriteriaType: t.Type<PluginOverrideCriteria<unknown>> = t.recursion(
t.type({ "PluginOverrideCriteriaType",
channel: tNullable(t.union([t.string, t.array(t.string)])), () =>
category: tNullable(t.union([t.string, t.array(t.string)])), t.partial({
level: tNullable(t.union([t.string, t.array(t.string)])), channel: tNullable(t.union([t.string, t.array(t.string)])),
user: tNullable(t.union([t.string, t.array(t.string)])), category: tNullable(t.union([t.string, t.array(t.string)])),
role: tNullable(t.union([t.string, t.array(t.string)])), level: tNullable(t.union([t.string, t.array(t.string)])),
user: tNullable(t.union([t.string, t.array(t.string)])),
role: tNullable(t.union([t.string, t.array(t.string)])),
all: tNullable(t.array(PluginOverrideCriteriaType)), all: tNullable(t.array(PluginOverrideCriteriaType)),
any: tNullable(t.array(PluginOverrideCriteriaType)), any: tNullable(t.array(PluginOverrideCriteriaType)),
not: tNullable(PluginOverrideCriteriaType), not: tNullable(PluginOverrideCriteriaType),
extra: t.unknown, extra: t.unknown,
}), }),
); );
const BasicPluginStructureType = t.type({ const BasicPluginStructureType = t.type({
enabled: tNullable(t.boolean), enabled: tNullable(t.boolean),
config: tNullable(t.unknown), config: tNullable(t.unknown),
overrides: tNullable(t.array(PluginOverrideCriteriaType)), overrides: tNullable(t.array(t.union([PluginOverrideCriteriaType, t.type({ config: t.unknown })]))),
replaceDefaultOverrides: tNullable(t.boolean), replaceDefaultOverrides: tNullable(t.boolean),
}); });
@ -101,7 +103,7 @@ export function getPluginConfigPreprocessor(
// 4. Merge with default options and validate/decode the entire config // 4. Merge with default options and validate/decode the entire config
let decodedConfig = {}; let decodedConfig = {};
const decodedOverrides = []; const decodedOverrides: Array<PluginOverrideCriteria<unknown> & { config: any }> = [];
if (options.config) { if (options.config) {
decodedConfig = blueprint.configSchema decodedConfig = blueprint.configSchema

View file

@ -4,6 +4,7 @@ import { tDelayString, MINUTES } from "../../utils";
import { GuildLogs } from "../../data/GuildLogs"; import { GuildLogs } from "../../data/GuildLogs";
import { GuildSavedMessages } from "../../data/GuildSavedMessages"; import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { SavedMessage } from "../../data/entities/SavedMessage"; import { SavedMessage } from "../../data/entities/SavedMessage";
import Timeout = NodeJS.Timeout;
export const MAX_DELAY = 5 * MINUTES; export const MAX_DELAY = 5 * MINUTES;
@ -25,8 +26,8 @@ export interface AutoDeletePluginType extends BasePluginType {
guildLogs: GuildLogs; guildLogs: GuildLogs;
deletionQueue: IDeletionQueueItem[]; deletionQueue: IDeletionQueueItem[];
nextDeletion: number; nextDeletion: number | null;
nextDeletionTimeout; nextDeletionTimeout: Timeout | null;
maxDelayWarningSent: boolean; maxDelayWarningSent: boolean;

View file

@ -9,7 +9,7 @@ export async function onMessageCreate(pluginData: GuildPluginData<AutoDeletePlug
const member = await resolveMember(pluginData.client, pluginData.guild, msg.user_id); const member = await resolveMember(pluginData.client, pluginData.guild, msg.user_id);
const config = pluginData.config.getMatchingConfig({ member, channelId: msg.channel_id }); const config = pluginData.config.getMatchingConfig({ member, channelId: msg.channel_id });
if (config.enabled) { if (config.enabled) {
let delay = convertDelayStringToMS(config.delay); let delay = convertDelayStringToMS(config.delay)!;
if (delay > MAX_DELAY) { if (delay > MAX_DELAY) {
delay = MAX_DELAY; delay = MAX_DELAY;

View file

@ -4,11 +4,11 @@ import { deleteNextItem } from "./deleteNextItem";
export function scheduleNextDeletion(pluginData: GuildPluginData<AutoDeletePluginType>) { export function scheduleNextDeletion(pluginData: GuildPluginData<AutoDeletePluginType>) {
if (pluginData.state.deletionQueue.length === 0) { if (pluginData.state.deletionQueue.length === 0) {
clearTimeout(pluginData.state.nextDeletionTimeout); clearTimeout(pluginData.state.nextDeletionTimeout!);
return; return;
} }
const firstDeleteAt = pluginData.state.deletionQueue[0].deleteAt; const firstDeleteAt = pluginData.state.deletionQueue[0].deleteAt;
clearTimeout(pluginData.state.nextDeletionTimeout); clearTimeout(pluginData.state.nextDeletionTimeout!);
pluginData.state.nextDeletionTimeout = setTimeout(() => deleteNextItem(pluginData), firstDeleteAt - Date.now()); pluginData.state.nextDeletionTimeout = setTimeout(() => deleteNextItem(pluginData), firstDeleteAt - Date.now());
} }

View file

@ -20,9 +20,9 @@ export const NewAutoReactionsCmd = autoReactionsCmd({
}, },
async run({ message: msg, args, pluginData }) { async run({ message: msg, args, pluginData }) {
const finalReactions = []; const finalReactions: string[] = [];
const me = pluginData.guild.members.get(pluginData.client.user.id); const me = pluginData.guild.members.get(pluginData.client.user.id)!;
const missingPermissions = getMissingChannelPermissions(me, args.channel as GuildChannel, requiredPermissions); const missingPermissions = getMissingChannelPermissions(me, args.channel as GuildChannel, requiredPermissions);
if (missingPermissions) { if (missingPermissions) {
sendErrorMessage( sendErrorMessage(

View file

@ -19,7 +19,7 @@ export const AddReactionsEvt = autoReactionsEvt({
const autoReaction = await pluginData.state.autoReactions.getForChannel(message.channel.id); const autoReaction = await pluginData.state.autoReactions.getForChannel(message.channel.id);
if (!autoReaction) return; if (!autoReaction) return;
const me = pluginData.guild.members.get(pluginData.client.user.id); const me = pluginData.guild.members.get(pluginData.client.user.id)!;
const missingPermissions = getMissingChannelPermissions( const missingPermissions = getMissingChannelPermissions(
me, me,
message.channel as GuildChannel, message.channel as GuildChannel,

View file

@ -158,7 +158,7 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()("automod",
configPreprocessor, configPreprocessor,
customOverrideMatcher(pluginData, criteria, matchParams) { customOverrideMatcher(pluginData, criteria, matchParams) {
return criteria?.antiraid_level && criteria.antiraid_level === pluginData.state.cachedAntiraidLevel; return criteria?.antiraid_level ? criteria.antiraid_level === pluginData.state.cachedAntiraidLevel : false;
}, },
events: [ events: [

View file

@ -1,7 +1,7 @@
import * as t from "io-ts"; import * as t from "io-ts";
import { automodAction } from "../helpers"; import { automodAction } from "../helpers";
import { LogType } from "../../../data/LogType"; import { LogType } from "../../../data/LogType";
import { unique } from "../../../utils"; import { nonNullish, unique } from "../../../utils";
import { Constants } from "eris"; import { Constants } from "eris";
import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions"; import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions";
import { LogsPlugin } from "../../Logs/LogsPlugin"; import { LogsPlugin } from "../../Logs/LogsPlugin";
@ -17,8 +17,8 @@ export const AddRolesAction = automodAction({
defaultConfig: [], defaultConfig: [],
async apply({ pluginData, contexts, actionConfig, ruleName }) { async apply({ pluginData, contexts, actionConfig, ruleName }) {
const members = unique(contexts.map(c => c.member).filter(Boolean)); const members = unique(contexts.map(c => c.member).filter(nonNullish));
const me = pluginData.guild.members.get(pluginData.client.user.id); const me = pluginData.guild.members.get(pluginData.client.user.id)!;
const missingPermissions = getMissingPermissions(me.permission, p.manageRoles); const missingPermissions = getMissingPermissions(me.permission, p.manageRoles);
if (missingPermissions) { if (missingPermissions) {
@ -29,8 +29,8 @@ export const AddRolesAction = automodAction({
return; return;
} }
const rolesToAssign = []; const rolesToAssign: string[] = [];
const rolesWeCannotAssign = []; const rolesWeCannotAssign: string[] = [];
for (const roleId of actionConfig) { for (const roleId of actionConfig) {
if (canAssignRole(pluginData.guild, me, roleId)) { if (canAssignRole(pluginData.guild, me, roleId)) {
rolesToAssign.push(roleId); rolesToAssign.push(roleId);

View file

@ -1,7 +1,7 @@
import * as t from "io-ts"; import * as t from "io-ts";
import { automodAction } from "../helpers"; import { automodAction } from "../helpers";
import { LogType } from "../../../data/LogType"; import { LogType } from "../../../data/LogType";
import { asyncMap, resolveMember, tNullable, unique } from "../../../utils"; import { asyncMap, nonNullish, resolveMember, tNullable, unique } from "../../../utils";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods"; import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
@ -20,14 +20,14 @@ export const BanAction = automodAction({
async apply({ pluginData, contexts, actionConfig, matchResult }) { async apply({ pluginData, contexts, actionConfig, matchResult }) {
const reason = actionConfig.reason || "Kicked automatically"; const reason = actionConfig.reason || "Kicked automatically";
const contactMethods = resolveActionContactMethods(pluginData, actionConfig); const contactMethods = resolveActionContactMethods(pluginData, actionConfig);
const deleteMessageDays = actionConfig.deleteMessageDays; const deleteMessageDays = actionConfig.deleteMessageDays || undefined;
const caseArgs = { const caseArgs = {
modId: pluginData.client.user.id, modId: pluginData.client.user.id,
extraNotes: [matchResult.fullSummary], extraNotes: matchResult.fullSummary ? [matchResult.fullSummary] : [],
}; };
const userIdsToBan = unique(contexts.map(c => c.user?.id).filter(Boolean)); const userIdsToBan = unique(contexts.map(c => c.user?.id).filter(nonNullish));
const modActions = pluginData.getPlugin(ModActionsPlugin); const modActions = pluginData.getPlugin(ModActionsPlugin);
for (const userId of userIdsToBan) { for (const userId of userIdsToBan) {

View file

@ -2,7 +2,7 @@ import * as t from "io-ts";
import { automodAction } from "../helpers"; import { automodAction } from "../helpers";
import { LogType } from "../../../data/LogType"; import { LogType } from "../../../data/LogType";
import { LogsPlugin } from "../../Logs/LogsPlugin"; import { LogsPlugin } from "../../Logs/LogsPlugin";
import { unique } from "../../../utils"; import { nonNullish, unique } from "../../../utils";
export const ChangeNicknameAction = automodAction({ export const ChangeNicknameAction = automodAction({
configType: t.union([ configType: t.union([
@ -15,7 +15,7 @@ export const ChangeNicknameAction = automodAction({
defaultConfig: {}, defaultConfig: {},
async apply({ pluginData, contexts, actionConfig }) { async apply({ pluginData, contexts, actionConfig }) {
const members = unique(contexts.map(c => c.member).filter(Boolean)); const members = unique(contexts.map(c => c.member).filter(nonNullish));
for (const member of members) { for (const member of members) {
if (pluginData.state.recentNicknameChanges.has(member.id)) continue; if (pluginData.state.recentNicknameChanges.has(member.id)) continue;

View file

@ -15,12 +15,12 @@ export const CleanAction = automodAction({
messageIdsToDeleteByChannelId.set(context.message.channel_id, []); messageIdsToDeleteByChannelId.set(context.message.channel_id, []);
} }
if (messageIdsToDeleteByChannelId.get(context.message.channel_id).includes(context.message.id)) { if (messageIdsToDeleteByChannelId.get(context.message.channel_id)!.includes(context.message.id)) {
console.warn(`Message ID to delete was already present: ${pluginData.guild.name}, rule ${ruleName}`); console.warn(`Message ID to delete was already present: ${pluginData.guild.name}, rule ${ruleName}`);
continue; continue;
} }
messageIdsToDeleteByChannelId.get(context.message.channel_id).push(context.message.id); messageIdsToDeleteByChannelId.get(context.message.channel_id)!.push(context.message.id);
} }
} }

View file

@ -1,7 +1,7 @@
import * as t from "io-ts"; import * as t from "io-ts";
import { automodAction } from "../helpers"; import { automodAction } from "../helpers";
import { LogType } from "../../../data/LogType"; import { LogType } from "../../../data/LogType";
import { asyncMap, resolveMember, tNullable, unique } from "../../../utils"; import { asyncMap, nonNullish, resolveMember, tNullable, unique } from "../../../utils";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods"; import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
@ -22,14 +22,15 @@ export const KickAction = automodAction({
const caseArgs = { const caseArgs = {
modId: pluginData.client.user.id, modId: pluginData.client.user.id,
extraNotes: [matchResult.fullSummary], extraNotes: matchResult.fullSummary ? [matchResult.fullSummary] : [],
}; };
const userIdsToKick = unique(contexts.map(c => c.user?.id).filter(Boolean)); const userIdsToKick = unique(contexts.map(c => c.user?.id).filter(nonNullish));
const membersToKick = await asyncMap(userIdsToKick, id => resolveMember(pluginData.client, pluginData.guild, id)); const membersToKick = await asyncMap(userIdsToKick, id => resolveMember(pluginData.client, pluginData.guild, id));
const modActions = pluginData.getPlugin(ModActionsPlugin); const modActions = pluginData.getPlugin(ModActionsPlugin);
for (const member of membersToKick) { for (const member of membersToKick) {
if (!member) continue;
await modActions.kickMember(member, reason, { contactMethods, caseArgs }); await modActions.kickMember(member, reason, { contactMethods, caseArgs });
} }
}, },

View file

@ -1,7 +1,15 @@
import * as t from "io-ts"; import * as t from "io-ts";
import { automodAction } from "../helpers"; import { automodAction } from "../helpers";
import { LogType } from "../../../data/LogType"; import { LogType } from "../../../data/LogType";
import { asyncMap, convertDelayStringToMS, resolveMember, tDelayString, tNullable, unique } from "../../../utils"; import {
asyncMap,
convertDelayStringToMS,
nonNullish,
resolveMember,
tDelayString,
tNullable,
unique,
} from "../../../utils";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods"; import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
import { MutesPlugin } from "../../Mutes/MutesPlugin"; import { MutesPlugin } from "../../Mutes/MutesPlugin";
@ -21,16 +29,16 @@ export const MuteAction = automodAction({
}, },
async apply({ pluginData, contexts, actionConfig, ruleName, matchResult }) { async apply({ pluginData, contexts, actionConfig, ruleName, matchResult }) {
const duration = actionConfig.duration ? convertDelayStringToMS(actionConfig.duration) : null; const duration = actionConfig.duration ? convertDelayStringToMS(actionConfig.duration)! : undefined;
const reason = actionConfig.reason || "Muted automatically"; const reason = actionConfig.reason || "Muted automatically";
const contactMethods = resolveActionContactMethods(pluginData, actionConfig); const contactMethods = resolveActionContactMethods(pluginData, actionConfig);
const caseArgs = { const caseArgs = {
modId: pluginData.client.user.id, modId: pluginData.client.user.id,
extraNotes: [matchResult.fullSummary], extraNotes: matchResult.fullSummary ? [matchResult.fullSummary] : [],
}; };
const userIdsToMute = unique(contexts.map(c => c.user?.id).filter(Boolean)); const userIdsToMute = unique(contexts.map(c => c.user?.id).filter(nonNullish));
const mutes = pluginData.getPlugin(MutesPlugin); const mutes = pluginData.getPlugin(MutesPlugin);
for (const userId of userIdsToMute) { for (const userId of userIdsToMute) {

View file

@ -1,7 +1,7 @@
import * as t from "io-ts"; import * as t from "io-ts";
import { automodAction } from "../helpers"; import { automodAction } from "../helpers";
import { LogType } from "../../../data/LogType"; import { LogType } from "../../../data/LogType";
import { asyncMap, resolveMember, tNullable, unique } from "../../../utils"; import { asyncMap, nonNullish, resolveMember, tNullable, unique } from "../../../utils";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods"; import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
import { getMissingPermissions } from "../../../utils/getMissingPermissions"; import { getMissingPermissions } from "../../../utils/getMissingPermissions";
@ -19,8 +19,8 @@ export const RemoveRolesAction = automodAction({
defaultConfig: [], defaultConfig: [],
async apply({ pluginData, contexts, actionConfig, ruleName }) { async apply({ pluginData, contexts, actionConfig, ruleName }) {
const members = unique(contexts.map(c => c.member).filter(Boolean)); const members = unique(contexts.map(c => c.member).filter(nonNullish));
const me = pluginData.guild.members.get(pluginData.client.user.id); const me = pluginData.guild.members.get(pluginData.client.user.id)!;
const missingPermissions = getMissingPermissions(me.permission, p.manageRoles); const missingPermissions = getMissingPermissions(me.permission, p.manageRoles);
if (missingPermissions) { if (missingPermissions) {
@ -31,8 +31,8 @@ export const RemoveRolesAction = automodAction({
return; return;
} }
const rolesToRemove = []; const rolesToRemove: string[] = [];
const rolesWeCannotRemove = []; const rolesWeCannotRemove: string[] = [];
for (const roleId of actionConfig) { for (const roleId of actionConfig) {
if (canAssignRole(pluginData.guild, me, roleId)) { if (canAssignRole(pluginData.guild, me, roleId)) {
rolesToRemove.push(roleId); rolesToRemove.push(roleId);

View file

@ -28,14 +28,14 @@ export const ReplyAction = automodAction({
async apply({ pluginData, contexts, actionConfig }) { async apply({ pluginData, contexts, actionConfig }) {
const contextsWithTextChannels = contexts const contextsWithTextChannels = contexts
.filter(c => c.message?.channel_id) .filter(c => c.message?.channel_id)
.filter(c => pluginData.guild.channels.get(c.message.channel_id) instanceof TextChannel); .filter(c => pluginData.guild.channels.get(c.message!.channel_id) instanceof TextChannel);
const contextsByChannelId = contextsWithTextChannels.reduce((map: Map<string, AutomodContext[]>, context) => { const contextsByChannelId = contextsWithTextChannels.reduce((map: Map<string, AutomodContext[]>, context) => {
if (!map.has(context.message.channel_id)) { if (!map.has(context.message!.channel_id)) {
map.set(context.message.channel_id, []); map.set(context.message!.channel_id, []);
} }
map.get(context.message.channel_id).push(context); map.get(context.message!.channel_id)!.push(context);
return map; return map;
}, new Map()); }, new Map());
@ -57,7 +57,7 @@ export const ReplyAction = automodAction({
const replyMsg = await channel.createMessage(formatted); const replyMsg = await channel.createMessage(formatted);
if (typeof actionConfig === "object" && actionConfig.auto_delete) { if (typeof actionConfig === "object" && actionConfig.auto_delete) {
const delay = convertDelayStringToMS(String(actionConfig.auto_delete)); const delay = convertDelayStringToMS(String(actionConfig.auto_delete))!;
setTimeout(() => replyMsg.delete().catch(noop), delay); setTimeout(() => replyMsg.delete().catch(noop), delay);
} }
} }

View file

@ -4,7 +4,7 @@ import { setAntiraidLevel } from "../functions/setAntiraidLevel";
export const SetAntiraidLevelAction = automodAction({ export const SetAntiraidLevelAction = automodAction({
configType: t.string, configType: t.string,
defaultConfig: null, defaultConfig: "",
async apply({ pluginData, contexts, actionConfig }) { async apply({ pluginData, contexts, actionConfig }) {
setAntiraidLevel(pluginData, actionConfig); setAntiraidLevel(pluginData, actionConfig);

View file

@ -1,7 +1,7 @@
import * as t from "io-ts"; import * as t from "io-ts";
import { automodAction } from "../helpers"; import { automodAction } from "../helpers";
import { LogType } from "../../../data/LogType"; import { LogType } from "../../../data/LogType";
import { asyncMap, resolveMember, tNullable, unique } from "../../../utils"; import { asyncMap, nonNullish, resolveMember, tNullable, unique } from "../../../utils";
import { resolveActionContactMethods } from "../functions/resolveActionContactMethods"; import { resolveActionContactMethods } from "../functions/resolveActionContactMethods";
import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin"; import { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
@ -22,14 +22,15 @@ export const WarnAction = automodAction({
const caseArgs = { const caseArgs = {
modId: pluginData.client.user.id, modId: pluginData.client.user.id,
extraNotes: [matchResult.fullSummary], extraNotes: matchResult.fullSummary ? [matchResult.fullSummary] : [],
}; };
const userIdsToWarn = unique(contexts.map(c => c.user?.id).filter(Boolean)); const userIdsToWarn = unique(contexts.map(c => c.user?.id).filter(nonNullish));
const membersToWarn = await asyncMap(userIdsToWarn, id => resolveMember(pluginData.client, pluginData.guild, id)); const membersToWarn = await asyncMap(userIdsToWarn, id => resolveMember(pluginData.client, pluginData.guild, id));
const modActions = pluginData.getPlugin(ModActionsPlugin); const modActions = pluginData.getPlugin(ModActionsPlugin);
for (const member of membersToWarn) { for (const member of membersToWarn) {
if (!member) continue;
await modActions.warnMember(member, reason, { contactMethods, caseArgs }); await modActions.warnMember(member, reason, { contactMethods, caseArgs });
} }
}, },

View file

@ -4,8 +4,9 @@ import { RECENT_ACTION_EXPIRY_TIME, RecentActionType } from "../constants";
import { getEmojiInString, getRoleMentions, getUrlsInString, getUserMentions } from "../../../utils"; import { getEmojiInString, getRoleMentions, getUrlsInString, getUserMentions } from "../../../utils";
export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) { export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) {
const globalIdentifier = context.message.user_id; const message = context.message!;
const perChannelIdentifier = `${context.message.channel_id}-${context.message.user_id}`; const globalIdentifier = message.user_id;
const perChannelIdentifier = `${message.channel_id}-${message.user_id}`;
const expiresAt = Date.now() + RECENT_ACTION_EXPIRY_TIME; const expiresAt = Date.now() + RECENT_ACTION_EXPIRY_TIME;
pluginData.state.recentActions.push({ pluginData.state.recentActions.push({
@ -23,8 +24,7 @@ export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodP
}); });
const mentionCount = const mentionCount =
getUserMentions(context.message.data.content || "").length + getUserMentions(message.data.content || "").length + getRoleMentions(message.data.content || "").length;
getRoleMentions(context.message.data.content || "").length;
if (mentionCount) { if (mentionCount) {
pluginData.state.recentActions.push({ pluginData.state.recentActions.push({
context, context,
@ -41,7 +41,7 @@ export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodP
}); });
} }
const linkCount = getUrlsInString(context.message.data.content || "").length; const linkCount = getUrlsInString(message.data.content || "").length;
if (linkCount) { if (linkCount) {
pluginData.state.recentActions.push({ pluginData.state.recentActions.push({
context, context,
@ -58,7 +58,7 @@ export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodP
}); });
} }
const attachmentCount = context.message.data.attachments && context.message.data.attachments.length; const attachmentCount = message.data.attachments && message.data.attachments.length;
if (attachmentCount) { if (attachmentCount) {
pluginData.state.recentActions.push({ pluginData.state.recentActions.push({
context, context,
@ -75,7 +75,7 @@ export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodP
}); });
} }
const emojiCount = getEmojiInString(context.message.data.content || "").length; const emojiCount = getEmojiInString(message.data.content || "").length;
if (emojiCount) { if (emojiCount) {
pluginData.state.recentActions.push({ pluginData.state.recentActions.push({
context, context,
@ -93,7 +93,7 @@ export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodP
} }
// + 1 is for the first line of the message (which doesn't have a line break) // + 1 is for the first line of the message (which doesn't have a line break)
const lineCount = context.message.data.content ? (context.message.data.content.match(/\n/g) || []).length + 1 : 0; const lineCount = message.data.content ? (message.data.content.match(/\n/g) || []).length + 1 : 0;
if (lineCount) { if (lineCount) {
pluginData.state.recentActions.push({ pluginData.state.recentActions.push({
context, context,
@ -110,7 +110,7 @@ export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodP
}); });
} }
const characterCount = [...(context.message.data.content || "")].length; const characterCount = [...(message.data.content || "")].length;
if (characterCount) { if (characterCount) {
pluginData.state.recentActions.push({ pluginData.state.recentActions.push({
context, context,
@ -127,7 +127,7 @@ export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodP
}); });
} }
const stickerCount = (context.message.data.stickers || []).length; const stickerCount = (message.data.stickers || []).length;
if (stickerCount) { if (stickerCount) {
pluginData.state.recentActions.push({ pluginData.state.recentActions.push({
context, context,

View file

@ -4,8 +4,9 @@ import { RECENT_ACTION_EXPIRY_TIME, RecentActionType } from "../constants";
import { getEmojiInString, getRoleMentions, getUrlsInString, getUserMentions } from "../../../utils"; import { getEmojiInString, getRoleMentions, getUrlsInString, getUserMentions } from "../../../utils";
export function clearRecentActionsForMessage(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) { export function clearRecentActionsForMessage(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) {
const globalIdentifier = context.message.user_id; const message = context.message!;
const perChannelIdentifier = `${context.message.channel_id}-${context.message.user_id}`; const globalIdentifier = message.user_id;
const perChannelIdentifier = `${message.channel_id}-${message.user_id}`;
pluginData.state.recentActions = pluginData.state.recentActions.filter(act => { pluginData.state.recentActions = pluginData.state.recentActions.filter(act => {
return act.identifier !== globalIdentifier && act.identifier !== perChannelIdentifier; return act.identifier !== globalIdentifier && act.identifier !== perChannelIdentifier;

View file

@ -7,6 +7,7 @@ import { findRecentSpam } from "./findRecentSpam";
import { getMatchingMessageRecentActions } from "./getMatchingMessageRecentActions"; import { getMatchingMessageRecentActions } from "./getMatchingMessageRecentActions";
import * as t from "io-ts"; import * as t from "io-ts";
import { getMessageSpamIdentifier } from "./getSpamIdentifier"; import { getMessageSpamIdentifier } from "./getSpamIdentifier";
import { SavedMessage } from "../../../data/entities/SavedMessage";
const MessageSpamTriggerConfig = t.type({ const MessageSpamTriggerConfig = t.type({
amount: t.number, amount: t.number,
@ -29,22 +30,25 @@ export function createMessageSpamTrigger(spamType: RecentActionType, prettyName:
return; return;
} }
const spamIdentifier = getMessageSpamIdentifier(context.message, triggerConfig.per_channel); const spamIdentifier = getMessageSpamIdentifier(context.message, Boolean(triggerConfig.per_channel));
const recentSpam = findRecentSpam(pluginData, spamType, spamIdentifier); const recentSpam = findRecentSpam(pluginData, spamType, spamIdentifier);
if (recentSpam) { if (recentSpam) {
await pluginData.state.archives.addSavedMessagesToArchive( if (recentSpam.archiveId) {
recentSpam.archiveId, await pluginData.state.archives.addSavedMessagesToArchive(
[context.message], recentSpam.archiveId,
pluginData.guild, [context.message],
); pluginData.guild,
);
}
return { return {
silentClean: true, silentClean: true,
extra: { archiveId: "" }, // FIXME: Fix up automod trigger match() typings so extra is not required when doing a silentClean
}; };
} }
const within = convertDelayStringToMS(triggerConfig.within); const within = convertDelayStringToMS(triggerConfig.within) ?? 0;
const matchedSpam = getMatchingMessageRecentActions( const matchedSpam = getMatchingMessageRecentActions(
pluginData, pluginData,
context.message, context.message,
@ -58,7 +62,7 @@ export function createMessageSpamTrigger(spamType: RecentActionType, prettyName:
const messages = matchedSpam.recentActions const messages = matchedSpam.recentActions
.map(action => action.context.message) .map(action => action.context.message)
.filter(Boolean) .filter(Boolean)
.sort(sorter("posted_at")); .sort(sorter("posted_at")) as SavedMessage[];
const archiveId = await pluginData.state.archives.createFromSavedMessages(messages, pluginData.guild); const archiveId = await pluginData.state.archives.createFromSavedMessages(messages, pluginData.guild);

View file

@ -16,7 +16,7 @@ export function getMatchingRecentActions(
action.type === type && action.type === type &&
(!identifier || action.identifier === identifier) && (!identifier || action.identifier === identifier) &&
action.context.timestamp >= since && action.context.timestamp >= since &&
action.context.timestamp <= to && action.context.timestamp <= to! &&
!action.context.actioned !action.context.actioned
); );
}); });

View file

@ -10,23 +10,25 @@ export function getTextMatchPartialSummary(
context: AutomodContext, context: AutomodContext,
) { ) {
if (type === "message") { if (type === "message") {
const channel = pluginData.guild.channels.get(context.message.channel_id); const message = context.message!;
const channelMention = channel ? verboseChannelMention(channel) : `\`#${context.message.channel_id}\``; const channel = pluginData.guild.channels.get(message.channel_id);
const channelMention = channel ? verboseChannelMention(channel) : `\`#${message.channel_id}\``;
return `message in ${channelMention}:\n${messageSummary(context.message)}`; return `message in ${channelMention}:\n${messageSummary(message)}`;
} else if (type === "embed") { } else if (type === "embed") {
const channel = pluginData.guild.channels.get(context.message.channel_id); const message = context.message!;
const channelMention = channel ? verboseChannelMention(channel) : `\`#${context.message.channel_id}\``; const channel = pluginData.guild.channels.get(message.channel_id);
const channelMention = channel ? verboseChannelMention(channel) : `\`#${message.channel_id}\``;
return `message embed in ${channelMention}:\n${messageSummary(context.message)}`; return `message embed in ${channelMention}:\n${messageSummary(message)}`;
} else if (type === "username") { } else if (type === "username") {
return `username: ${context.user.username}`; return `username: ${context.user!.username}`;
} else if (type === "nickname") { } else if (type === "nickname") {
return `nickname: ${context.member.nick}`; return `nickname: ${context.member!.nick}`;
} else if (type === "visiblename") { } else if (type === "visiblename") {
const visibleName = context.member?.nick || context.user.username; const visibleName = context.member?.nick || context.user!.username;
return `visible name: ${visibleName}`; return `visible name: ${visibleName}`;
} else if (type === "customstatus") { } else if (type === "customstatus") {
return `custom status: ${context.member.game.state}`; return `custom status: ${context.member!.game!.state}`;
} }
} }

View file

@ -7,10 +7,10 @@ import { AutomodPluginType } from "../types";
export function resolveActionContactMethods( export function resolveActionContactMethods(
pluginData: GuildPluginData<AutomodPluginType>, pluginData: GuildPluginData<AutomodPluginType>,
actionConfig: { actionConfig: {
notify?: string; notify?: string | null;
notifyChannel?: string; notifyChannel?: string | null;
}, },
): UserNotificationMethod[] | null { ): UserNotificationMethod[] {
if (actionConfig.notify === "dm") { if (actionConfig.notify === "dm") {
return [{ type: "dm" }]; return [{ type: "dm" }];
} else if (actionConfig.notify === "channel") { } else if (actionConfig.notify === "channel") {
@ -28,5 +28,5 @@ export function resolveActionContactMethods(
return []; return [];
} }
return null; return [];
} }

View file

@ -5,13 +5,14 @@ import { availableActions } from "../actions/availableActions";
import { AutomodTriggerMatchResult } from "../helpers"; import { AutomodTriggerMatchResult } from "../helpers";
import { CleanAction } from "../actions/clean"; import { CleanAction } from "../actions/clean";
import { checkAndUpdateCooldown } from "./checkAndUpdateCooldown"; import { checkAndUpdateCooldown } from "./checkAndUpdateCooldown";
import { TextChannel } from "eris";
export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) { export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) {
const userId = context.user?.id || context.member?.id || context.message?.user_id; const userId = context.user?.id || context.member?.id || context.message?.user_id;
const user = context.user || (userId && pluginData.client.users.get(userId)); const user = context.user || (userId && pluginData.client.users.get(userId));
const member = context.member || (userId && pluginData.guild.members.get(userId)); const member = context.member || (userId && pluginData.guild.members.get(userId)) || null;
const channelId = context.message?.channel_id; const channelId = context.message?.channel_id;
const channel = channelId && pluginData.guild.channels.get(channelId); const channel = channelId ? (pluginData.guild.channels.get(channelId) as TextChannel) : null;
const categoryId = channel?.parentID; const categoryId = channel?.parentID;
const config = pluginData.config.getMatchingConfig({ const config = pluginData.config.getMatchingConfig({
@ -29,8 +30,8 @@ export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>,
return; return;
} }
let matchResult: AutomodTriggerMatchResult<any>; let matchResult: AutomodTriggerMatchResult<any> | null | undefined;
let contexts: AutomodContext[]; let contexts: AutomodContext[] = [];
triggerLoop: for (const triggerItem of rule.triggers) { triggerLoop: for (const triggerItem of rule.triggers) {
for (const [triggerName, triggerConfig] of Object.entries(triggerItem)) { for (const [triggerName, triggerConfig] of Object.entries(triggerItem)) {

View file

@ -3,9 +3,8 @@ import { Awaitable } from "knub/dist/utils";
import * as t from "io-ts"; import * as t from "io-ts";
import { AutomodContext, AutomodPluginType } from "./types"; import { AutomodContext, AutomodPluginType } from "./types";
export interface AutomodTriggerMatchResult<TExtra extends any = unknown> { interface BaseAutomodTriggerMatchResult {
extraContexts?: AutomodContext[]; extraContexts?: AutomodContext[];
extra?: TExtra;
silentClean?: boolean; // TODO: Maybe generalize to a "silent" value in general, which mutes alert/log silentClean?: boolean; // TODO: Maybe generalize to a "silent" value in general, which mutes alert/log
@ -13,12 +12,16 @@ export interface AutomodTriggerMatchResult<TExtra extends any = unknown> {
fullSummary?: string; fullSummary?: string;
} }
export type AutomodTriggerMatchResult<TExtra extends any = unknown> = unknown extends TExtra
? BaseAutomodTriggerMatchResult
: BaseAutomodTriggerMatchResult & { extra: TExtra };
type AutomodTriggerMatchFn<TConfigType, TMatchResultExtra> = (meta: { type AutomodTriggerMatchFn<TConfigType, TMatchResultExtra> = (meta: {
ruleName: string; ruleName: string;
pluginData: GuildPluginData<AutomodPluginType>; pluginData: GuildPluginData<AutomodPluginType>;
context: AutomodContext; context: AutomodContext;
triggerConfig: TConfigType; triggerConfig: TConfigType;
}) => Awaitable<null | AutomodTriggerMatchResult<TMatchResultExtra>>; }) => Awaitable<null | undefined | AutomodTriggerMatchResult<TMatchResultExtra>>;
type AutomodTriggerRenderMatchInformationFn<TConfigType, TMatchResultExtra> = (meta: { type AutomodTriggerRenderMatchInformationFn<TConfigType, TMatchResultExtra> = (meta: {
ruleName: string; ruleName: string;

View file

@ -73,15 +73,15 @@ export const MatchAttachmentTypeTrigger = automodTrigger<MatchResultType>()({
}, },
renderMatchInformation({ pluginData, contexts, matchResult }) { renderMatchInformation({ pluginData, contexts, matchResult }) {
const channel = pluginData.guild.channels.get(contexts[0].message.channel_id); const channel = pluginData.guild.channels.get(contexts[0].message!.channel_id)!;
const prettyChannel = verboseChannelMention(channel); const prettyChannel = verboseChannelMention(channel);
return ( return (
asSingleLine(` asSingleLine(`
Matched attachment type \`${disableInlineCode(matchResult.extra.matchedType)}\` Matched attachment type \`${disableInlineCode(matchResult.extra.matchedType)}\`
(${matchResult.extra.mode === "blacklist" ? "(blacklisted)" : "(not in whitelist)"}) (${matchResult.extra.mode === "blacklist" ? "(blacklisted)" : "(not in whitelist)"})
in message (\`${contexts[0].message.id}\`) in ${prettyChannel}: in message (\`${contexts[0].message!.id}\`) in ${prettyChannel}:
`) + messageSummary(contexts[0].message) `) + messageSummary(contexts[0].message!)
); );
}, },
}); });

View file

@ -1,10 +1,10 @@
import * as t from "io-ts"; import * as t from "io-ts";
import { GuildInvite } from "eris";
import { automodTrigger } from "../helpers"; import { automodTrigger } from "../helpers";
import { import {
disableCodeBlocks, disableCodeBlocks,
disableInlineCode, disableInlineCode,
getInviteCodesInString, getInviteCodesInString,
GuildInvite,
isGuildInvite, isGuildInvite,
resolveInvite, resolveInvite,
tNullable, tNullable,

View file

@ -19,7 +19,7 @@ export const MemberJoinTrigger = automodTrigger<unknown>()({
} }
if (triggerConfig.only_new) { if (triggerConfig.only_new) {
const threshold = Date.now() - convertDelayStringToMS(triggerConfig.new_threshold); const threshold = Date.now() - convertDelayStringToMS(triggerConfig.new_threshold)!;
return context.member.createdAt >= threshold ? {} : null; return context.member.createdAt >= threshold ? {} : null;
} }
@ -27,6 +27,6 @@ export const MemberJoinTrigger = automodTrigger<unknown>()({
}, },
renderMatchInformation({ pluginData, contexts, triggerConfig }) { renderMatchInformation({ pluginData, contexts, triggerConfig }) {
return null; return "";
}, },
}); });

View file

@ -25,7 +25,7 @@ export const MemberJoinSpamTrigger = automodTrigger<unknown>()({
return {}; return {};
} }
const since = Date.now() - convertDelayStringToMS(triggerConfig.within); const since = Date.now() - convertDelayStringToMS(triggerConfig.within)!;
const matchingActions = getMatchingRecentActions(pluginData, RecentActionType.MemberJoin, null, since); const matchingActions = getMatchingRecentActions(pluginData, RecentActionType.MemberJoin, null, since);
const totalCount = sumRecentActionCounts(matchingActions); const totalCount = sumRecentActionCounts(matchingActions);
@ -46,6 +46,6 @@ export const MemberJoinSpamTrigger = automodTrigger<unknown>()({
}, },
renderMatchInformation({ pluginData, contexts, triggerConfig }) { renderMatchInformation({ pluginData, contexts, triggerConfig }) {
return null; return "";
}, },
}); });

View file

@ -9,16 +9,16 @@ interface RoleAddedMatchResult {
export const RoleAddedTrigger = automodTrigger<RoleAddedMatchResult>()({ export const RoleAddedTrigger = automodTrigger<RoleAddedMatchResult>()({
configType: t.union([t.string, t.array(t.string)]), configType: t.union([t.string, t.array(t.string)]),
defaultConfig: null, defaultConfig: [],
async match({ triggerConfig, context, pluginData }) { async match({ triggerConfig, context, pluginData }) {
if (!context.member || !context.rolesChanged || context.rolesChanged.added.length === 0) { if (!context.member || !context.rolesChanged || context.rolesChanged.added!.length === 0) {
return; return;
} }
const triggerRoles = Array.isArray(triggerConfig) ? triggerConfig : [triggerConfig]; const triggerRoles = Array.isArray(triggerConfig) ? triggerConfig : [triggerConfig];
for (const roleId of triggerRoles) { for (const roleId of triggerRoles) {
if (context.rolesChanged.added.includes(roleId)) { if (context.rolesChanged.added!.includes(roleId)) {
if (consumeIgnoredRoleChange(pluginData, context.member.id, roleId)) { if (consumeIgnoredRoleChange(pluginData, context.member.id, roleId)) {
continue; continue;
} }

View file

@ -9,10 +9,10 @@ interface RoleAddedMatchResult {
export const RoleRemovedTrigger = automodTrigger<RoleAddedMatchResult>()({ export const RoleRemovedTrigger = automodTrigger<RoleAddedMatchResult>()({
configType: t.union([t.string, t.array(t.string)]), configType: t.union([t.string, t.array(t.string)]),
defaultConfig: null, defaultConfig: [],
async match({ triggerConfig, context, pluginData }) { async match({ triggerConfig, context, pluginData }) {
if (!context.member || !context.rolesChanged || context.rolesChanged.removed.length === 0) { if (!context.member || !context.rolesChanged || context.rolesChanged.removed!.length === 0) {
return; return;
} }
@ -22,7 +22,7 @@ export const RoleRemovedTrigger = automodTrigger<RoleAddedMatchResult>()({
continue; continue;
} }
if (context.rolesChanged.removed.includes(roleId)) { if (context.rolesChanged.removed!.includes(roleId)) {
return { return {
extra: { extra: {
matchedRoleId: roleId, matchedRoleId: roleId,

View file

@ -105,13 +105,13 @@ export interface AutomodContext {
export interface RecentAction { export interface RecentAction {
type: RecentActionType; type: RecentActionType;
identifier: string; identifier: string | null;
count: number; count: number;
context: AutomodContext; context: AutomodContext;
} }
export interface RecentSpam { export interface RecentSpam {
archiveId: string; archiveId: string | null;
type: RecentActionType; type: RecentActionType;
identifiers: string[]; identifiers: string[];
timestamp: number; timestamp: number;

View file

@ -45,8 +45,9 @@ export const BotControlPlugin = zeppelinGlobalPlugin<BotControlPluginType>()("bo
pluginData.state.configs = new Configs(); pluginData.state.configs = new Configs();
pluginData.state.apiPermissionAssignments = new ApiPermissionAssignments(); pluginData.state.apiPermissionAssignments = new ApiPermissionAssignments();
if (getActiveReload()) { const activeReload = getActiveReload();
const [guildId, channelId] = getActiveReload(); if (activeReload) {
const [guildId, channelId] = activeReload;
resetActiveReload(); resetActiveReload();
const guild = pluginData.client.guilds.get(guildId); const guild = pluginData.client.guilds.get(guildId);

View file

@ -1,4 +1,4 @@
let activeReload: [string, string] = null; let activeReload: [string, string] | null = null;
export function getActiveReload() { export function getActiveReload() {
return activeReload; return activeReload;

View file

@ -19,7 +19,7 @@ export const LeaveServerCmd = botControlCmd({
return; return;
} }
const guildToLeave = pluginData.client.guilds.get(args.guildId); const guildToLeave = pluginData.client.guilds.get(args.guildId)!;
const guildName = guildToLeave.name; const guildName = guildToLeave.name;
try { try {

View file

@ -21,7 +21,7 @@ export const ServersCmd = botControlCmd({
async run({ pluginData, message: msg, args }) { async run({ pluginData, message: msg, args }) {
const showList = Boolean(args.all || args.initialized || args.uninitialized || args.search); const showList = Boolean(args.all || args.initialized || args.uninitialized || args.search);
const search = args.search && new RegExp([...args.search].map(s => escapeStringRegexp(s)).join(".*"), "i"); const search = args.search ? new RegExp([...args.search].map(s => escapeStringRegexp(s)).join(".*"), "i") : null;
const joinedGuilds = Array.from(pluginData.client.guilds.values()); const joinedGuilds = Array.from(pluginData.client.guilds.values());
const loadedGuilds = pluginData.getKnubInstance().getLoadedGuilds(); const loadedGuilds = pluginData.getKnubInstance().getLoadedGuilds();
@ -39,7 +39,7 @@ export const ServersCmd = botControlCmd({
} }
if (args.search) { if (args.search) {
filteredGuilds = filteredGuilds.filter(g => search.test(`${g.id} ${g.name}`)); filteredGuilds = filteredGuilds.filter(g => search!.test(`${g.id} ${g.name}`));
} }
if (filteredGuilds.length) { if (filteredGuilds.length) {

View file

@ -12,7 +12,7 @@ export async function createCase(pluginData: GuildPluginData<CasesPluginType>, a
const mod = await resolveUser(pluginData.client, args.modId); const mod = await resolveUser(pluginData.client, args.modId);
const modName = `${mod.username}#${mod.discriminator}`; const modName = `${mod.username}#${mod.discriminator}`;
let ppName = null; let ppName: string | null = null;
if (args.ppId) { if (args.ppId) {
const pp = await resolveUser(pluginData.client, args.ppId); const pp = await resolveUser(pluginData.client, args.ppId);
ppName = `${pp.username}#${pp.discriminator}`; ppName = `${pp.username}#${pp.discriminator}`;

View file

@ -15,7 +15,9 @@ export async function getCaseEmbed(
requestMemberId?: string, requestMemberId?: string,
): Promise<AdvancedMessageContent> { ): Promise<AdvancedMessageContent> {
const theCase = await pluginData.state.cases.with("notes").find(resolveCaseId(caseOrCaseId)); const theCase = await pluginData.state.cases.with("notes").find(resolveCaseId(caseOrCaseId));
if (!theCase) return null; if (!theCase) {
throw new Error("Unknown case");
}
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin); const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);

View file

@ -28,12 +28,13 @@ export async function getCaseSummary(
caseOrCaseId: Case | number, caseOrCaseId: Case | number,
withLinks = false, withLinks = false,
requestMemberId?: string, requestMemberId?: string,
) { ): Promise<string | null> {
const config = pluginData.config.get(); const config = pluginData.config.get();
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin); const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
const caseId = caseOrCaseId instanceof Case ? caseOrCaseId.id : caseOrCaseId; const caseId = caseOrCaseId instanceof Case ? caseOrCaseId.id : caseOrCaseId;
const theCase = await pluginData.state.cases.with("notes").find(caseId); const theCase = await pluginData.state.cases.with("notes").find(caseId);
if (!theCase) return null;
const firstNote = theCase.notes[0]; const firstNote = theCase.notes[0];
let reason = firstNote ? firstNote.body : ""; let reason = firstNote ? firstNote.body : "";
@ -47,7 +48,7 @@ export async function getCaseSummary(
if (reason.length > CASE_SUMMARY_REASON_MAX_LENGTH) { if (reason.length > CASE_SUMMARY_REASON_MAX_LENGTH) {
const match = reason.slice(CASE_SUMMARY_REASON_MAX_LENGTH, 100).match(/(?:[.,!?\s]|$)/); const match = reason.slice(CASE_SUMMARY_REASON_MAX_LENGTH, 100).match(/(?:[.,!?\s]|$)/);
const nextWhitespaceIndex = match ? CASE_SUMMARY_REASON_MAX_LENGTH + match.index : CASE_SUMMARY_REASON_MAX_LENGTH; const nextWhitespaceIndex = match ? CASE_SUMMARY_REASON_MAX_LENGTH + match.index! : CASE_SUMMARY_REASON_MAX_LENGTH;
if (nextWhitespaceIndex < reason.length) { if (nextWhitespaceIndex < reason.length) {
reason = reason.slice(0, nextWhitespaceIndex - 1) + "..."; reason = reason.slice(0, nextWhitespaceIndex - 1) + "...";
} }
@ -56,7 +57,7 @@ export async function getCaseSummary(
reason = disableLinkPreviews(reason); reason = disableLinkPreviews(reason);
const timestamp = moment.utc(theCase.created_at, DBDateFormat); const timestamp = moment.utc(theCase.created_at, DBDateFormat);
const relativeTimeCutoff = convertDelayStringToMS(config.relative_time_cutoff); const relativeTimeCutoff = convertDelayStringToMS(config.relative_time_cutoff)!;
const useRelativeTime = config.show_relative_times && Date.now() - timestamp.valueOf() < relativeTimeCutoff; const useRelativeTime = config.show_relative_times && Date.now() - timestamp.valueOf() < relativeTimeCutoff;
const timestampWithTz = requestMemberId const timestampWithTz = requestMemberId
? await timeAndDate.inMemberTz(requestMemberId, timestamp) ? await timeAndDate.inMemberTz(requestMemberId, timestamp)

View file

@ -11,13 +11,13 @@ import { logger } from "../../../logger";
export async function postToCaseLogChannel( export async function postToCaseLogChannel(
pluginData: GuildPluginData<CasesPluginType>, pluginData: GuildPluginData<CasesPluginType>,
content: MessageContent, content: MessageContent,
file: MessageFile = null, file?: MessageFile,
): Promise<Message | null> { ): Promise<Message | null> {
const caseLogChannelId = pluginData.config.get().case_log_channel; const caseLogChannelId = pluginData.config.get().case_log_channel;
if (!caseLogChannelId) return; if (!caseLogChannelId) return null;
const caseLogChannel = pluginData.guild.channels.get(caseLogChannelId); const caseLogChannel = pluginData.guild.channels.get(caseLogChannelId);
if (!caseLogChannel || !(caseLogChannel instanceof TextChannel)) return; if (!caseLogChannel || !(caseLogChannel instanceof TextChannel)) return null;
let result; let result;
try { try {
@ -30,7 +30,7 @@ export async function postToCaseLogChannel(
pluginData.state.logs.log(LogType.BOT_ALERT, { pluginData.state.logs.log(LogType.BOT_ALERT, {
body: `Missing permissions to post mod cases in <#${caseLogChannel.id}>`, body: `Missing permissions to post mod cases in <#${caseLogChannel.id}>`,
}); });
return; return null;
} }
throw e; throw e;
@ -44,17 +44,17 @@ export async function postCaseToCaseLogChannel(
caseOrCaseId: Case | number, caseOrCaseId: Case | number,
): Promise<Message | null> { ): Promise<Message | null> {
const theCase = await pluginData.state.cases.find(resolveCaseId(caseOrCaseId)); const theCase = await pluginData.state.cases.find(resolveCaseId(caseOrCaseId));
if (!theCase) return; if (!theCase) return null;
const caseEmbed = await getCaseEmbed(pluginData, caseOrCaseId); const caseEmbed = await getCaseEmbed(pluginData, caseOrCaseId);
if (!caseEmbed) return; if (!caseEmbed) return null;
if (theCase.log_message_id) { if (theCase.log_message_id) {
const [channelId, messageId] = theCase.log_message_id.split("-"); const [channelId, messageId] = theCase.log_message_id.split("-");
try { try {
await pluginData.client.editMessage(channelId, messageId, caseEmbed); await pluginData.client.editMessage(channelId, messageId, caseEmbed);
return; return null;
} catch (e) {} // tslint:disable-line:no-empty } catch (e) {} // tslint:disable-line:no-empty
} }

View file

@ -1,16 +1,9 @@
import { GuildPluginData } from "knub"; import { GuildPluginData } from "knub";
import { CensorPluginType } from "../types"; import { CensorPluginType } from "../types";
import { SavedMessage } from "../../../data/entities/SavedMessage"; import { SavedMessage } from "../../../data/entities/SavedMessage";
import { AnyInvite, Embed, GuildInvite } from "eris"; import { Embed, Invite } from "eris";
import { ZalgoRegex } from "../../../data/Zalgo"; import { ZalgoRegex } from "../../../data/Zalgo";
import { import { getInviteCodesInString, getUrlsInString, resolveMember, resolveInvite, isGuildInvite } from "../../../utils";
getInviteCodesInString,
getUrlsInString,
resolveMember,
resolveInvite,
isGuildInvite,
isRESTGuildInvite,
} from "../../../utils";
import cloneDeep from "lodash.clonedeep"; import cloneDeep from "lodash.clonedeep";
import { censorMessage } from "./censorMessage"; import { censorMessage } from "./censorMessage";
import escapeStringRegexp from "escape-string-regexp"; import escapeStringRegexp from "escape-string-regexp";
@ -59,7 +52,7 @@ export async function applyFiltersToMsg(
const inviteCodes = getInviteCodesInString(messageContent); const inviteCodes = getInviteCodesInString(messageContent);
const invites: Array<AnyInvite | null> = await Promise.all( const invites: Array<Invite | null> = await Promise.all(
inviteCodes.map(code => resolveInvite(pluginData.client, code)), inviteCodes.map(code => resolveInvite(pluginData.client, code)),
); );
@ -75,7 +68,7 @@ export async function applyFiltersToMsg(
return true; return true;
} }
if (isRESTGuildInvite(invite)) { if (isGuildInvite(invite)) {
if (inviteGuildWhitelist && !inviteGuildWhitelist.includes(invite.guild.id)) { if (inviteGuildWhitelist && !inviteGuildWhitelist.includes(invite.guild.id)) {
censorMessage( censorMessage(
pluginData, pluginData,

View file

@ -46,9 +46,9 @@ export const ArchiveChannelCmd = channelArchiverCmd({
const maxMessagesToArchive = args.messages ? Math.min(args.messages, MAX_ARCHIVED_MESSAGES) : MAX_ARCHIVED_MESSAGES; const maxMessagesToArchive = args.messages ? Math.min(args.messages, MAX_ARCHIVED_MESSAGES) : MAX_ARCHIVED_MESSAGES;
if (maxMessagesToArchive <= 0) return; if (maxMessagesToArchive <= 0) return;
const archiveLines = []; const archiveLines: string[] = [];
let archivedMessages = 0; let archivedMessages = 0;
let previousId; let previousId: string | undefined;
const startTime = Date.now(); const startTime = Date.now();
const progressMsg = await msg.channel.createMessage("Creating archive..."); const progressMsg = await msg.channel.createMessage("Creating archive...");
@ -80,7 +80,7 @@ export const ArchiveChannelCmd = channelArchiverCmd({
} }
if (message.reactions && Object.keys(message.reactions).length > 0) { if (message.reactions && Object.keys(message.reactions).length > 0) {
const reactionCounts = []; const reactionCounts: string[] = [];
for (const [emoji, info] of Object.entries(message.reactions)) { for (const [emoji, info] of Object.entries(message.reactions)) {
reactionCounts.push(`${info.count}x ${emoji}`); reactionCounts.push(`${info.count}x ${emoji}`);
} }

View file

@ -15,7 +15,8 @@ export function getCompanionChannelOptsForVoiceChannelId(
return Object.values(config.entries) return Object.values(config.entries)
.filter( .filter(
opts => opts =>
opts.voice_channel_ids.includes(voiceChannel.id) || opts.voice_channel_ids.includes(voiceChannel.parentID), opts.voice_channel_ids.includes(voiceChannel.id) ||
(voiceChannel.parentID && opts.voice_channel_ids.includes(voiceChannel.parentID)),
) )
.map(opts => Object.assign({}, defaultCompanionChannelOpts, opts)); .map(opts => Object.assign({}, defaultCompanionChannelOpts, opts));
} }

View file

@ -24,7 +24,7 @@ export async function handleCompanionPermissions(
export async function handleCompanionPermissions( export async function handleCompanionPermissions(
pluginData: GuildPluginData<CompanionChannelsPluginType>, pluginData: GuildPluginData<CompanionChannelsPluginType>,
userId: string, userId: string,
voiceChannel?: VoiceChannel, voiceChannel: VoiceChannel | null,
oldChannel?: VoiceChannel, oldChannel?: VoiceChannel,
) { ) {
if (pluginData.state.errorCooldownManager.isOnCooldown(ERROR_COOLDOWN_KEY)) { if (pluginData.state.errorCooldownManager.isOnCooldown(ERROR_COOLDOWN_KEY)) {
@ -65,7 +65,7 @@ export async function handleCompanionPermissions(
for (const channelId of permsToDelete) { for (const channelId of permsToDelete) {
const channel = pluginData.guild.channels.get(channelId); const channel = pluginData.guild.channels.get(channelId);
if (!channel || !(channel instanceof TextChannel)) continue; if (!channel || !(channel instanceof TextChannel)) continue;
await channel.deletePermission(userId, `Companion Channel for ${oldChannel.id} | User Left`); await channel.deletePermission(userId, `Companion Channel for ${oldChannel!.id} | User Left`);
} }
for (const [channelId, permissions] of permsToSet) { for (const [channelId, permissions] of permsToSet) {
@ -76,7 +76,7 @@ export async function handleCompanionPermissions(
permissions, permissions,
0, 0,
"member", "member",
`Companion Channel for ${voiceChannel.id} | User Joined`, `Companion Channel for ${voiceChannel!.id} | User Joined`,
); );
} }
} catch (e) { } catch (e) {

View file

@ -25,7 +25,7 @@ export async function addRoleAction(
const target = await resolveMember(pluginData.client, pluginData.guild, targetId); const target = await resolveMember(pluginData.client, pluginData.guild, targetId);
if (!target) throw new ActionError(`Unknown target member: ${targetId}`); if (!target) throw new ActionError(`Unknown target member: ${targetId}`);
if (event.trigger.type === "command" && !canActOn(pluginData, (eventData.msg as Message).member, target)) { if (event.trigger.type === "command" && !canActOn(pluginData, eventData.msg.member, target)) {
throw new ActionError("Missing permissions"); throw new ActionError("Missing permissions");
} }

View file

@ -25,7 +25,7 @@ export async function moveToVoiceChannelAction(
const target = await resolveMember(pluginData.client, pluginData.guild, targetId); const target = await resolveMember(pluginData.client, pluginData.guild, targetId);
if (!target) throw new ActionError("Unknown target member"); if (!target) throw new ActionError("Unknown target member");
if (event.trigger.type === "command" && !canActOn(pluginData, (eventData.msg as Message).member, target)) { if (event.trigger.type === "command" && !canActOn(pluginData, eventData.msg.member, target)) {
throw new ActionError("Missing permissions"); throw new ActionError("Missing permissions");
} }

View file

@ -10,6 +10,7 @@ import { DeleteFollowCmd, ListFollowCmd } from "./commands/ListFollowCmd";
import { ChannelJoinAlertsEvt, ChannelLeaveAlertsEvt, ChannelSwitchAlertsEvt } from "./events/SendAlertsEvts"; import { ChannelJoinAlertsEvt, ChannelLeaveAlertsEvt, ChannelSwitchAlertsEvt } from "./events/SendAlertsEvts";
import { GuildBanRemoveAlertsEvt } from "./events/BanRemoveAlertsEvt"; import { GuildBanRemoveAlertsEvt } from "./events/BanRemoveAlertsEvt";
import { trimPluginDescription } from "../../utils"; import { trimPluginDescription } from "../../utils";
import Timeout = NodeJS.Timeout;
const defaultOptions: PluginOptions<LocateUserPluginType> = { const defaultOptions: PluginOptions<LocateUserPluginType> = {
config: { config: {
@ -70,7 +71,7 @@ export const LocateUserPlugin = zeppelinGuildPlugin<LocateUserPluginType>()("loc
}, },
onUnload(pluginData) { onUnload(pluginData) {
clearTimeout(pluginData.state.outdatedAlertsTimeout); clearTimeout(pluginData.state.outdatedAlertsTimeout as Timeout);
pluginData.state.unloaded = true; pluginData.state.unloaded = true;
}, },
}); });

View file

@ -14,7 +14,6 @@ export const WhereCmd = locateUserCmd({
}, },
async run({ message: msg, args, pluginData }) { async run({ message: msg, args, pluginData }) {
const member = await resolveMember(pluginData.client, pluginData.guild, args.member.id); sendWhere(pluginData, args.member, msg.channel, `${msg.member.mention} | `);
sendWhere(pluginData, member, msg.channel, `${msg.member.mention} | `);
}, },
}); });

View file

@ -13,7 +13,7 @@ export interface LocateUserPluginType extends BasePluginType {
config: TConfigSchema; config: TConfigSchema;
state: { state: {
alerts: GuildVCAlerts; alerts: GuildVCAlerts;
outdatedAlertsTimeout: Timeout; outdatedAlertsTimeout: Timeout | null;
usersWithAlerts: string[]; usersWithAlerts: string[];
unloaded: boolean; unloaded: boolean;
}; };

View file

@ -8,6 +8,7 @@ import { moveMember } from "./moveMember";
export async function sendAlerts(pluginData: GuildPluginData<LocateUserPluginType>, userId: string) { export async function sendAlerts(pluginData: GuildPluginData<LocateUserPluginType>, userId: string) {
const triggeredAlerts = await pluginData.state.alerts.getAlertsByUserId(userId); const triggeredAlerts = await pluginData.state.alerts.getAlertsByUserId(userId);
const member = await resolveMember(pluginData.client, pluginData.guild, userId); const member = await resolveMember(pluginData.client, pluginData.guild, userId);
if (!member) return;
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`;

View file

@ -1,4 +1,4 @@
import { Member, TextableChannel, VoiceChannel } from "eris"; import { Invite, Member, TextableChannel, VoiceChannel } from "eris";
import { getInviteLink } from "knub/dist/helpers"; import { getInviteLink } from "knub/dist/helpers";
import { createOrReuseInvite } from "./createOrReuseInvite"; import { createOrReuseInvite } from "./createOrReuseInvite";
import { GuildPluginData } from "knub"; import { GuildPluginData } from "knub";
@ -11,12 +11,14 @@ export async function sendWhere(
channel: TextableChannel, channel: TextableChannel,
prepend: string, prepend: string,
) { ) {
const voice = pluginData.guild.channels.get(member.voiceState.channelID) as VoiceChannel; const voice = member.voiceState.channelID
? (pluginData.guild.channels.get(member.voiceState.channelID) as VoiceChannel)
: null;
if (voice == null) { if (voice == null) {
channel.createMessage(prepend + "That user is not in a channel"); channel.createMessage(prepend + "That user is not in a channel");
} else { } else {
let invite = null; let invite: Invite;
try { try {
invite = await createOrReuseInvite(voice); invite = await createOrReuseInvite(voice);
} catch (e) { } catch (e) {

View file

@ -28,11 +28,11 @@ export const LogsGuildMemberAddEvt = logsEvt({
cases.sort((a, b) => (a.created_at > b.created_at ? -1 : 1)); cases.sort((a, b) => (a.created_at > b.created_at ? -1 : 1));
if (cases.length) { if (cases.length) {
const recentCaseLines = []; const recentCaseLines: string[] = [];
const recentCases = cases.slice(0, 2); const recentCases = cases.slice(0, 2);
const casesPlugin = pluginData.getPlugin(CasesPlugin); const casesPlugin = pluginData.getPlugin(CasesPlugin);
for (const theCase of recentCases) { for (const theCase of recentCases) {
recentCaseLines.push(await casesPlugin.getCaseSummary(theCase)); recentCaseLines.push((await casesPlugin.getCaseSummary(theCase))!);
} }
let recentCaseSummary = recentCaseLines.join("\n"); let recentCaseSummary = recentCaseLines.join("\n");

View file

@ -14,16 +14,17 @@ import { renderTemplate, TemplateParseError } from "../../../templateFormatter";
import { logger } from "../../../logger"; import { logger } from "../../../logger";
import moment from "moment-timezone"; import moment from "moment-timezone";
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
import { MessageContent } from "eris";
export async function getLogMessage( export async function getLogMessage(
pluginData: GuildPluginData<LogsPluginType>, pluginData: GuildPluginData<LogsPluginType>,
type: LogType, type: LogType,
data: any, data: any,
opts?: Pick<TLogChannel, "format" | "timestamp_format" | "include_embed_timestamp">, opts?: Pick<TLogChannel, "format" | "timestamp_format" | "include_embed_timestamp">,
): Promise<string> { ): Promise<MessageContent | null> {
const config = pluginData.config.get(); const config = pluginData.config.get();
const format = opts?.format?.[LogType[type]] || config.format[LogType[type]] || ""; const format = opts?.format?.[LogType[type]] || config.format[LogType[type]] || "";
if (format === "" || format == null) return; if (format === "" || format == null) return null;
// See comment on FORMAT_NO_TIMESTAMP in types.ts // See comment on FORMAT_NO_TIMESTAMP in types.ts
const timestampFormat = const timestampFormat =
@ -45,7 +46,7 @@ export async function getLogMessage(
const usersOrMembers = Array.isArray(inputUserOrMember) ? inputUserOrMember : [inputUserOrMember]; const usersOrMembers = Array.isArray(inputUserOrMember) ? inputUserOrMember : [inputUserOrMember];
const mentions = []; const mentions: string[] = [];
for (const userOrMember of usersOrMembers) { for (const userOrMember of usersOrMembers) {
let user; let user;
let member; let member;
@ -91,7 +92,7 @@ export async function getLogMessage(
} catch (e) { } catch (e) {
if (e instanceof TemplateParseError) { if (e instanceof TemplateParseError) {
logger.error(`Error when parsing template:\nError: ${e.message}\nTemplate: ${format}`); logger.error(`Error when parsing template:\nError: ${e.message}\nTemplate: ${format}`);
return; return null;
} else { } else {
throw e; throw e;
} }

View file

@ -109,13 +109,13 @@ export async function log(pluginData: GuildPluginData<LogsPluginType>, type: Log
if (!pluginData.state.batches.has(channel.id)) { if (!pluginData.state.batches.has(channel.id)) {
pluginData.state.batches.set(channel.id, []); pluginData.state.batches.set(channel.id, []);
setTimeout(async () => { setTimeout(async () => {
const batchedMessage = pluginData.state.batches.get(channel.id).join("\n"); const batchedMessage = pluginData.state.batches.get(channel.id)!.join("\n");
pluginData.state.batches.delete(channel.id); pluginData.state.batches.delete(channel.id);
createChunkedMessage(channel, batchedMessage).catch(noop); createChunkedMessage(channel, batchedMessage).catch(noop);
}, batchTime); }, batchTime);
} }
pluginData.state.batches.get(channel.id).push(message); pluginData.state.batches.get(channel.id)!.push(message);
} else { } else {
// If we're not batching log messages, just send them immediately // If we're not batching log messages, just send them immediately
await createChunkedMessage(channel, message).catch(noop); await createChunkedMessage(channel, message).catch(noop);

View file

@ -7,7 +7,7 @@ export async function saveMessagesToDB(
channel: TextChannel, channel: TextChannel,
ids: string[], ids: string[],
) { ) {
const failed = []; const failed: string[] = [];
for (const id of ids) { for (const id of ids) {
const savedMessage = await pluginData.state.savedMessages.find(id); const savedMessage = await pluginData.state.savedMessages.find(id);
if (savedMessage) continue; if (savedMessage) continue;

View file

@ -65,7 +65,7 @@ export const AddCaseCmd = modActionsCmd({
modId: mod.id, modId: mod.id,
type: CaseTypes[type], type: CaseTypes[type],
reason, reason,
ppId: mod.id !== msg.author.id ? msg.author.id : null, ppId: mod.id !== msg.author.id ? msg.author.id : undefined,
}); });
if (user) { if (user) {

View file

@ -78,7 +78,7 @@ export const BanCmd = modActionsCmd({
contactMethods, contactMethods,
caseArgs: { caseArgs: {
modId: mod.id, modId: mod.id,
ppId: mod.id !== msg.author.id ? msg.author.id : null, ppId: mod.id !== msg.author.id ? msg.author.id : undefined,
}, },
deleteMessageDays, deleteMessageDays,
}); });

View file

@ -9,6 +9,7 @@ import { LogsPlugin } from "../../Logs/LogsPlugin";
import { LogType } from "../../../data/LogType"; import { LogType } from "../../../data/LogType";
import moment from "moment-timezone"; import moment from "moment-timezone";
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin"; import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin";
import { Case } from "../../../data/entities/Case";
export const DeleteCaseCmd = modActionsCmd({ export const DeleteCaseCmd = modActionsCmd({
trigger: ["delete_case", "deletecase"], trigger: ["delete_case", "deletecase"],
@ -25,8 +26,8 @@ export const DeleteCaseCmd = modActionsCmd({
}, },
async run({ pluginData, message, args }) { async run({ pluginData, message, args }) {
const failed = []; const failed: number[] = [];
const validCases = []; const validCases: Case[] = [];
let cancelled = 0; let cancelled = 0;
for (const num of args.caseNumber) { for (const num of args.caseNumber) {

View file

@ -77,7 +77,7 @@ export const ForcebanCmd = modActionsCmd({
modId: mod.id, modId: mod.id,
type: CaseTypes.Ban, type: CaseTypes.Ban,
reason, reason,
ppId: mod.id !== msg.author.id ? msg.author.id : null, ppId: mod.id !== msg.author.id ? msg.author.id : undefined,
}); });
// Confirm the action // Confirm the action

View file

@ -14,7 +14,7 @@ export const HideCaseCmd = modActionsCmd({
], ],
async run({ pluginData, message: msg, args }) { async run({ pluginData, message: msg, args }) {
const failed = []; const failed: number[] = [];
for (const num of args.caseNum) { for (const num of args.caseNum) {
const theCase = await pluginData.state.cases.findByCaseNumber(num); const theCase = await pluginData.state.cases.findByCaseNumber(num);

View file

@ -62,7 +62,7 @@ export const MassbanCmd = modActionsCmd({
const loadingMsg = await msg.channel.createMessage("Banning..."); const loadingMsg = await msg.channel.createMessage("Banning...");
// Ban each user and count failed bans (if any) // Ban each user and count failed bans (if any)
const failedBans = []; const failedBans: string[] = [];
const casesPlugin = pluginData.getPlugin(CasesPlugin); const casesPlugin = pluginData.getPlugin(CasesPlugin);
for (const userId of args.userIds) { for (const userId of args.userIds) {
try { try {

View file

@ -62,7 +62,7 @@ export const MassmuteCmd = modActionsCmd({
// Mute everyone and count fails // Mute everyone and count fails
const modId = msg.author.id; const modId = msg.author.id;
const failedMutes = []; const failedMutes: string[] = [];
const mutesPlugin = pluginData.getPlugin(MutesPlugin); const mutesPlugin = pluginData.getPlugin(MutesPlugin);
for (const userId of args.userIds) { for (const userId of args.userIds) {
try { try {

View file

@ -59,7 +59,7 @@ export const UnbanCmd = modActionsCmd({
modId: mod.id, modId: mod.id,
type: CaseTypes.Unban, type: CaseTypes.Unban,
reason, reason,
ppId: mod.id !== msg.author.id ? msg.author.id : null, ppId: mod.id !== msg.author.id ? msg.author.id : undefined,
}); });
// Confirm the action // Confirm the action

View file

@ -14,7 +14,7 @@ export const UnhideCaseCmd = modActionsCmd({
], ],
async run({ pluginData, message: msg, args }) { async run({ pluginData, message: msg, args }) {
const failed = []; const failed: number[] = [];
for (const num of args.caseNum) { for (const num of args.caseNum) {
const theCase = await pluginData.state.cases.findByCaseNumber(num); const theCase = await pluginData.state.cases.findByCaseNumber(num);

View file

@ -24,7 +24,7 @@ export const UpdateCmd = modActionsCmd({
], ],
async run({ pluginData, message: msg, args }) { async run({ pluginData, message: msg, args }) {
let theCase: Case; let theCase: Case | undefined;
if (args.caseNumber != null) { if (args.caseNumber != null) {
theCase = await pluginData.state.cases.findByCaseNumber(args.caseNumber); theCase = await pluginData.state.cases.findByCaseNumber(args.caseNumber);
} else { } else {

View file

@ -91,7 +91,7 @@ export const WarnCmd = modActionsCmd({
contactMethods, contactMethods,
caseArgs: { caseArgs: {
modId: mod.id, modId: mod.id,
ppId: mod.id !== msg.author.id ? msg.author.id : null, ppId: mod.id !== msg.author.id ? msg.author.id : undefined,
reason, reason,
}, },
retryPromptChannel: msg.channel as TextChannel, retryPromptChannel: msg.channel as TextChannel,

Some files were not shown because too many files have changed in this diff Show more