Skip to content

Commit d32ea38

Browse files
Ferass El HafidiFerass El Hafidi
authored andcommitted
Initial support for bridging Matrix bans to IRC
The bridge used to *not* bridge Matrix bans to IRC. As such, when someone banned an IRC user from Matrix, it would only prevent Matrix users from seeing messages coming from said user, but would not ban that user from IRC at all. This resulted in Matrix channel moderators being confused when other IRC users are reporting spam that simply isn't bridged at all as a result. This commit adds support for bridging Matrix bans to IRC. Currently, it just bans on IRC based on the IRC user's nickname, but this could change in the future, and most importantly is better than not bridging the ban at all. Signed-off-by: Ferass El Hafidi <vitali64pmemail@protonmail.com>
1 parent b62a6a7 commit d32ea38

File tree

3 files changed

+108
-8
lines changed

3 files changed

+108
-8
lines changed

src/bridge/IrcBridge.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,18 +1211,20 @@ export class IrcBridge {
12111211
else if (event.content.membership === "join") {
12121212
await this.matrixHandler.onJoin(request, memberEvent as unknown as OnMemberEventData, target);
12131213
}
1214-
else if (["ban", "leave"].includes(event.content.membership as string)) {
1215-
// Given a "self-kick" is a leave, and you can't ban yourself,
1216-
// if the 2 IDs are different then we know it is either a kick
1217-
// or a ban (or a rescinded invite)
1218-
const isKickOrBan = target.getId() !== sender.getId();
1219-
if (isKickOrBan) {
1214+
else if (event.content.membership === "leave") {
1215+
// Given a "self-kick" is a leave, if the 2 IDs are different then
1216+
// we know it is a kick (or a rescinded invite)
1217+
const isKick = target.getId() !== sender.getId();
1218+
if (isKick) {
12201219
await this.matrixHandler.onKick(request, memberEvent as unknown as MatrixEventKick, sender, target);
12211220
}
12221221
else {
12231222
await this.matrixHandler.onLeave(request, memberEvent, target);
12241223
}
12251224
}
1225+
else if (event.content.membership === "ban") {
1226+
await this.matrixHandler.onBan(request, memberEvent as unknown as MatrixEventKick, sender, target);
1227+
}
12261228
}
12271229
else if (event.type === "m.room.power_levels" && event.state_key === "") {
12281230
this.ircHandler.roomAccessSyncer.onMatrixPowerlevelEvent(event);

src/bridge/MatrixHandler.ts

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ export class MatrixHandler {
652652

653653
private async _onKick(req: BridgeRequest, event: MatrixEventKick, kicker: MatrixUser, kickee: MatrixUser) {
654654
req.log.info(
655-
"onKick %s is kicking/banning %s from %s (reason: %s)",
655+
"onKick %s is kicking %s from %s (reason: %s)",
656656
kicker.getId(), kickee.getId(), event.room_id, event.content.reason || "none"
657657
);
658658
this._onMemberEvent(req, event);
@@ -741,7 +741,81 @@ export class MatrixHandler {
741741
// If we aren't joined this will no-op.
742742
await client.leaveChannel(
743743
ircRoom.channel,
744-
`Kicked by ${kicker.getId()} ` +
744+
`Kicked by ${kicker.getId()}` +
745+
(event.content.reason ? ` : ${event.content.reason}` : "")
746+
);
747+
})));
748+
}
749+
}
750+
751+
private async _onBan(req: BridgeRequest, event: MatrixEventKick, sender: MatrixUser, banned: MatrixUser) {
752+
req.log.info(
753+
"onBan %s is banning %s from %s (reason: %s)",
754+
sender.getId(), banned.getId(), event.room_id, event.content.reason || "none"
755+
);
756+
this._onMemberEvent(req, event);
757+
758+
const ircRooms = await this.ircBridge.getStore().getIrcChannelsForRoomId(event.room_id);
759+
// do we have an active connection for the banned? This tells us if they are real
760+
// or virtual.
761+
const bannedClients = this.ircBridge.getBridgedClientsForUserId(banned.getId());
762+
763+
if (bannedClients.length === 0) {
764+
// Matrix on IRC banning, work out which IRC user to ban.
765+
let server = null;
766+
for (let i = 0; i < ircRooms.length; i++) {
767+
if (ircRooms[i].server.claimsUserId(banned.getId())) {
768+
server = ircRooms[i].server;
769+
break;
770+
}
771+
}
772+
if (!server) {
773+
return; // kicking a bogus user
774+
}
775+
const bannedNick = server.getNickFromUserId(banned.getId());
776+
if (!bannedNick) {
777+
return; // bogus virtual user ID
778+
}
779+
// work out which client will do the kicking
780+
const senderClient = this.ircBridge.getIrcUserFromCache(server, sender.getId());
781+
if (!senderClient) {
782+
// well this is awkward.. whine about it and bail.
783+
req.log.warn(
784+
"%s has no client instance to send kick from. Cannot kick.",
785+
sender.getId()
786+
);
787+
return;
788+
}
789+
// we may be bridging this matrix room into many different IRC channels, and we want
790+
// to kick this user from all of them.
791+
for (let i = 0; i < ircRooms.length; i++) {
792+
if (ircRooms[i].server.domain !== server.domain) {
793+
return;
794+
}
795+
senderClient.ban(bannedNick, ircRooms[i].channel);
796+
senderClient.kick(
797+
bannedNick, ircRooms[i].channel,
798+
`Banned by ${sender.getId()}` +
799+
(event.content.reason ? ` : ${event.content.reason}` : "")
800+
);
801+
}
802+
}
803+
else {
804+
// Matrix on Matrix banning: part the channel.
805+
const bannedServerLookup: {[serverDomain: string]: BridgedClient} = {};
806+
bannedClients.forEach((ircClient) => {
807+
bannedServerLookup[ircClient.server.domain] = ircClient;
808+
});
809+
await Promise.all(ircRooms.map((async (ircRoom) => {
810+
// Make the connected IRC client leave the channel.
811+
const client = bannedServerLookup[ircRoom.server.domain];
812+
if (!client) {
813+
return; // not connected to this server
814+
}
815+
// If we aren't joined this will no-op.
816+
await client.leaveChannel(
817+
ircRoom.channel,
818+
`Banned by ${sender.getId()}` +
745819
(event.content.reason ? ` : ${event.content.reason}` : "")
746820
);
747821
})));
@@ -1466,6 +1540,10 @@ export class MatrixHandler {
14661540
return reqHandler(req, this._onKick(req, event, kicker, kickee));
14671541
}
14681542

1543+
public onBan(req: BridgeRequest, event: MatrixEventKick, sender: MatrixUser, banned: MatrixUser) {
1544+
return reqHandler(req, this._onBan(req, event, sender, banned));
1545+
}
1546+
14691547
public onMessage(req: BridgeRequest, event: MatrixMessageEvent) {
14701548
return reqHandler(req, this._onMessage(req, event));
14711549
}

src/irc/BridgedClient.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,26 @@ export class BridgedClient extends EventEmitter {
510510
await c.send("KICK", channel, nick, reason);
511511
}
512512

513+
public async ban(nick: string, channel: string): Promise<void> {
514+
if (this.state.status !== BridgedClientStatus.CONNECTED) {
515+
return; // we were never connected to the network.
516+
}
517+
if (!this.state.client.chans.has(channel)) {
518+
// we were never joined to it. We need to be joined to it to kick people.
519+
return;
520+
}
521+
if (!channel.startsWith("#")) {
522+
return; // PM room
523+
}
524+
525+
const c = this.state.client;
526+
527+
this.log.debug("Banning %s from channel %s", nick, channel);
528+
529+
// best effort ban
530+
await c.send("MODE", channel, "+b", nick + "!*@*");
531+
}
532+
513533
public sendAction(room: IrcRoom, action: IrcAction) {
514534
this.keepAlive();
515535
let expiryTs = 0;

0 commit comments

Comments
 (0)