Skip to content

Commit

Permalink
Add strict & typechecked ts-eslint rules
Browse files Browse the repository at this point in the history
Signed-off-by: RedGuy12 <paul@reid-family.org>
  • Loading branch information
cobaltt7 committed Aug 18, 2023
1 parent f098e80 commit c32fd57
Show file tree
Hide file tree
Showing 60 changed files with 367 additions and 306 deletions.
56 changes: 53 additions & 3 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
/** @type {import("eslint").ESLint.ConfigData} */
module.exports = {
env: { es2021: true, node: true },
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:unicorn/all"],
extends: [
"eslint:recommended",
"plugin:unicorn/all",
"plugin:@typescript-eslint/strict-type-checked",
],
ignorePatterns: "dist",
overrides: [
{
extends: ["plugin:@typescript-eslint/disable-type-checked"],
files: ".eslintrc.cjs",
parserOptions: { sourceType: "script" },
parserOptions: { project: false, sourceType: "script" },
rules: {
"sort-keys": ["error", "asc", { caseSensitive: false, natural: true }],
"unicorn/prevent-abbreviations": "off",
Expand All @@ -15,11 +20,56 @@ module.exports = {
},
],
parser: "@typescript-eslint/parser",
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
parserOptions: {
ecmaVersion: "latest",
project: true,
sourceType: "module",
tsconfigRootDir: __dirname,
},
plugins: ["@typescript-eslint"],
rules: {
"@typescript-eslint/no-base-to-string": [
"error",
{
ignoredTypeNames: [
//todo
"AnonymousGuild",
"BaseGuildEmoji",
"CategoryChannel",
"ClientUser",
"OAuth2Guild",
"ReactionEmoji",
"DirectoryChannel",
"PublicThreadChannel",
"PrivateThreadChannel",
"Guild",
"GuildEmoji",
"GuildPreviewEmoji",
"InviteGuild",
"PartialDMChannel",
"PartialGuildMember",
"PartialMessage",
"PartialUser",
"BaseGuildTextChannel",
"BaseGuildVoiceChannel",
"ForumChannel",
"NewsChannel",
"TextChannel",
"StageChannel",
"VoiceChannel",
],
},
],
"@typescript-eslint/no-misused-promises": ["error", { checksVoidReturn: false }],
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"no-fallthrough": "off",
"no-mixed-spaces-and-tabs": "off",
"no-restricted-syntax": [
"error",
"CallExpression[callee.name='String']",
"TSIndexSignature",
],
"no-sparse-arrays": "off",
"quotes": ["error", "double", { avoidEscape: true }],
"semi": ["error", "always"],
Expand Down
8 changes: 4 additions & 4 deletions common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { ChannelType, type NonThreadGuildBasedChannel } from "discord.js";
import constants from "./constants.js";
import { client } from "strife.js";

const guild = await client.guilds.fetch(process.env.GUILD_ID ?? "");
const guild = await client.guilds.fetch(process.env.GUILD_ID);

async function getConfig() {
const channels = await guild.channels.fetch();
const roles = await guild.roles.fetch();

const latestRelease: string =
const latestRelease: string = // todo find an eslint rule
process.env.NODE_ENV == "production"
? (
await fetch(
Expand Down Expand Up @@ -79,9 +79,9 @@ async function getConfig() {
type: T | T[] = [],
matchType: "end" | "full" | "partial" | "start" = "full",
): (NonThreadGuildBasedChannel & { type: T }) | undefined {
const types = [type].flat() as ChannelType[];
const types = new Set<ChannelType>([type].flat());
return channels.find((channel): channel is typeof channel & { type: T } => {
if (!channel || !types.includes(channel.type)) return false;
if (!channel || !types.has(channel.type)) return false;

switch (matchType) {
case "full": {
Expand Down
23 changes: 12 additions & 11 deletions common/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,22 @@ import { extractMessageExtremities } from "../util/discord.js";
import logError from "./logError.js";
import { getLoggingThread } from "../modules/logging/misc.js";

let timeouts: {
[key: Snowflake]:
| { callback: () => Promise<Message<true>>; timeout: NodeJS.Timeout }
| undefined;
} = {};
let timeouts: Record<
Snowflake,
{ callback: () => Promise<Message<true>>; timeout: NodeJS.Timeout } | undefined
> = {};

export const DATABASE_THREAD = "databases";

const thread = await getLoggingThread(DATABASE_THREAD);

const databases: { [key: string]: Message<true> | undefined } = {};
const databases: Record<string, Message<true> | undefined> = {};

for (const message of (await thread.messages.fetch({ limit: 100 })).toJSON()) {
const name = message.content.split(" ")[1]?.toLowerCase();
if (name) {
databases[name] =
message.author.id === client.user?.id
message.author.id === client.user.id
? message
: message.attachments.size
? await thread.send({
Expand All @@ -41,7 +40,7 @@ for (const message of (await thread.messages.fetch({ limit: 100 })).toJSON()) {

const contructed: string[] = [];

export default class Database<Data extends { [key: string]: string | number | boolean | null }> {
export default class Database<Data extends Record<string, string | number | boolean | null>> {
message: Message<true> | undefined;

#data: ReadonlyArray<Data> | undefined;
Expand Down Expand Up @@ -138,7 +137,7 @@ export default class Database<Data extends { [key: string]: string | number | bo
};

timeouts[this.message.id] = {
timeout: setTimeout(async () => await callback(), 15_000),
timeout: setTimeout(callback, 15_000),
callback,
};
timeoutId && clearTimeout(timeoutId.timeout);
Expand Down Expand Up @@ -198,7 +197,9 @@ export default class Database<Data extends { [key: string]: string | number | bo
}

export async function cleanDatabaseListeners() {
console.log(`Cleaning ${Object.values(timeouts).length} listeners: ${Object.keys(timeouts)}`);
console.log(
`Cleaning ${Object.values(timeouts).length} listeners: ${Object.keys(timeouts).join(",")}`,
);
await Promise.all(Object.values(timeouts).map((info) => info?.callback()));
timeouts = {};
console.log("Listeners cleaned");
Expand All @@ -216,6 +217,6 @@ export async function backupDatabases(channel: TextBasedChannel) {

await channel.send("# Daily Scradd Database Backup");
while (attachments.length) {
await channel.send({ files: attachments.splice(0, 10) })
await channel.send({ files: attachments.splice(0, 10) });
}
}
9 changes: 4 additions & 5 deletions common/logError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ export default async function logError(
try {
console.error(error);

const name = `${
(error && typeof error === "object" && "name" in error && error.name) || "Error"
}`;
const name =
error && typeof error === "object" && "name" in error ? `${error.name}` : "Error";
if (["DeprecationWarning", "ExperimentalWarning"].includes(name)) return;

return await log(
Expand Down Expand Up @@ -58,12 +57,12 @@ export default async function logError(
*
* @returns The standardized error.
*/
export function generateError(error: unknown, returnObject: true): { [key: string]: unknown };
export function generateError(error: unknown, returnObject: true): Record<string, unknown>;
export function generateError(error: unknown, returnObject?: false): string;
export function generateError(
error: unknown,
returnObject = false,
): string | { [key: string]: unknown } {
): string | Record<string, unknown> {
if (typeof error === "object" && error) {
const serialized = serializeError(error);

Expand Down
37 changes: 19 additions & 18 deletions modules/automod/automod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ export default async function automodMessage(message: Message) {
config.channels.info?.id !== parentChannel?.id &&
config.channels.advertise &&
config.channels.advertise.id !== baseChannel?.id &&
!message.author?.bot
!message.author.bot
) {
const invites = (
await Promise.all(
(message.content.match(GlobalInvitesPattern) ?? []).map(async (code) => {
const invite = await client?.fetchInvite(code).catch(() => {});
const invite = await client.fetchInvite(code).catch(() => {});
return invite?.guild && !WHITELISTED_INVITE_GUILDS.has(invite.guild.id) && code;
}),
)
Expand Down Expand Up @@ -114,10 +114,10 @@ export default async function automodMessage(message: Message) {
strikes: bad.strikes + censored.strikes,
words: bad.words.map((words, index) => [
...words,
...(censored.words?.[index] ?? []),
...(censored.words[index] ?? []),
]),
},
{ strikes: 0, words: Array<string[]>(badWordRegexps.length).fill([]) },
{ strikes: 0, words: Array.from<string[]>({ length: badWordRegexps.length }).fill([]) },
);
const badEmbedWords = message.embeds
.flatMap((embed) => [
Expand All @@ -135,30 +135,31 @@ export default async function automodMessage(message: Message) {
strikes: bad.strikes + censored.strikes,
words: bad.words.map((words, index) => [
...words,
...(censored.words?.[index] ?? []),
...(censored.words[index] ?? []),
]),
}
: bad;
},
{
strikes: 0,
words: Array<string[]>(badWordRegexps.length).fill([]),
words: Array.from<string[]>({ length: badWordRegexps.length }).fill([]),
},
);

const languageStrikes = badWords.strikes + badEmbedWords.strikes;

if (badWords.strikes || needsDelete) {
if (!message.deletable)
log(`${LoggingErrorEmoji} Missing permissions to delete ${message.url}`);

message.delete();
if (badWords.strikes)
await message.channel.send(
`${constants.emojis.statuses.no} ${message.author.toString()}, ${
languageStrikes < 1 ? "that’s not appropriate" : "language"
}!`,
);
if (message.deletable) {
await message.delete();
if (badWords.strikes)
await message.channel.send(
`${constants.emojis.statuses.no} ${message.author.toString()}, ${
languageStrikes < 1 ? "that’s not appropriate" : "language"
}!`,
);
} else {
await log(`${LoggingErrorEmoji} Missing permissions to delete ${message.url}`);
}
} else if (badEmbedWords.strikes) {
await message.suppressEmbeds();
await message.reply(
Expand All @@ -174,8 +175,8 @@ export default async function automodMessage(message: Message) {
"Watch your language!",
languageStrikes,
`Sent message with words: ${[
...(badEmbedWords.words.flat() ?? []),
...(badWords.words.flat() ?? []),
...badEmbedWords.words.flat(),
...badWords.words.flat(),
].join(", ")}`,
);
}
Expand Down
2 changes: 1 addition & 1 deletion modules/automod/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function censorOptions(options: readonly CommandInteractionOption[]): {

for (const option of options) {
const censoredValue = (option.value === "string" && censor(option.value)) || undefined;
const censoredOptions = (option.options && censorOptions(option.options)) || undefined;
const censoredOptions = option.options && censorOptions(option.options);

strikes += (censoredValue?.strikes ?? 0) + (censoredOptions?.strikes ?? 0);
words.push(
Expand Down
9 changes: 5 additions & 4 deletions modules/automod/nicknames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ export default async function changeNickname(member: GuildMember) {

if (newNick !== member.displayName) {
const unpingable = isPingable(member.displayName);
return await setNickname(
await setNickname(
member,
newNick,
`${censored ? "Has bad words" : ""}${censored && unpingable ? "; " : ""}${
unpingable ? "Unpingable" : ""
}`,
);
return;
}

const members = (await config.guild.members.fetch({ query: newNick, limit: 100 })).filter(
Expand All @@ -41,7 +42,7 @@ export default async function changeNickname(member: GuildMember) {
const nick = censored ? censored.censored : found.user.displayName;

if (nick !== found.displayName && isPingable(nick)) {
setNickname(found, nick, "Conflicts");
await setNickname(found, nick, "Conflicts");
unsafe.delete(id);
}
}
Expand All @@ -55,7 +56,7 @@ export default async function changeNickname(member: GuildMember) {
const nick = censored ? censored.censored : member.user.displayName;

if (nick !== newNick && isPingable(nick)) {
setNickname(member, nick, "Conflicts");
await setNickname(member, nick, "Conflicts");
unchanged.delete(member.id);
}
}
Expand All @@ -65,7 +66,7 @@ export default async function changeNickname(member: GuildMember) {
const nick = censored ? censored.censored : member.user.username;

if (nick !== member.displayName && isPingable(nick)) {
setNickname(member, nick, "Conflicts");
await setNickname(member, nick, "Conflicts");
unchanged.delete(member.id);
}
}
Expand Down
5 changes: 2 additions & 3 deletions modules/board/explore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
ComponentType,
type InteractionReplyOptions,
ButtonInteraction,
type CacheType,
ChatInputCommandInteraction,
type MessageEditOptions,
} from "discord.js";
Expand Down Expand Up @@ -67,7 +66,7 @@ async function textChannelMatches(
}

export default async function makeSlideshow(
interaction: ChatInputCommandInteraction<"cached" | "raw"> | ButtonInteraction<CacheType>,
interaction: ChatInputCommandInteraction<"cached" | "raw"> | ButtonInteraction,
{
minReactions = defaultMinReactions,
user,
Expand Down Expand Up @@ -185,7 +184,7 @@ export default async function makeSlideshow(
.on("end", async () => {
await reply.edit({
allowedMentions: { users: [] },
components: disableComponents(reply?.components ?? []),
components: disableComponents(reply.components),
});
});
}
2 changes: 1 addition & 1 deletion modules/board/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export async function generateBoardMessage(
info.onBoard && (await config.channels.board?.messages.fetch(info.onBoard).catch(() => {}));

if (onBoard) {
const linkButton = onBoard.components?.[0]?.components?.[0];
const linkButton = onBoard.components[0]?.components?.[0];
const buttons =
linkButton?.type === ComponentType.Button
? [...(extraButtons.pre || []), linkButton.toJSON(), ...(extraButtons.post || [])]
Expand Down
2 changes: 1 addition & 1 deletion modules/board/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default async function updateBoard(message: Message) {
if (count < Math.floor(minReactions * 0.8)) {
await boardMessage.delete();
} else {
const content = boardMessage.content.replace(/\d+/, String(count));
const content = boardMessage.content.replace(/\d+/, count.toString());
await boardMessage.edit(content);
}
} else if (count >= minReactions) {
Expand Down
3 changes: 1 addition & 2 deletions modules/bot/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { unifiedDiff } from "difflib";
import {
ComponentType,
MessageContextMenuCommandInteraction,
type MessageEditOptions,
MessageType,
ModalSubmitInteraction,
TextInputStyle,
Expand Down Expand Up @@ -73,7 +72,7 @@ export async function submitEdit(interaction: ModalSubmitInteraction, id?: strin
const text =
interaction.fields.getTextInputValue("json1") +
interaction.fields.getTextInputValue("json2");
const json = await new Promise<MessageEditOptions>((resolve) => {
const json = await new Promise((resolve) => {
resolve(JSON.parse(text));
}).catch(async (error: unknown) => {
await interaction.reply({
Expand Down
Loading

0 comments on commit c32fd57

Please sign in to comment.