Plugin

KeywordNotify

Sends a notification if a given message matches certain keywords or regexes

index.tsx
Download

Source

src/plugins/keywordNotify/index.tsx
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import "./style.css";
8
9import { DataStore } from "@api/index";
10import { definePluginSettings } from "@api/Settings";
11import { Flex } from "@components/Flex";
12import { DeleteIcon } from "@components/Icons";
13import { Devs } from "@utils/constants";
14import { useForceUpdater } from "@utils/react";
15import definePlugin, { OptionType } from "@utils/types";
16import { Button, Forms, TextInput, UserStore, UserUtils, useState } from "@webpack/common";
17import { User } from "discord-types/general";
18
19let regexes: string[] = [];
20let me: User | null = null;
21
22async function setRegexes(idx: number, reg: string) {
23 regexes[idx] = reg;
24 await DataStore.set("KeywordNotify_rules", regexes);
25}
26
27async function removeRegex(idx: number, updater: () => void) {
28 regexes.splice(idx, 1);
29 await DataStore.set("KeywordNotify_rules", regexes);
30 updater();
31}
32
33async function addRegex(updater: () => void) {
34 regexes.push("");
35 await DataStore.set("KeywordNotify_rules", regexes);
36 updater();
37}
38
39const settings = definePluginSettings({
40 replace: {
41 type: OptionType.COMPONENT,
42 description: "",
43 component: () => {
44 const update = useForceUpdater();
45 const [values, setValues] = useState(regexes);
46
47 const elements = regexes.map((a, i) => {
48 const setValue = (v: string) => {
49 const valuesCopy = [...values];
50 valuesCopy[i] = v;
51 setValues(valuesCopy);
52 };
53
54 return (
55 <>
56 <Forms.FormTitle tag="h4">Keyword Regex {i + 1}</Forms.FormTitle>
57
58 <Flex flexDirection="row">
59 <div style={{ flexGrow: 1 }}>
60 <TextInput
61 placeholder={"example|regex"}
62 spellCheck={false}
63 value={values[i]}
64 onChange={setValue}
65 onBlur={() => setRegexes(i, values[i])}
66 />
67 </div>
68
69 <Button
70 onClick={() => removeRegex(i, update)}
71 look={Button.Looks.BLANK}
72 size={Button.Sizes.ICON}
73 className="keywordnotify-delete">
74 <DeleteIcon />
75 </Button>
76 </Flex>
77 </>
78 );
79 });
80
81 return (
82 <>
83 {elements}
84 <div><Button onClick={() => addRegex(update)}>Add Regex</Button></div>
85 </>
86 );
87 }
88 },
89});
90
91export default definePlugin({
92 name: "KeywordNotify",
93 authors: [Devs.FiveCord],
94 description: "Sends a notification if a given message matches certain keywords or regexes",
95 settings,
96 patches: [
97 {
98 find: "}_dispatch(",
99 replacement: {
100 match: /}_dispatch\((.{1,2}),.{1,2}\){/,
101 replace: "$&$1=$self.modify($1);"
102 }
103 }
104 ],
105
106 async start() {
107 regexes = await DataStore.get("KeywordNotify_rules") ?? [];
108 me = await UserUtils.getUser(UserStore.getCurrentUser().id);
109 },
110
111 applyRegexes(m) {
112 if (regexes.some(r => m.content.match(new RegExp(r)))) {
113 m.mentions.push(me);
114 }
115 },
116
117 modify(e) {
118 if (e.type === "MESSAGE_CREATE") {
119 this.applyRegexes(e.message);
120 } else if (e.type === "LOAD_MESSAGES_SUCCESS") {
121 for (let msg = 0; msg < e.messages.length; ++msg) {
122 this.applyRegexes(e.messages[msg]);
123 }
124 }
125 return e;
126 }
127});
128