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

commit efd6d6d0a28efcf9e2d8ba63abe9758201d979d6
parent c4ed22a2a334af917c15a70fd31e8055bea820cd
Author: MTRNord <mtrnord1@gmail.com>
Date:   Fri, 18 Feb 2022 22:13:30 +0100

Some quality of live improvements

Diffstat:
Msrc/components/Banlist.tsx | 18++++++++++++++----
Msrc/components/Home.tsx | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/index.tsx | 6+++++-
Msrc/windowExt.ts | 23+++++++++++++++++++++++
4 files changed, 105 insertions(+), 15 deletions(-)

diff --git a/src/components/Banlist.tsx b/src/components/Banlist.tsx @@ -1,5 +1,15 @@ import { PureComponent } from 'react'; -import { MembershipEvent, M_POLICY_RULE_SERVER, M_POLICY_RULE_SERVER_ALT, M_POLICY_RULE_SERVER_OLD, M_POLICY_RULE_USER, M_POLICY_RULE_USER_ALT, M_POLICY_RULE_USER_OLD, ServerRuleEvent, UserRuleEvent } from '../windowExt'; +import { + MembershipEvent, + M_POLICY_RULE_SERVER, + M_POLICY_RULE_SERVER_ALT, + M_POLICY_RULE_SERVER_OLD, + M_POLICY_RULE_USER, + M_POLICY_RULE_USER_ALT, + M_POLICY_RULE_USER_OLD, + ServerRuleEvent, + UserRuleEvent +} from '../windowExt'; type Props = Record<string, unknown>; type State = { @@ -55,12 +65,12 @@ class BanList extends PureComponent<Props, State> { const filtered_user_rules = filter ? user_rules.filter(element => element.content && Object.keys(element.content).length > 0 && element.content?.entity.includes(filter)) : user_rules; const filtered_server_rules = filter ? server_rules.filter(element => element.content && Object.keys(element.content).length > 0 && element.content?.entity.includes(filter)) : server_rules; return ( - <main className="m-4 flex items-center flex-col justify-center"> - <div className='flex flex-row justify-between content-between w-full px-12'> + <main className="m-8 flex items-center flex-col justify-center"> + <div className='flex flex-row justify-between content-between w-full'> <h1 className="text-3xl font-bold underline text-gray-900 dark:text-gray-200 mb-4">Banlist data</h1> <input onChange={this.onSearch.bind(this)} value={filter} placeholder="Search for user or server" className='py-1.5 px-2 rounded border-none placeholder:text-gray-900 text-gray-9000'></input> </div> - <div className='flex flex-row px-12'> + <div className='flex flex-row justify-between content-between w-full'> <section className='mr-2'> <h3 className="text-2xl font-bold text-gray-900 dark:text-gray-200 mb-2">User Bans ({user_rules.length})</h3> <table className='table-auto border-collapse border-2 border-slate-500 text-gray-900 dark:text-gray-200 text-base font-normal break-words'> diff --git a/src/components/Home.tsx b/src/components/Home.tsx @@ -1,28 +1,68 @@ import { PureComponent } from 'react'; +import { + CanonicalAliasEvent, + CANONICAL_ALIAS, + M_POLICY_RULE_SERVER, + M_POLICY_RULE_SERVER_ALT, + M_POLICY_RULE_SERVER_OLD, + M_POLICY_RULE_USER, + M_POLICY_RULE_USER_ALT, + M_POLICY_RULE_USER_OLD, + ServerRuleEvent, + ShortcodeEvent, + UserRuleEvent +} from '../windowExt'; type Props = Record<string, unknown>; type State = { mxid?: string; roomId?: string; + aliases_shortcodes: Map<string, string>; + banlist?: string; }; class Home extends PureComponent<Props, State> { - constructor(props: Props | Readonly<Props>) { super(props); const urlParams = (new URL(window.location.href)).searchParams; const roomId = urlParams.get("room_id") ?? undefined; this.state = { roomId: roomId, - } as State; + aliases_shortcodes: new Map() + }; + } + + async componentDidMount() { + const user_rules = await window.widget_api.readStateEvents(M_POLICY_RULE_USER, 250_000_000_000, undefined, ["*"]) as UserRuleEvent[]; + const server_rules = await window.widget_api.readStateEvents(M_POLICY_RULE_SERVER, 250_000_000_000, undefined, ["*"]) as ServerRuleEvent[]; + const user_rules_old = await window.widget_api.readStateEvents(M_POLICY_RULE_USER_OLD, 250_000_000_000, undefined, ["*"]) as UserRuleEvent[]; + const server_rules_old = await window.widget_api.readStateEvents(M_POLICY_RULE_SERVER_OLD, 250_000_000_000, undefined, ["*"]) as ServerRuleEvent[]; + const user_rules_alt = await window.widget_api.readStateEvents(M_POLICY_RULE_USER_ALT, 250_000_000_000, undefined, ["*"]) as UserRuleEvent[]; + const server_rules_alt = await window.widget_api.readStateEvents(M_POLICY_RULE_SERVER_ALT, 250_000_000_000, undefined, ["*"]) as ServerRuleEvent[]; + const user_rules_all = [...user_rules, ...user_rules_old, ...user_rules_alt]; + const server_rules_all = [...server_rules, ...server_rules_old, ...server_rules_alt]; + const listrooms = [...new Set([...user_rules_all.map(element => element.room_id), ...server_rules_all.map(element => element.room_id)])]; + const aliases = await window.widget_api.readStateEvents(CANONICAL_ALIAS, listrooms.length, undefined, listrooms) as CanonicalAliasEvent[]; + const shortcodes = await window.widget_api.readStateEvents(CANONICAL_ALIAS, listrooms.length, undefined, listrooms) as ShortcodeEvent[]; + const aliases_shortcodes = new Map(); + + for (const event of aliases) { + if (event.content?.alias) { + aliases_shortcodes.set(event.content?.alias, shortcodes.find(shortcode_event => shortcode_event.room_id === event.room_id)?.content?.shortcode); + } + } + + this.setState({ + aliases_shortcodes + }); } - handleInputChange(event: { target: any; }) { + handleInputChange(event: { target: { type: string, checked?: boolean, value: string | boolean, name: string; }; }) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value - } as State); + } as unknown as State); } async handleSubmit() { @@ -33,22 +73,35 @@ class Home extends PureComponent<Props, State> { } render() { - const { mxid } = this.state; + const { mxid, banlist, aliases_shortcodes } = this.state; return ( - <main className="m-4"> + <main className="m-8"> <h1 className="text-3xl font-bold underline text-gray-900 dark:text-gray-200 mb-4">Home</h1> <div className='flex flex-col'> <section> <h3 className="text-2xl font-bold text-gray-900 dark:text-gray-200 mb-2">Ban user</h3> - <form onSubmit={this.handleSubmit.bind(this)} className="w-96 grid grid-cols-1 gap-6"> - <label className="block"> - <span className="text-gray-900 dark:text-gray-200 visually-hidden">MXID</span> + <form onSubmit={this.handleSubmit.bind(this)} className="grid grid-cols-1 gap-6"> + <label className="inner-flex flex-col"> + <span className="text-xl text-gray-900 dark:text-gray-200 font-bold py-2">Banlist (warning this doesnt check if the banlist can be written to or the bot watches it)</span> + <div className="w-96"> + <select defaultValue="" required name="banlist" value={banlist ?? ""} className="max-w-full placeholder:text-gray-900 text-gray-900 rounded py-1.5 px-2" onChange={this.handleInputChange.bind(this)}> + <option value="" disabled>Select an Banlist</option> + { + [...aliases_shortcodes].map(([alias, shortcode]) => { + return <option value={shortcode}>{alias}</option>; + }) + } + </select> + </div> + </label> + <label className="block w-96"> + <span className="text-gray-900 dark:text-gray-200 visually-hidden py-2">MXID</span> <div className="mt-1 w-full flex flex-row box-border items-center cursor-text duration-300"> <input placeholder="MXID" 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="mxid" value={mxid} onChange={this.handleInputChange} /> </div> </label> - <input className="bg-red-400 hover:bg-red-500 cursor-pointer h-10 rounded dark:text-gray-900 text-gray-200" type="submit" value="Ban User" /> + <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 User" /> </form> </section> </div> diff --git a/src/index.tsx b/src/index.tsx @@ -5,12 +5,14 @@ import App from './components/App'; import reportWebVitals from './reportWebVitals'; import { WidgetApi } from 'matrix-widget-api'; import { + CANONICAL_ALIAS, M_POLICY_RULE_SERVER, M_POLICY_RULE_SERVER_ALT, M_POLICY_RULE_SERVER_OLD, M_POLICY_RULE_USER, M_POLICY_RULE_USER_ALT, - M_POLICY_RULE_USER_OLD + M_POLICY_RULE_USER_OLD, + ORG_MATRIX_MJOLNIR_SHORTCODE, } from './windowExt'; const urlParams = (new URL(window.location.href)).searchParams; @@ -22,6 +24,8 @@ window.widget_api.requestCapabilityToReceiveState(M_POLICY_RULE_USER_OLD); window.widget_api.requestCapabilityToReceiveState(M_POLICY_RULE_SERVER_OLD); window.widget_api.requestCapabilityToReceiveState(M_POLICY_RULE_USER_ALT); window.widget_api.requestCapabilityToReceiveState(M_POLICY_RULE_SERVER_ALT); +window.widget_api.requestCapabilityToReceiveState(CANONICAL_ALIAS); +window.widget_api.requestCapabilityToReceiveState(ORG_MATRIX_MJOLNIR_SHORTCODE); window.widget_api.requestCapabilityToSendMessage("m.text"); window.widget_api.requestCapabilityToReceiveState("m.room.member"); // TODO maybe figure out the proper way later diff --git a/src/windowExt.ts b/src/windowExt.ts @@ -10,6 +10,9 @@ export const M_POLICY_RULE_USER_OLD = "org.matrix.mjolnir.rule.user"; export const M_POLICY_RULE_SERVER_OLD = "org.matrix.mjolnir.rule.server"; export const M_POLICY_RULE_USER_ALT = "m.room.rule.user"; export const M_POLICY_RULE_SERVER_ALT = "m.room.rule.server"; +export const CANONICAL_ALIAS = "m.room.canonical_alias"; +export const ORG_MATRIX_MJOLNIR_SHORTCODE = "org.matrix.mjolnir.shortcode"; + export type UserRuleEvent = { sender: string; state_key: string; @@ -45,4 +48,24 @@ export type MembershipEvent = { avatar_url?: string; displayname?: string; }; +}; + +export type CanonicalAliasEvent = { + sender: string; + state_key: string; + event_id: string; + room_id: string; + content?: { + alias: string; + }; +}; + +export type ShortcodeEvent = { + sender: string; + state_key: string; + event_id: string; + room_id: string; + content?: { + shortcode: string; + }; }; \ No newline at end of file