mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-06-28 07:32:30 +00:00
Early ban list processing (parsing, specifically)
This commit is contained in:
parent
05e86a972b
commit
23c6c20768
|
@ -9,3 +9,11 @@ dataPath: "/data/storage"
|
|||
|
||||
# Whether the bot should autojoin rooms it is invited to or not
|
||||
autojoin: true
|
||||
|
||||
# The room ID where people can use the bot. The bot has no access controls, so
|
||||
# anyone in this room can use the bot - secure your room!
|
||||
managementRoom: "#moderators:example.org"
|
||||
|
||||
# A list of ban lists to follow (matrix.to URLs)
|
||||
banLists:
|
||||
- "https://matrix.to/#/#sample-ban-list:t2bot.io" # S.A.M.P.L.E.
|
||||
|
|
|
@ -14,27 +14,53 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixClient, Permalinks } from "matrix-bot-sdk";
|
||||
import { MatrixClient } from "matrix-bot-sdk";
|
||||
import { ListRule } from "./ListRule";
|
||||
|
||||
export const RULE_USER = "m.room.rule.user";
|
||||
export const RULE_ROOM = "m.room.rule.room";
|
||||
export const RULE_SERVER = "m.room.rule.server";
|
||||
|
||||
export const USER_RULE_TYPES = [RULE_USER, "org.matrix.mjolnir.rule.user"];
|
||||
export const ROOM_RULE_TYPES = [RULE_ROOM, "org.matrix.mjolnir.rule.room"];
|
||||
export const SERVER_RULE_TYPES = [RULE_SERVER, "org.matrix.mjolnir.rule.server"];
|
||||
export const ALL_RULE_TYPES = [...USER_RULE_TYPES, ...ROOM_RULE_TYPES, ...SERVER_RULE_TYPES];
|
||||
|
||||
export default class BanList {
|
||||
private viaServers: string[] = [];
|
||||
public rules: ListRule[] = [];
|
||||
|
||||
constructor(private roomRef: string, private client: MatrixClient) {
|
||||
if (this.roomRef.startsWith("https")) {
|
||||
const parts = Permalinks.parseUrl(this.roomRef);
|
||||
this.roomRef = parts.roomIdOrAlias;
|
||||
constructor(public readonly roomId: string, public readonly roomRef, private client: MatrixClient) {
|
||||
}
|
||||
|
||||
if (parts.viaServers) this.viaServers = parts.viaServers;
|
||||
public async updateList() {
|
||||
this.rules = [];
|
||||
|
||||
const state = await this.client.getRoomState(this.roomId);
|
||||
for (const event of state) {
|
||||
if (event['state_key'] === '' || !ALL_RULE_TYPES.includes(event['type'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// It's a rule - parse it
|
||||
const content = event['content'];
|
||||
if (!content) continue;
|
||||
|
||||
const entity = content['entity'];
|
||||
const recommendation = content['recommendation'];
|
||||
const reason = content['reason'];
|
||||
|
||||
if (!entity || !recommendation || !reason) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.rules.push(new ListRule(entity, recommendation, reason));
|
||||
}
|
||||
|
||||
client.resolveRoom(this.roomRef).then(roomId => this.roomRef = roomId);
|
||||
}
|
||||
|
||||
public async ensureJoined(): Promise<void> {
|
||||
await this.client.joinRoom(this.roomRef, this.viaServers);
|
||||
public getRuleFor(entity: string): ListRule {
|
||||
for (const rule of this.rules) {
|
||||
if (rule.isMatch(entity)) return rule;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Update list
|
||||
// TODO: Match checking
|
||||
|
||||
}
|
||||
|
|
|
@ -14,17 +14,24 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MatrixGlob } from "matrix-bot-sdk/lib/MatrixGlob";
|
||||
|
||||
export const RECOMMENDATION_BAN = "m.ban";
|
||||
export const RECOMMENDATION_BAN_TYPES = [RECOMMENDATION_BAN, "org.matrix.mjolnir.ban"];
|
||||
|
||||
export class ListRule {
|
||||
constructor(public entity: string, private action: string, public reason: string) {
|
||||
// TODO: Convert entity to regex
|
||||
|
||||
private glob: MatrixGlob;
|
||||
|
||||
constructor(public readonly entity: string, private action: string, public readonly reason: string) {
|
||||
this.glob = new MatrixGlob(entity);
|
||||
}
|
||||
|
||||
public get recommendation(): string {
|
||||
if (RECOMMENDATION_BAN_TYPES.includes(this.action)) return RECOMMENDATION_BAN;
|
||||
}
|
||||
|
||||
// TODO: Match checking functions
|
||||
public isMatch(entity: string): boolean {
|
||||
return this.glob.test(entity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ interface IConfig {
|
|||
accessToken: string;
|
||||
dataPath: string;
|
||||
autojoin: boolean;
|
||||
managementRoom: string;
|
||||
banLists: string[]; // matrix.to urls
|
||||
}
|
||||
|
||||
export default <IConfig>config;
|
||||
|
|
82
src/index.ts
82
src/index.ts
|
@ -19,27 +19,103 @@ import {
|
|||
AutojoinRoomsMixin,
|
||||
LogService,
|
||||
MatrixClient,
|
||||
Permalinks,
|
||||
RichConsoleLogger,
|
||||
SimpleFsStorageProvider
|
||||
} from "matrix-bot-sdk";
|
||||
import config from "./config";
|
||||
import BanList, { ALL_RULE_TYPES } from "./BanList";
|
||||
|
||||
LogService.setLogger(new RichConsoleLogger());
|
||||
|
||||
const storage = new SimpleFsStorageProvider(path.join(config.dataPath, "bot.json"));
|
||||
const client = new MatrixClient(config.homeserverUrl, config.accessToken, storage);
|
||||
const lists: BanList[] = [];
|
||||
let managementRoomId = "";
|
||||
|
||||
if (config.autojoin) {
|
||||
AutojoinRoomsMixin.setupOnClient(client);
|
||||
}
|
||||
|
||||
client.on("room.event", async (roomId, event) => {
|
||||
if (!event['state_key']) return; // we also don't do anything with state events that have no state key
|
||||
|
||||
if (ALL_RULE_TYPES.includes(event['type'])) {
|
||||
for (const list of lists) {
|
||||
if (list.roomId !== roomId) continue;
|
||||
await list.updateList();
|
||||
// TODO: Re-apply ACLs as needed
|
||||
}
|
||||
} else if (event['type'] === "m.room.member") {
|
||||
// TODO: Check membership against ban lists
|
||||
}
|
||||
});
|
||||
|
||||
client.on("room.message", async (roomId, event) => {
|
||||
if (!event['content']) return;
|
||||
|
||||
const content = event['content'];
|
||||
if (content['msgtype'] === 'm.text' && content['body'] === '!mjolnir') {
|
||||
await client.sendNotice(roomId, "Hello world!");
|
||||
if (content['msgtype'] === "m.text" && content['body'] === "!mjolnir") {
|
||||
await client.sendReadReceipt(roomId, event['event_id']);
|
||||
return printStatus(roomId);
|
||||
}
|
||||
});
|
||||
|
||||
client.start().then(() => LogService.info("index", "Bot started!"));
|
||||
|
||||
(async function () {
|
||||
// Ensure we're in all the rooms we expect to be in
|
||||
const joinedRooms = await client.getJoinedRooms();
|
||||
for (const roomRef of config.banLists) {
|
||||
const permalink = Permalinks.parseUrl(roomRef);
|
||||
if (!permalink.roomIdOrAlias) continue;
|
||||
|
||||
const roomId = await client.resolveRoom(permalink.roomIdOrAlias);
|
||||
if (!joinedRooms.includes(roomId)) {
|
||||
await client.joinRoom(permalink.roomIdOrAlias, permalink.viaServers);
|
||||
}
|
||||
|
||||
const list = new BanList(roomId, roomRef, client);
|
||||
await list.updateList();
|
||||
lists.push(list);
|
||||
}
|
||||
|
||||
// Ensure we're also in the management room
|
||||
managementRoomId = await client.joinRoom(config.managementRoom);
|
||||
await client.sendNotice(managementRoomId, "Mjolnir is starting up. Use !mjolnir to query status.");
|
||||
|
||||
// TODO: Check permissions for mjolnir in protected rooms
|
||||
// TODO: Complain about permission changes in protected rooms (including after power levels change)
|
||||
|
||||
await client.start();
|
||||
LogService.info("index", "Bot started!")
|
||||
})();
|
||||
|
||||
async function printStatus(roomId: string) {
|
||||
const rooms = await client.getJoinedRooms();
|
||||
|
||||
let html = "";
|
||||
let text = "";
|
||||
|
||||
// Append header information first
|
||||
html += "<b>Running: </b>✅<br/>";
|
||||
text += "Running: ✅\n";
|
||||
html += `<b>Protected rooms: </b> ${rooms.length}<br/>`;
|
||||
text += `Protected rooms: ${rooms.length}\n`;
|
||||
|
||||
// Append list information
|
||||
html += "<b>Subscribed lists:</b><br><ul>";
|
||||
text += "Subscribed lists:\n";
|
||||
for (const list of lists) {
|
||||
html += `<li><a href="${list.roomRef}">${list.roomId}</a> (${list.rules.length} rules)</li>`;
|
||||
text += `${list.roomRef} (${list.rules.length} rules)\n`;
|
||||
}
|
||||
html += "</ul>";
|
||||
|
||||
const message = {
|
||||
msgtype: "m.notice",
|
||||
body: text,
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: html,
|
||||
};
|
||||
return client.sendMessage(roomId, message);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user