commit 2cf4d6e677f45805df7cfbb7606622a3a703aced
parent 4801d81951128756c6690ed97363bd8b0c6fbb4b
Author: MTRNord <mtrnord1@gmail.com>
Date: Thu, 3 Mar 2022 16:50:31 +0100
Prepare ability to add bot to new room and allow smarter filtering of the dropwdown using a custom state event.
Diffstat:
7 files changed, 202 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
@@ -28,4 +28,30 @@ Something like this (room_id and https are required)
## Hosted instance
A hosted widget is at https://moderation_widget.nordgedanken.dev/ you can add it via
-`/addwidget https://moderation_widget.nordgedanken.dev?room_id=$matrix_room_id`
-\ No newline at end of file
+`/addwidget https://moderation_widget.nordgedanken.dev?room_id=$matrix_room_id`
+
+## Adcanced
+
+### Use a state event to allow showing relevant Lists only in the dropdown
+
+TODO:
+
+1. Allow the bot to send the state event. For this you need to make the bot PL 1
+and allow sending `dev.nordgedanken.mjolnir_banlists` for PL1 users.
+2. Send the following state event as the bot:
+
+You can use `/devtools` in element-web to send a custom state event.
+
+Event Type: `dev.nordgedanken.mjolnir_banlists`
+State Key: The user id of the bot
+Content:
+
+```
+{
+ "banlists": {
+ "#matrix-org-coc-bl:matrix.org": "COC"
+ }
+}
+```
+
+Edit it to fit your banlists.
+\ No newline at end of file
diff --git a/src/components/App.tsx b/src/components/App.tsx
@@ -1,8 +1,9 @@
import { PureComponent } from 'react';
import BanList from './Banlist';
import Home from './Home';
+import NewRoom from './NewRoom';
-type Pages = "banlist" | "home";
+type Pages = "banlist" | "home" | "new_room";
type Props = Record<string, unknown>;
type State = {
@@ -29,6 +30,26 @@ class App extends PureComponent<Props, State> {
});
}
+ onNewRoomClick() {
+ this.setState({
+ current_page: "new_room"
+ });
+ }
+
+ getPage(page: Pages) {
+ switch (page) {
+ case "home": {
+ return <Home />;
+ }
+ case "banlist": {
+ return <BanList />;
+ }
+ case "new_room": {
+ return <NewRoom />;
+ }
+ }
+ }
+
render() {
const current_page = this.state.current_page;
return (
@@ -37,13 +58,14 @@ class App extends PureComponent<Props, State> {
<div className='flex lg:flex-1 items-center justify-between lg:flex-row flex-col'>
<nav className='flex lg:flex-shrink-0 my-4'>
<button className='px-4 h-auto min-w-[1.5rem] flex items-center whitespace-nowrap cursor-pointer text-gray-900 dark:text-gray-200 font-medium brightness-100 hover:brightness-75 duration-200 ease-in-out transition-all' onClick={this.onHomeClick.bind(this)}>Home</button>
+ {/*<button className='px-4 h-auto min-w-[1.5rem] flex items-center whitespace-nowrap cursor-pointer text-gray-900 dark:text-gray-200 font-medium brightness-100 hover:brightness-75 duration-200 ease-in-out transition-all' onClick={this.onNewRoomClick.bind(this)}>Add to new Room</button> */}
<button className='px-4 h-auto min-w-[1.5rem] flex items-center whitespace-nowrap cursor-pointer text-gray-900 dark:text-gray-200 font-medium brightness-100 hover:brightness-75 duration-200 ease-in-out transition-all' onClick={this.onBanListClick.bind(this)}>BanList</button>
</nav>
</div>
</header>
<div className='lg:pt-20 pt-52 z-0 max-h-full'>
{
- current_page === "home" ? <Home /> : (current_page === "banlist" ? <BanList /> : <Home />)
+ this.getPage(current_page)
}
</div>
</div>
diff --git a/src/components/NewRoom.tsx b/src/components/NewRoom.tsx
@@ -0,0 +1,28 @@
+import { PureComponent } from "react";
+import NewRoomForm from "./forms/NewRoomForm";
+
+type Props = Record<string, unknown>;
+type State = Record<string, unknown>;
+
+export default class NewRoom extends PureComponent<Props, State> {
+ render() {
+ return (
+ <main className="m-8">
+ <h1 className="text-3xl font-bold underline text-gray-900 dark:text-gray-200 mb-4">Add new Room</h1>
+ <div className='flex flex-row justify-center items-center flex-wrap'>
+ <div className='flex flex-row flex-wrap justify-start'>
+ <section className='mr-16 mb-16'>
+ <NewRoomForm />
+ </section>
+ <section className='mr-16 mb-16'>
+ <h3 className="text-2xl font-bold text-gray-900 dark:text-gray-200 mb-2">What will this do?</h3>
+ <p className="text-base font-normal text-gray-900 dark:text-gray-200 mb-2">
+ TODO explenation
+ </p>
+ </section>
+ </div>
+ </div>
+ </main>
+ );
+ }
+}
+\ No newline at end of file
diff --git a/src/components/forms/BanForm.tsx b/src/components/forms/BanForm.tsx
@@ -2,6 +2,8 @@ import { PureComponent } from "react";
import {
CanonicalAliasEvent,
CANONICAL_ALIAS,
+ DEV_NORDGEDANKEN_MJOLNIR_BANLISTS,
+ MJjolnirBanlists,
M_POLICY_RULE_SERVER,
M_POLICY_RULE_SERVER_ALT,
M_POLICY_RULE_SERVER_OLD,
@@ -46,6 +48,7 @@ class BanForm extends PureComponent<Props, State> {
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 banlist_codes = await window.widget_api.readStateEvents(DEV_NORDGEDANKEN_MJOLNIR_BANLISTS, 250_000_000_000, undefined, [this.state.roomId ?? "*"]) as MJjolnirBanlists[];
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)])];
@@ -54,8 +57,18 @@ class BanForm extends PureComponent<Props, State> {
const aliases_shortcodes = new Map();
for (const event of aliases) {
- if (event.content?.alias) {
+ let result_known = true;
+ if (banlist_codes.length > 0) {
+ if (banlist_codes.some(known_lists_event => known_lists_event.content?.banlists.keys.includes(event.room_id) || known_lists_event.content?.banlists.keys.includes(event.content?.alias ?? ""))) {
+ result_known = true;
+ } else {
+ result_known = false;
+ }
+ }
+ if (event.content?.alias && result_known) {
aliases_shortcodes.set(event.content?.alias, shortcodes.find(shortcode_event => shortcode_event.room_id === event.room_id)?.content?.shortcode);
+ } else if (result_known) {
+ aliases_shortcodes.set(event.room_id, shortcodes.find(shortcode_event => shortcode_event.room_id === event.room_id)?.content?.shortcode);
}
}
diff --git a/src/components/forms/NewRoomForm.tsx b/src/components/forms/NewRoomForm.tsx
@@ -0,0 +1,86 @@
+import { PureComponent } from "react";
+import { DEV_NORDGEDANKEN_MJOLNIR_BANLISTS, MJjolnirBanlists, M_ROOM_MEMBER, M_ROOM_POWERLEVELS } from "../../windowExt";
+type Props = Record<string, unknown>;
+type State = {
+ room: string;
+ roomId?: string;
+ user_id: string;
+};
+class NewRoomForm 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 = {
+ room: "",
+ roomId: roomId,
+ user_id: ""
+ };
+ }
+
+ async componentDidMount() {
+ // TODO use to get the bot if available
+ const banlist_codes = await window.widget_api.readStateEvents(DEV_NORDGEDANKEN_MJOLNIR_BANLISTS, 250_000_000_000, undefined, [this.state.roomId ?? "*"]) as MJjolnirBanlists[];
+ }
+
+ 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 unknown as State);
+ }
+
+ async handleSubmit(ev: { preventDefault: () => void; }) {
+ ev.preventDefault();
+
+ if (this.state.user_id === "" || this.state.room === "") {
+ return;
+ }
+ if (this.state.room.startsWith("!")) {
+ // TODO patch power_levels
+ // TODO fix race due to perms
+ window.widget_api.requestCapabilityToSendState(M_ROOM_MEMBER);
+ window.widget_api.requestCapabilityToReceiveState(M_ROOM_POWERLEVELS);
+ window.widget_api.requestCapabilityToSendState(M_ROOM_POWERLEVELS);
+ await window.widget_api.updateRequestedCapabilities();
+ await window.widget_api.sendStateEvent(M_ROOM_MEMBER, this.state.user_id, {
+ displayname: "Administrator",
+ is_direct: false,
+ membership: "invite"
+ }, this.state.room);
+ }
+
+ await window.widget_api.sendRoomEvent("m.room.message", {
+ msgtype: "m.text",
+ body: `!mjolnir rooms add ${this.state.room}`,
+ }, this.state.roomId);
+ }
+
+ render() {
+ const { room, user_id } = this.state;
+ return (
+ <>
+ <h3 className="text-2xl font-bold text-gray-900 dark:text-gray-200 mb-2">Protect a new Room</h3>
+ <form onSubmit={this.handleSubmit.bind(this)} className="grid grid-cols-1 gap-4">
+ <label className="block w-96">
+ <span className="text-gray-900 dark:text-gray-200 visually-hidden mb-2">Room (required)</span>
+ <div className="mt-1 w-full flex flex-row box-border items-center cursor-text duration-300 max-w-full">
+ <input required placeholder="Room (required)" 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="room" value={room} onChange={this.handleInputChange.bind(this)} />
+ </div>
+ </label>
+ <label className="block w-96">
+ <span className="text-gray-900 dark:text-gray-200 visually-hidden mb-2">Bot user_id (required as we can't know)</span>
+ <div className="mt-1 w-full flex flex-row box-border items-center cursor-text duration-300 max-w-full">
+ <input required placeholder="Bot user_id (required as we can't know)" 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="user_id" value={user_id} onChange={this.handleInputChange.bind(this)} />
+ </div>
+ </label>
+ <input className="bg-green-400 hover:bg-green-500 cursor-pointer h-10 rounded dark:text-gray-900 text-gray-200 w-96" type="submit" value="Protect" />
+ </form>
+ </>
+ );
+ }
+}
+
+export default NewRoomForm;
+\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
@@ -6,12 +6,15 @@ import reportWebVitals from './reportWebVitals';
import { WidgetApi } from 'matrix-widget-api';
import {
CANONICAL_ALIAS,
+ DEV_NORDGEDANKEN_MJOLNIR_BANLISTS,
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_ROOM_MEMBER,
+ M_TEXT,
ORG_MATRIX_MJOLNIR_SHORTCODE,
} from './windowExt';
@@ -26,8 +29,9 @@ 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");
+window.widget_api.requestCapabilityToSendMessage(M_TEXT);
+window.widget_api.requestCapabilityToReceiveState(M_ROOM_MEMBER);
+window.widget_api.requestCapabilityToReceiveState(DEV_NORDGEDANKEN_MJOLNIR_BANLISTS);
// TODO maybe figure out the proper way later
window.widget_api.requestCapabilityForRoomTimeline("*");
diff --git a/src/windowExt.ts b/src/windowExt.ts
@@ -12,6 +12,10 @@ 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 const M_TEXT = "m.text";
+export const M_ROOM_MEMBER = "m.room.member";
+export const M_ROOM_POWERLEVELS = "m.room.power_levels";
+export const DEV_NORDGEDANKEN_MJOLNIR_BANLISTS = "dev.nordgedanken.mjolnir_banlists";
export type UserRuleEvent = {
sender: string;
@@ -68,4 +72,14 @@ export type ShortcodeEvent = {
content?: {
shortcode: string;
};
+};
+
+export type MJjolnirBanlists = {
+ sender: string;
+ state_key: string;
+ event_id: string;
+ room_id: string;
+ content?: {
+ banlists: Record<string, string>;
+ };
};
\ No newline at end of file