matrix-moderation-widget

A widget for moderating with mjolnir. Highly WIP. Use at your own risk!
git clone git://archive.git.mtrnord.blog/MTRNord/matrix-moderation-widget.git
Log | Files | Refs | README | LICENSE

BanForm.tsx (9336B)


      1 import { PureComponent } from "react";
      2 import {
      3     CanonicalAliasEvent,
      4     CANONICAL_ALIAS,
      5     DEV_NORDGEDANKEN_MJOLNIR_BANLISTS,
      6     MJjolnirBanlists,
      7     M_POLICY_RULE_SERVER,
      8     M_POLICY_RULE_SERVER_ALT,
      9     M_POLICY_RULE_SERVER_OLD,
     10     M_POLICY_RULE_USER,
     11     M_POLICY_RULE_USER_ALT,
     12     M_POLICY_RULE_USER_OLD,
     13     ORG_MATRIX_MJOLNIR_SHORTCODE,
     14     RuleEvent,
     15     ShortcodeEvent,
     16 } from "../../windowExt";
     17 
     18 type Props = Record<string, unknown>;
     19 type State = {
     20     glob: string;
     21     reason: string;
     22     roomId?: string;
     23     aliases_shortcodes: Map<string, string>;
     24     banlist: string;
     25     bantype: "user" | "server" | "room" | "";
     26 };
     27 
     28 class BanForm extends PureComponent<Props, State> {
     29     constructor(props: Props | Readonly<Props>) {
     30         super(props);
     31         const urlParams = (new URL(window.location.href)).searchParams;
     32         const roomId = urlParams.get("room_id") ?? undefined;
     33         this.state = {
     34             glob: "",
     35             reason: "",
     36             roomId: roomId,
     37             banlist: "",
     38             bantype: "",
     39             aliases_shortcodes: new Map()
     40         };
     41     }
     42 
     43     async componentDidMount() {
     44         const user_rules = await window.widget_api.readStateEvents(M_POLICY_RULE_USER, 250_000_000_000, undefined, ["*"]) as RuleEvent[];
     45         const server_rules = await window.widget_api.readStateEvents(M_POLICY_RULE_SERVER, 250_000_000_000, undefined, ["*"]) as RuleEvent[];
     46         const user_rules_old = await window.widget_api.readStateEvents(M_POLICY_RULE_USER_OLD, 250_000_000_000, undefined, ["*"]) as RuleEvent[];
     47         const server_rules_old = await window.widget_api.readStateEvents(M_POLICY_RULE_SERVER_OLD, 250_000_000_000, undefined, ["*"]) as RuleEvent[];
     48         const user_rules_alt = await window.widget_api.readStateEvents(M_POLICY_RULE_USER_ALT, 250_000_000_000, undefined, ["*"]) as RuleEvent[];
     49         const server_rules_alt = await window.widget_api.readStateEvents(M_POLICY_RULE_SERVER_ALT, 250_000_000_000, undefined, ["*"]) as RuleEvent[];
     50         const banlist_codes = await window.widget_api.readStateEvents(DEV_NORDGEDANKEN_MJOLNIR_BANLISTS, 250_000_000_000, undefined, [this.state.roomId ?? "*"]) as MJjolnirBanlists[];
     51         const user_rules_all = [...user_rules, ...user_rules_old, ...user_rules_alt];
     52         const server_rules_all = [...server_rules, ...server_rules_old, ...server_rules_alt];
     53         const listrooms = [...new Set([...user_rules_all.map(element => element.room_id), ...server_rules_all.map(element => element.room_id)])];
     54         const aliases = await window.widget_api.readStateEvents(CANONICAL_ALIAS, listrooms.length, undefined, listrooms) as CanonicalAliasEvent[];
     55         const shortcodes = await window.widget_api.readStateEvents(ORG_MATRIX_MJOLNIR_SHORTCODE, listrooms.length, undefined, listrooms) as ShortcodeEvent[];
     56         const aliases_shortcodes = new Map();
     57 
     58         for (const event of aliases) {
     59             let result_known = true;
     60             if (banlist_codes.length > 0) {
     61                 if (banlist_codes.some(known_lists_event => (event.room_id in (known_lists_event.content?.banlists ?? {})) || ((event.content?.alias ?? "") in (known_lists_event.content?.banlists ?? {})))) {
     62                     result_known = true;
     63                 } else {
     64                     result_known = false;
     65                 }
     66             }
     67             if (event.content?.alias && result_known) {
     68                 aliases_shortcodes.set(event.content?.alias, shortcodes.find(shortcode_event => shortcode_event.room_id === event.room_id)?.content?.shortcode);
     69             } else if (result_known) {
     70                 aliases_shortcodes.set(event.room_id, shortcodes.find(shortcode_event => shortcode_event.room_id === event.room_id)?.content?.shortcode);
     71             }
     72         }
     73 
     74         if (banlist_codes.length > 0) {
     75             const lists_event = banlist_codes.find(list => list.room_id === this.state.roomId);
     76             for (const [list, shortcode] of Object.entries(lists_event?.content?.banlists ?? {})) {
     77                 if (!aliases_shortcodes.has(list)) {
     78                     aliases_shortcodes.set(list, shortcode);
     79                 }
     80             }
     81         }
     82         this.setState({
     83             aliases_shortcodes
     84         });
     85     }
     86 
     87     handleInputChange(event: { target: { type: string, checked?: boolean, value: string | boolean, name: string; }; }) {
     88         const target = event.target;
     89         const value = target.type === 'checkbox' ? target.checked : target.value;
     90         const name = target.name;
     91         this.setState({
     92             [name]: value
     93         } as unknown as State);
     94     }
     95 
     96     async handleSubmit(ev: { preventDefault: () => void; }) {
     97         ev.preventDefault();
     98 
     99         const { glob, banlist, bantype, reason, roomId } = this.state;
    100 
    101         if (glob === "" || banlist === "" || bantype === "") {
    102             // TODO display warning
    103             return;
    104         } else {
    105             if (reason === "") {
    106                 await window.widget_api.sendRoomEvent("m.room.message", {
    107                     msgtype: "m.text",
    108                     body: `!mjolnir ban ${banlist} ${bantype} ${glob}`,
    109                 }, roomId);
    110             } else {
    111                 await window.widget_api.sendRoomEvent("m.room.message", {
    112                     msgtype: "m.text",
    113                     body: `!mjolnir ban ${banlist} ${bantype} ${glob} ${reason}`,
    114                 }, roomId);
    115             }
    116         }
    117     }
    118 
    119     render() {
    120         const { glob, banlist, aliases_shortcodes, bantype, reason } = this.state;
    121         return (
    122             <>
    123                 <h3 className="text-2xl font-bold text-gray-900 dark:text-gray-200 mb-2">Ban</h3>
    124                 <form onSubmit={this.handleSubmit.bind(this)} className="grid grid-cols-1 gap-4">
    125                     <label className="inner-flex flex-col">
    126                         <span className="text-gray-900 dark:text-gray-200 visually-hidden mb-2">Banlist</span>
    127                         <h4 className="text-gray-900 dark:text-gray-200 text-base font-thin mb-2 w-96 italic">(Warning: This doesn't check if the banlist can be written to or the bot if watches it)</h4>
    128                         <div className="w-96">
    129                             <select aria-label="Banlist" aria-required="true" required name="banlist" value={banlist} className="max-w-full min-w-full placeholder:text-gray-900 text-gray-900 rounded py-1.5 px-2" onChange={this.handleInputChange.bind(this)}>
    130                                 <option value="" disabled>Select an Banlist</option>
    131                                 {
    132                                     [...aliases_shortcodes].map(([alias, shortcode]) => {
    133                                         return <option key={alias} value={shortcode}>{alias}</option>;
    134                                     })
    135                                 }
    136                             </select>
    137                         </div>
    138                     </label>
    139                     <label className="inner-flex flex-col w-96">
    140                         <span className="text-gray-900 dark:text-gray-200 visually-hidden mb-2">Bantype</span>
    141                         <div className="w-96">
    142                             <select aria-label="Bantype" aria-required="true" required defaultValue="" name="bantype" value={bantype} className="max-w-full min-w-full placeholder:text-gray-900 text-gray-900 rounded py-1.5 px-2" onChange={this.handleInputChange.bind(this)}>
    143                                 <option value="" disabled>Select an Bantype</option>
    144                                 <option value="user">User</option>
    145                                 <option value="server">Server</option>
    146                                 <option value="room">Room</option>
    147                             </select>
    148                         </div>
    149                     </label>
    150                     <label className="block w-96">
    151                         <span className="text-gray-900 dark:text-gray-200 visually-hidden mb-2">Glob</span>
    152                         <div className="mt-1 w-full flex flex-row box-border items-center cursor-text duration-300 max-w-full">
    153                             <input aria-label="Glob" aria-required="true" required placeholder="Glob" className="rounded py-1.5 px-2 min-w-[1.25rem] flex-[1] border-none placeholder:text-gray-900 text-gray-900" type="text" name="glob" value={glob} onChange={this.handleInputChange.bind(this)} />
    154                         </div>
    155                     </label>
    156                     <label className="block w-96">
    157                         <span className="text-gray-900 dark:text-gray-200 visually-hidden mb-2">Reason</span>
    158                         <div className="mt-1 w-full flex flex-row box-border items-center cursor-text duration-300 max-w-full">
    159                             <input aria-label="Reason" aria-required="false" placeholder="Reason" className="rounded py-1.5 px-2 min-w-[1.25rem] flex-[1] border-none placeholder:text-gray-900 text-gray-900" type="text" name="reason" value={reason} onChange={this.handleInputChange.bind(this)} />
    160                         </div>
    161                     </label>
    162 
    163                     <input className="bg-red-400 hover:bg-red-500 cursor-pointer h-10 rounded dark:text-gray-900 text-gray-200 w-96" type="submit" value="Ban" />
    164                 </form>
    165             </>
    166         );
    167     }
    168 }
    169 
    170 export default BanForm;