diff --git a/src/events/message/messageCreate.ts b/src/events/message/messageCreate.ts index 2ccdd20..6ad95ca 100644 --- a/src/events/message/messageCreate.ts +++ b/src/events/message/messageCreate.ts @@ -2,6 +2,7 @@ import { Client, EmbedBuilder, Message, TextBasedChannel } from "discord.js"; import { getLocale } from "../../utils/locales"; import { isImage, userWithNickname } from "../../utils/misc"; import { showDate } from "../../utils/time"; +import { RegexC, RegExpFlags } from "../../utils/regex"; /** https://discord.js.org/#/docs/discord.js/main/class/Client?scrollTo=e-messageCreate */ export default async (message: Message, client: Client) => { @@ -24,7 +25,7 @@ export default async (message: Message, client: Client) => { /* Citation */ const regex = /https:\/\/(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(\d{17,19})\/(\d{17,19})\/(\d{17,19})/g; - const urls = message.content.match(new RegExp(regex, "g")); + const urls = message.content.match(RegexC(regex, RegExpFlags.Global)); // Ignore message if there is no URLs if (!urls) { @@ -42,7 +43,7 @@ export default async (message: Message, client: Client) => { }[] = [], match, ) => { - const [, guild_id, channel_id, message_id] = new RegExp(regex).exec( + const [, guild_id, channel_id, message_id] = RegexC(regex).exec( match, ) as RegExpExecArray; @@ -178,7 +179,7 @@ export default async (message: Message, client: Client) => { // Delete source message if no content when removing links if ( - !message.content.replace(new RegExp(regex, "g"), "").trim() && + !message.content.replace(RegexC(regex, RegExpFlags.Global), "").trim() && messages.length === urls.length && !message.mentions.repliedUser && message.channel.isSendable() diff --git a/src/tests/utils/regex.test.ts b/src/tests/utils/regex.test.ts new file mode 100644 index 0000000..8f12cdf --- /dev/null +++ b/src/tests/utils/regex.test.ts @@ -0,0 +1,26 @@ +import { RegexC, RegExpFlags } from "../../utils/regex"; + +describe("Regex flags", () => { + test("One parameter", () => { + const regex = RegexC("", RegExpFlags.Global); + expect(regex.global).toBeTruthy(); + }); + + test("All parameters", () => { + const regex = RegexC( + "", + RegExpFlags.Global | + RegExpFlags.MultiLine | + RegExpFlags.Insensitive | + RegExpFlags.Sticky | + RegExpFlags.Unicode | + RegExpFlags.SingleLine, + ); + expect(regex.global).toBeTruthy(); + expect(regex.multiline).toBeTruthy(); + expect(regex.ignoreCase).toBeTruthy(); + expect(regex.sticky).toBeTruthy(); + expect(regex.unicode).toBeTruthy(); + expect(regex.dotAll).toBeTruthy(); + }); +}); diff --git a/src/utils/regex.ts b/src/utils/regex.ts new file mode 100644 index 0000000..436a24d --- /dev/null +++ b/src/utils/regex.ts @@ -0,0 +1,30 @@ +export enum RegExpFlags { + // Global + Global = 1 << 0, + // Multi Line + MultiLine = 1 << 1, + // Ignore Case + Insensitive = 1 << 2, + // Sticky + Sticky = 1 << 3, + // Unicode + Unicode = 1 << 4, + // Dot All + SingleLine = 1 << 6, +} + +const flagsToString = (flags: number) => { + let result = ""; + + if (flags & RegExpFlags.Global) result += "g"; + if (flags & RegExpFlags.MultiLine) result += "m"; + if (flags & RegExpFlags.Insensitive) result += "i"; + if (flags & RegExpFlags.Sticky) result += "y"; + if (flags & RegExpFlags.Unicode) result += "u"; + if (flags & RegExpFlags.SingleLine) result += "s"; + + return result; +}; + +export const RegexC = (pattern: RegExp | string, flags: number = 0) => + new RegExp(pattern, flagsToString(flags)); diff --git a/src/utils/reminder.ts b/src/utils/reminder.ts index 4eedf46..14e385e 100644 --- a/src/utils/reminder.ts +++ b/src/utils/reminder.ts @@ -2,6 +2,7 @@ import { Client, Colors, EmbedBuilder, User } from "discord.js"; import { getLocale } from "./locales"; import { cleanCodeBlock } from "./misc"; import { showDate, strToSeconds, timeDeltaToString } from "./time"; +import { RegexC, RegExpFlags } from "./regex"; /** * Option possible for reminders @@ -52,7 +53,10 @@ export const splitTime = (time: string) => { }; const lowered = time.toLowerCase(); - const trimmed = lowered.replaceAll(new RegExp(Object.values(mapping).join("|"), "g"), ""); + const trimmed = lowered.replaceAll( + RegexC(Object.values(mapping).join("|"), RegExpFlags.Global), + "", + ); // Depending of the last character of the string switch (lowered.slice(-1)) { diff --git a/src/utils/time.ts b/src/utils/time.ts index f07d80a..6cd8149 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -1,4 +1,5 @@ import moment from "moment-timezone"; +import { RegexC, RegExpFlags } from "./regex"; /** * Parsed string adapted with TZ (locales) and format for the specified lang @@ -41,7 +42,7 @@ export const strToSeconds = (time: string) => { return -1; } - const regex = new RegExp( + const regex = RegexC( `(?<${TimeSecond[TimeSecond.Year]}>[0-9]+(?=[y|a]))|(?<${ TimeSecond[TimeSecond.Week] }>[0-9]+(?=[w]))|(?<${TimeSecond[TimeSecond.Day]}>[0-9]+(?=[d|j]))|(?<${ @@ -49,6 +50,7 @@ export const strToSeconds = (time: string) => { }>[0-9]+(?=[h]))|(?<${TimeSecond[TimeSecond.Minute]}>[0-9]+(?=[m]))|(?<${ TimeSecond[TimeSecond.Second] }>[0-9]+(?=[s]?))`, + RegExpFlags.Global | RegExpFlags.Insensitive, ); const data = Object.assign({}, regex.exec(time.toLowerCase())?.groups);