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;
}
const fn = this.queue.shift();
const fn = this.queue.shift()!;
new Promise(resolve => {
// Either fn() completes or the timeout is reached
fn().then(resolve);

View file

@ -16,7 +16,7 @@ export class QueuedEventEmitter {
this.listeners.set(eventName, []);
}
this.listeners.get(eventName).push(listener);
this.listeners.get(eventName)!.push(listener);
return listener;
}
@ -25,7 +25,7 @@ export class QueuedEventEmitter {
return;
}
const listeners = this.listeners.get(eventName);
const listeners = this.listeners.get(eventName)!;
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.
*/
export class RegExpRunner extends EventEmitter {
private _worker: RegExpWorker;
private _failedTimesInterval: Timeout;
private _worker: RegExpWorker | null;
private readonly _failedTimesInterval: Timeout;
private cooldown: CooldownManager;
private failedTimes: Map<string, number>;
@ -90,13 +90,13 @@ export class RegExpRunner extends EventEmitter {
if (isTimeoutError(e)) {
if (this.failedTimes.has(regex.source)) {
// 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 {
// This is the first time this regex failed, init fail counter
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
this.cooldown.setCooldown(regex.source, REGEX_FAIL_COOLDOWN);
this.failedTimes.delete(regex.source);

View file

@ -13,7 +13,10 @@ export class SimpleCache<T = any> {
constructor(retentionTime: number, maxItems?: number) {
this.retentionTime = retentionTime;
this.maxItems = maxItems;
if (maxItems) {
this.maxItems = maxItems;
}
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);
if (!info) return null;

View file

@ -147,7 +147,7 @@ export function initAuth(app: express.Express) {
res.json({ valid: true });
});
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);
});
}

View file

@ -19,12 +19,12 @@ export function initGuildsAPI(app: express.Express) {
guildRouter.use(...apiTokenAuthHandlers());
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);
});
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);
}
@ -34,7 +34,7 @@ export function initGuildsAPI(app: express.Express) {
guildRouter.post("/:guildId/check-permission", async (req: Request, res: Response) => {
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 });
});
@ -54,7 +54,7 @@ export function initGuildsAPI(app: express.Express) {
config = config.trim() + "\n"; // Normalize start/end whitespace in the config
const currentConfig = await configs.getActiveByKey(`guild-${req.params.guildId}`);
if (config === currentConfig.config) {
if (currentConfig && config === currentConfig.config) {
return ok(res);
}
@ -81,7 +81,7 @@ export function initGuildsAPI(app: express.Express) {
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);
});

View file

@ -24,7 +24,7 @@ export const hasGuildPermission = async (userId: string, guildId: string, permis
*/
export function requireGuildPermission(permission: ApiPermissions) {
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);
}

View file

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

View file

@ -48,7 +48,9 @@ export const commandTypes = {
},
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);
if (result == null) {
@ -110,7 +112,7 @@ export const commandTypeHelpers = {
delay: createTypeHelper<number>(commandTypes.delay),
resolvedUser: createTypeHelper<Promise<User>>(commandTypes.resolvedUser),
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),
anyId: createTypeHelper<Promise<string>>(commandTypes.anyId),
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}`;
}
const plugin = pluginNameToPlugin.get(pluginName);
const plugin = pluginNameToPlugin.get(pluginName)!;
try {
const mergedOptions = configUtils.mergeConfig(plugin.defaultOptions || {}, pluginOptions);
await plugin.configPreprocessor(mergedOptions as PluginOptions<any>);
await plugin.configPreprocessor?.(mergedOptions as PluginOptions<any>);
} catch (err) {
if (err instanceof ConfigValidationError || err instanceof StrictValidationError) {
return `${pluginName}: ${err.message}`;

View file

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

View file

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

View file

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

View file

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

View file

@ -96,7 +96,7 @@ export class GuildSavedMessages extends BaseGuildRepository {
.getMany();
}
getUserMessagesByChannelAfterId(userId, channelId, afterId, limit = null) {
getUserMessagesByChannelAfterId(userId, channelId, afterId, limit?: number) {
let query = this.messages
.createQueryBuilder()
.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 onceEventListener;
let timeoutFn;
const callHandler = async (msg: SavedMessage) => {
const callHandler = async (msg?: SavedMessage) => {
this.events.off(`create:${id}`, onceEventListener);
clearTimeout(timeoutFn);
@ -259,7 +259,7 @@ export class GuildSavedMessages extends BaseGuildRepository {
onceEventListener = this.events.once(`create:${id}`, callHandler);
timeoutFn = setTimeout(() => {
called = true;
callHandler(null);
callHandler(undefined);
}, timeout);
const messageInDB = await this.find(id);

View file

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

View file

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

View file

@ -17,5 +17,5 @@ export class ArchiveEntry {
@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() 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() audit_log_id: string;
@Column({ nullable: true }) audit_log_id: string | null;
@Column() created_at: string;
@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.
* Format: "channelid-messageid"
*/
@Column() log_message_id: string;
@Column({ nullable: true }) log_message_id: string | null;
@OneToMany(
type => CaseNote,

View file

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

View file

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

View file

@ -20,16 +20,16 @@ export class ScheduledPost {
@Column("simple-json") attachments: Attachment[];
@Column() post_at: string;
@Column({ nullable: true }) post_at: string | null;
/**
* 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;
}

View file

@ -212,13 +212,13 @@ connect().then(async () => {
sendSuccessMessageFn(channel, body) {
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));
},
sendErrorMessageFn(channel, body) {
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));
},
},

View file

@ -19,8 +19,8 @@ export class MigrateUsernamesToNewHistoryTable1556909512501 implements Migration
const migrateNextBatch = (): Promise<{ finished: boolean; migrated?: number }> => {
return new Promise(async resolve => {
const toInsert = [];
const toDelete = [];
const toInsert: any[][] = [];
const toDelete: number[] = [];
const stream = await queryRunner.stream(
`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 {
public async up(queryRunner: QueryRunner): Promise<any> {
const table = await queryRunner.getTable("vc_alerts");
const table = (await queryRunner.getTable("vc_alerts"))!;
await table.addIndex(
new TableIndex({
columnNames: ["requestor_id"],
@ -16,7 +16,7 @@ export class AddMoreIndicesToVCAlerts1562838838927 implements MigrationInterface
}
public async down(queryRunner: QueryRunner): Promise<any> {
const table = await queryRunner.getTable("vc_alerts");
const table = (await queryRunner.getTable("vc_alerts"))!;
await table.removeIndex(
new TableIndex({
columnNames: ["requestor_id"],

View file

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

View file

@ -3,7 +3,7 @@ import pkgUp from "pkg-up";
const backendPackageJson = pkgUp.sync({
cwd: __dirname,
});
}) as string;
export const backendDir = path.dirname(backendPackageJson);
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);
}
const PluginOverrideCriteriaType = t.recursion("PluginOverrideCriteriaType", () =>
t.type({
channel: tNullable(t.union([t.string, t.array(t.string)])),
category: 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)])),
const PluginOverrideCriteriaType: t.Type<PluginOverrideCriteria<unknown>> = t.recursion(
"PluginOverrideCriteriaType",
() =>
t.partial({
channel: tNullable(t.union([t.string, t.array(t.string)])),
category: 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)),
any: tNullable(t.array(PluginOverrideCriteriaType)),
not: tNullable(PluginOverrideCriteriaType),
all: tNullable(t.array(PluginOverrideCriteriaType)),
any: tNullable(t.array(PluginOverrideCriteriaType)),
not: tNullable(PluginOverrideCriteriaType),
extra: t.unknown,
}),
extra: t.unknown,
}),
);
const BasicPluginStructureType = t.type({
enabled: tNullable(t.boolean),
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),
});
@ -101,7 +103,7 @@ export function getPluginConfigPreprocessor(
// 4. Merge with default options and validate/decode the entire config
let decodedConfig = {};
const decodedOverrides = [];
const decodedOverrides: Array<PluginOverrideCriteria<unknown> & { config: any }> = [];
if (options.config) {
decodedConfig = blueprint.configSchema

View file

@ -4,6 +4,7 @@ import { tDelayString, MINUTES } from "../../utils";
import { GuildLogs } from "../../data/GuildLogs";
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
import { SavedMessage } from "../../data/entities/SavedMessage";
import Timeout = NodeJS.Timeout;
export const MAX_DELAY = 5 * MINUTES;
@ -25,8 +26,8 @@ export interface AutoDeletePluginType extends BasePluginType {
guildLogs: GuildLogs;
deletionQueue: IDeletionQueueItem[];
nextDeletion: number;
nextDeletionTimeout;
nextDeletion: number | null;
nextDeletionTimeout: Timeout | null;
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 config = pluginData.config.getMatchingConfig({ member, channelId: msg.channel_id });
if (config.enabled) {
let delay = convertDelayStringToMS(config.delay);
let delay = convertDelayStringToMS(config.delay)!;
if (delay > MAX_DELAY) {
delay = MAX_DELAY;

View file

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

View file

@ -19,7 +19,7 @@ export const AddReactionsEvt = autoReactionsEvt({
const autoReaction = await pluginData.state.autoReactions.getForChannel(message.channel.id);
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(
me,
message.channel as GuildChannel,

View file

@ -158,7 +158,7 @@ export const AutomodPlugin = zeppelinGuildPlugin<AutomodPluginType>()("automod",
configPreprocessor,
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: [

View file

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

View file

@ -1,7 +1,7 @@
import * as t from "io-ts";
import { automodAction } from "../helpers";
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 { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
@ -20,14 +20,14 @@ export const BanAction = automodAction({
async apply({ pluginData, contexts, actionConfig, matchResult }) {
const reason = actionConfig.reason || "Kicked automatically";
const contactMethods = resolveActionContactMethods(pluginData, actionConfig);
const deleteMessageDays = actionConfig.deleteMessageDays;
const deleteMessageDays = actionConfig.deleteMessageDays || undefined;
const caseArgs = {
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);
for (const userId of userIdsToBan) {

View file

@ -2,7 +2,7 @@ import * as t from "io-ts";
import { automodAction } from "../helpers";
import { LogType } from "../../../data/LogType";
import { LogsPlugin } from "../../Logs/LogsPlugin";
import { unique } from "../../../utils";
import { nonNullish, unique } from "../../../utils";
export const ChangeNicknameAction = automodAction({
configType: t.union([
@ -15,7 +15,7 @@ export const ChangeNicknameAction = automodAction({
defaultConfig: {},
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) {
if (pluginData.state.recentNicknameChanges.has(member.id)) continue;

View file

@ -15,12 +15,12 @@ export const CleanAction = automodAction({
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}`);
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 { automodAction } from "../helpers";
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 { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
@ -22,14 +22,15 @@ export const KickAction = automodAction({
const caseArgs = {
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 modActions = pluginData.getPlugin(ModActionsPlugin);
for (const member of membersToKick) {
if (!member) continue;
await modActions.kickMember(member, reason, { contactMethods, caseArgs });
}
},

View file

@ -1,7 +1,15 @@
import * as t from "io-ts";
import { automodAction } from "../helpers";
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 { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
import { MutesPlugin } from "../../Mutes/MutesPlugin";
@ -21,16 +29,16 @@ export const MuteAction = automodAction({
},
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 contactMethods = resolveActionContactMethods(pluginData, actionConfig);
const caseArgs = {
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);
for (const userId of userIdsToMute) {

View file

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

View file

@ -28,14 +28,14 @@ export const ReplyAction = automodAction({
async apply({ pluginData, contexts, actionConfig }) {
const contextsWithTextChannels = contexts
.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) => {
if (!map.has(context.message.channel_id)) {
map.set(context.message.channel_id, []);
if (!map.has(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;
}, new Map());
@ -57,7 +57,7 @@ export const ReplyAction = automodAction({
const replyMsg = await channel.createMessage(formatted);
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);
}
}

View file

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

View file

@ -1,7 +1,7 @@
import * as t from "io-ts";
import { automodAction } from "../helpers";
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 { ModActionsPlugin } from "../../ModActions/ModActionsPlugin";
@ -22,14 +22,15 @@ export const WarnAction = automodAction({
const caseArgs = {
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 modActions = pluginData.getPlugin(ModActionsPlugin);
for (const member of membersToWarn) {
if (!member) continue;
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";
export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) {
const globalIdentifier = context.message.user_id;
const perChannelIdentifier = `${context.message.channel_id}-${context.message.user_id}`;
const message = context.message!;
const globalIdentifier = message.user_id;
const perChannelIdentifier = `${message.channel_id}-${message.user_id}`;
const expiresAt = Date.now() + RECENT_ACTION_EXPIRY_TIME;
pluginData.state.recentActions.push({
@ -23,8 +24,7 @@ export function addRecentActionsFromMessage(pluginData: GuildPluginData<AutomodP
});
const mentionCount =
getUserMentions(context.message.data.content || "").length +
getRoleMentions(context.message.data.content || "").length;
getUserMentions(message.data.content || "").length + getRoleMentions(message.data.content || "").length;
if (mentionCount) {
pluginData.state.recentActions.push({
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) {
pluginData.state.recentActions.push({
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) {
pluginData.state.recentActions.push({
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) {
pluginData.state.recentActions.push({
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)
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) {
pluginData.state.recentActions.push({
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) {
pluginData.state.recentActions.push({
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) {
pluginData.state.recentActions.push({
context,

View file

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

View file

@ -7,6 +7,7 @@ import { findRecentSpam } from "./findRecentSpam";
import { getMatchingMessageRecentActions } from "./getMatchingMessageRecentActions";
import * as t from "io-ts";
import { getMessageSpamIdentifier } from "./getSpamIdentifier";
import { SavedMessage } from "../../../data/entities/SavedMessage";
const MessageSpamTriggerConfig = t.type({
amount: t.number,
@ -29,22 +30,25 @@ export function createMessageSpamTrigger(spamType: RecentActionType, prettyName:
return;
}
const spamIdentifier = getMessageSpamIdentifier(context.message, triggerConfig.per_channel);
const spamIdentifier = getMessageSpamIdentifier(context.message, Boolean(triggerConfig.per_channel));
const recentSpam = findRecentSpam(pluginData, spamType, spamIdentifier);
if (recentSpam) {
await pluginData.state.archives.addSavedMessagesToArchive(
recentSpam.archiveId,
[context.message],
pluginData.guild,
);
if (recentSpam.archiveId) {
await pluginData.state.archives.addSavedMessagesToArchive(
recentSpam.archiveId,
[context.message],
pluginData.guild,
);
}
return {
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(
pluginData,
context.message,
@ -58,7 +62,7 @@ export function createMessageSpamTrigger(spamType: RecentActionType, prettyName:
const messages = matchedSpam.recentActions
.map(action => action.context.message)
.filter(Boolean)
.sort(sorter("posted_at"));
.sort(sorter("posted_at")) as SavedMessage[];
const archiveId = await pluginData.state.archives.createFromSavedMessages(messages, pluginData.guild);

View file

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

View file

@ -10,23 +10,25 @@ export function getTextMatchPartialSummary(
context: AutomodContext,
) {
if (type === "message") {
const channel = pluginData.guild.channels.get(context.message.channel_id);
const channelMention = channel ? verboseChannelMention(channel) : `\`#${context.message.channel_id}\``;
const message = context.message!;
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") {
const channel = pluginData.guild.channels.get(context.message.channel_id);
const channelMention = channel ? verboseChannelMention(channel) : `\`#${context.message.channel_id}\``;
const message = context.message!;
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") {
return `username: ${context.user.username}`;
return `username: ${context.user!.username}`;
} else if (type === "nickname") {
return `nickname: ${context.member.nick}`;
return `nickname: ${context.member!.nick}`;
} else if (type === "visiblename") {
const visibleName = context.member?.nick || context.user.username;
const visibleName = context.member?.nick || context.user!.username;
return `visible name: ${visibleName}`;
} 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(
pluginData: GuildPluginData<AutomodPluginType>,
actionConfig: {
notify?: string;
notifyChannel?: string;
notify?: string | null;
notifyChannel?: string | null;
},
): UserNotificationMethod[] | null {
): UserNotificationMethod[] {
if (actionConfig.notify === "dm") {
return [{ type: "dm" }];
} else if (actionConfig.notify === "channel") {
@ -28,5 +28,5 @@ export function resolveActionContactMethods(
return [];
}
return null;
return [];
}

View file

@ -5,13 +5,14 @@ import { availableActions } from "../actions/availableActions";
import { AutomodTriggerMatchResult } from "../helpers";
import { CleanAction } from "../actions/clean";
import { checkAndUpdateCooldown } from "./checkAndUpdateCooldown";
import { TextChannel } from "eris";
export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>, context: AutomodContext) {
const userId = context.user?.id || context.member?.id || context.message?.user_id;
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 channel = channelId && pluginData.guild.channels.get(channelId);
const channel = channelId ? (pluginData.guild.channels.get(channelId) as TextChannel) : null;
const categoryId = channel?.parentID;
const config = pluginData.config.getMatchingConfig({
@ -29,8 +30,8 @@ export async function runAutomod(pluginData: GuildPluginData<AutomodPluginType>,
return;
}
let matchResult: AutomodTriggerMatchResult<any>;
let contexts: AutomodContext[];
let matchResult: AutomodTriggerMatchResult<any> | null | undefined;
let contexts: AutomodContext[] = [];
triggerLoop: for (const triggerItem of rule.triggers) {
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 { AutomodContext, AutomodPluginType } from "./types";
export interface AutomodTriggerMatchResult<TExtra extends any = unknown> {
interface BaseAutomodTriggerMatchResult {
extraContexts?: AutomodContext[];
extra?: TExtra;
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;
}
export type AutomodTriggerMatchResult<TExtra extends any = unknown> = unknown extends TExtra
? BaseAutomodTriggerMatchResult
: BaseAutomodTriggerMatchResult & { extra: TExtra };
type AutomodTriggerMatchFn<TConfigType, TMatchResultExtra> = (meta: {
ruleName: string;
pluginData: GuildPluginData<AutomodPluginType>;
context: AutomodContext;
triggerConfig: TConfigType;
}) => Awaitable<null | AutomodTriggerMatchResult<TMatchResultExtra>>;
}) => Awaitable<null | undefined | AutomodTriggerMatchResult<TMatchResultExtra>>;
type AutomodTriggerRenderMatchInformationFn<TConfigType, TMatchResultExtra> = (meta: {
ruleName: string;

View file

@ -73,15 +73,15 @@ export const MatchAttachmentTypeTrigger = automodTrigger<MatchResultType>()({
},
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);
return (
asSingleLine(`
Matched attachment type \`${disableInlineCode(matchResult.extra.matchedType)}\`
(${matchResult.extra.mode === "blacklist" ? "(blacklisted)" : "(not in whitelist)"})
in message (\`${contexts[0].message.id}\`) in ${prettyChannel}:
`) + messageSummary(contexts[0].message)
in message (\`${contexts[0].message!.id}\`) in ${prettyChannel}:
`) + messageSummary(contexts[0].message!)
);
},
});

View file

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

View file

@ -19,7 +19,7 @@ export const MemberJoinTrigger = automodTrigger<unknown>()({
}
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;
}
@ -27,6 +27,6 @@ export const MemberJoinTrigger = automodTrigger<unknown>()({
},
renderMatchInformation({ pluginData, contexts, triggerConfig }) {
return null;
return "";
},
});

View file

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

View file

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

View file

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

View file

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

View file

@ -45,8 +45,9 @@ export const BotControlPlugin = zeppelinGlobalPlugin<BotControlPluginType>()("bo
pluginData.state.configs = new Configs();
pluginData.state.apiPermissionAssignments = new ApiPermissionAssignments();
if (getActiveReload()) {
const [guildId, channelId] = getActiveReload();
const activeReload = getActiveReload();
if (activeReload) {
const [guildId, channelId] = activeReload;
resetActiveReload();
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() {
return activeReload;

View file

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

View file

@ -21,7 +21,7 @@ export const ServersCmd = botControlCmd({
async run({ pluginData, message: msg, args }) {
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 loadedGuilds = pluginData.getKnubInstance().getLoadedGuilds();
@ -39,7 +39,7 @@ export const ServersCmd = botControlCmd({
}
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) {

View file

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

View file

@ -15,7 +15,9 @@ export async function getCaseEmbed(
requestMemberId?: string,
): Promise<AdvancedMessageContent> {
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);

View file

@ -28,12 +28,13 @@ export async function getCaseSummary(
caseOrCaseId: Case | number,
withLinks = false,
requestMemberId?: string,
) {
): Promise<string | null> {
const config = pluginData.config.get();
const timeAndDate = pluginData.getPlugin(TimeAndDatePlugin);
const caseId = caseOrCaseId instanceof Case ? caseOrCaseId.id : caseOrCaseId;
const theCase = await pluginData.state.cases.with("notes").find(caseId);
if (!theCase) return null;
const firstNote = theCase.notes[0];
let reason = firstNote ? firstNote.body : "";
@ -47,7 +48,7 @@ export async function getCaseSummary(
if (reason.length > CASE_SUMMARY_REASON_MAX_LENGTH) {
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) {
reason = reason.slice(0, nextWhitespaceIndex - 1) + "...";
}
@ -56,7 +57,7 @@ export async function getCaseSummary(
reason = disableLinkPreviews(reason);
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 timestampWithTz = requestMemberId
? await timeAndDate.inMemberTz(requestMemberId, timestamp)

View file

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

View file

@ -1,16 +1,9 @@
import { GuildPluginData } from "knub";
import { CensorPluginType } from "../types";
import { SavedMessage } from "../../../data/entities/SavedMessage";
import { AnyInvite, Embed, GuildInvite } from "eris";
import { Embed, Invite } from "eris";
import { ZalgoRegex } from "../../../data/Zalgo";
import {
getInviteCodesInString,
getUrlsInString,
resolveMember,
resolveInvite,
isGuildInvite,
isRESTGuildInvite,
} from "../../../utils";
import { getInviteCodesInString, getUrlsInString, resolveMember, resolveInvite, isGuildInvite } from "../../../utils";
import cloneDeep from "lodash.clonedeep";
import { censorMessage } from "./censorMessage";
import escapeStringRegexp from "escape-string-regexp";
@ -59,7 +52,7 @@ export async function applyFiltersToMsg(
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)),
);
@ -75,7 +68,7 @@ export async function applyFiltersToMsg(
return true;
}
if (isRESTGuildInvite(invite)) {
if (isGuildInvite(invite)) {
if (inviteGuildWhitelist && !inviteGuildWhitelist.includes(invite.guild.id)) {
censorMessage(
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;
if (maxMessagesToArchive <= 0) return;
const archiveLines = [];
const archiveLines: string[] = [];
let archivedMessages = 0;
let previousId;
let previousId: string | undefined;
const startTime = Date.now();
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) {
const reactionCounts = [];
const reactionCounts: string[] = [];
for (const [emoji, info] of Object.entries(message.reactions)) {
reactionCounts.push(`${info.count}x ${emoji}`);
}

View file

@ -15,7 +15,8 @@ export function getCompanionChannelOptsForVoiceChannelId(
return Object.values(config.entries)
.filter(
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));
}

View file

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

View file

@ -25,7 +25,7 @@ export async function addRoleAction(
const target = await resolveMember(pluginData.client, pluginData.guild, 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");
}

View file

@ -25,7 +25,7 @@ export async function moveToVoiceChannelAction(
const target = await resolveMember(pluginData.client, pluginData.guild, targetId);
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");
}

View file

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

View file

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

View file

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

View file

@ -8,6 +8,7 @@ import { moveMember } from "./moveMember";
export async function sendAlerts(pluginData: GuildPluginData<LocateUserPluginType>, userId: string) {
const triggeredAlerts = await pluginData.state.alerts.getAlertsByUserId(userId);
const member = await resolveMember(pluginData.client, pluginData.guild, userId);
if (!member) return;
triggeredAlerts.forEach(alert => {
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 { createOrReuseInvite } from "./createOrReuseInvite";
import { GuildPluginData } from "knub";
@ -11,12 +11,14 @@ export async function sendWhere(
channel: TextableChannel,
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) {
channel.createMessage(prepend + "That user is not in a channel");
} else {
let invite = null;
let invite: Invite;
try {
invite = await createOrReuseInvite(voice);
} catch (e) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,7 +14,7 @@ export const HideCaseCmd = modActionsCmd({
],
async run({ pluginData, message: msg, args }) {
const failed = [];
const failed: number[] = [];
for (const num of args.caseNum) {
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...");
// Ban each user and count failed bans (if any)
const failedBans = [];
const failedBans: string[] = [];
const casesPlugin = pluginData.getPlugin(CasesPlugin);
for (const userId of args.userIds) {
try {

View file

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

View file

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

View file

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

View file

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

View file

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

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