mirror of
https://git.sr.ht/~emerson/reflectionircd
synced 2025-04-13 09:59:52 +00:00
only allow one client to connect at a time
Controversial change, but offloading the problem of being a bouncer onto bouncers is a better idea
This commit is contained in:
parent
dd054af414
commit
d4e3137d93
2 changed files with 81 additions and 127 deletions
|
@ -7,7 +7,7 @@ import { MatrixUser } from './MatrixUser.js';
|
|||
import { IRCMessage, parseIRCMessage } from './Message.js';
|
||||
import { Server } from './Server.js';
|
||||
|
||||
export abstract class Client {
|
||||
export class Client {
|
||||
capVersion: string
|
||||
enabledCaps: Map<string, string>
|
||||
allCaps: Map<string, string>
|
||||
|
@ -839,12 +839,12 @@ export abstract class Client {
|
|||
this.writeMessage(`${msgToSend}\r\n`);
|
||||
}
|
||||
|
||||
abstract writeMessage(message: string): void;
|
||||
writeMessage(message: string): void {};
|
||||
|
||||
closeConnectionWithError(message: string) {
|
||||
this.sendMessage(this.server.name, 'ERROR', [message], new Map());
|
||||
this.closeConnection();
|
||||
}
|
||||
|
||||
abstract closeConnection(): void;
|
||||
closeConnection(): void {};
|
||||
}
|
156
src/Server.ts
156
src/Server.ts
|
@ -16,7 +16,7 @@ export class Server {
|
|||
private directMessages: Set<string>
|
||||
private matrixUsers: Map<string, MatrixUser>
|
||||
public ourMatrixUser: MatrixUser;
|
||||
private clients: Set<Client>
|
||||
private client: Client
|
||||
public nickToMatrixUser: Map<string, MatrixUser>
|
||||
public eventIdStore: Map<string, Client>
|
||||
public eventIDToLabel: Map<string, string>
|
||||
|
@ -41,7 +41,7 @@ export class Server {
|
|||
this.directMessages = new Set();
|
||||
this.matrixUsers = new Map();
|
||||
this.nickToMatrixUser = new Map();
|
||||
this.clients = new Set();
|
||||
this.client = new Client(this);
|
||||
this.eventIdStore = new Map();
|
||||
this.eventIDToLabel = new Map();
|
||||
this.nextBatch = "";
|
||||
|
@ -139,7 +139,7 @@ export class Server {
|
|||
this.invitedChannels.delete(targetChannel.roomId);
|
||||
this.channels.set(targetChannel.name, targetChannel);
|
||||
this.roomIdToChannel.set(targetChannel.roomId, targetChannel);
|
||||
this.clients.forEach(c => this.joinNewIRCClient(c, targetChannel));
|
||||
this.joinNewIRCClient(this.client, targetChannel);
|
||||
if (this.initialSync === false && this.syncLocks.size === 0) {
|
||||
this.initialSync = true;
|
||||
this.doLog('Synced to network!');
|
||||
|
@ -179,10 +179,6 @@ export class Server {
|
|||
return this.ourMatrixUser.getMask();
|
||||
}
|
||||
|
||||
getClients(): Set<Client> {
|
||||
return this.clients;
|
||||
}
|
||||
|
||||
joinNewIRCClient(client: Client, targetChannel: Channel) {
|
||||
if (client.enabledCaps.has('extended-join')) {
|
||||
client.sendMessage(this.getMask(), "JOIN", [targetChannel.name, this.ourMatrixUser.accountName, this.mxid], new Map([['account', this.ourMatrixUser.realname]]));
|
||||
|
@ -198,18 +194,24 @@ export class Server {
|
|||
targetChannel.matrixUsers.set(sourceUser.nick, sourceUser);
|
||||
const prefix = sourceUser.getMask();
|
||||
const joinTags = new Map([["account", sourceUser.accountName], ['time', new Date(event["origin_server_ts"]).toISOString()]])
|
||||
this.sendToAllWithCap('extended-join', prefix, "JOIN", [targetChannel.name, sourceUser.accountName, sourceUser.realname], joinTags);
|
||||
this.sendToAllWithoutCap('extended-join', prefix, "JOIN", [targetChannel.name], joinTags);
|
||||
if (this.client.enabledCaps.has('extended-join')) {
|
||||
this.client.sendMessage(prefix, "JOIN", [targetChannel.name, sourceUser.accountName, sourceUser.realname], joinTags);
|
||||
} else {
|
||||
this.client.sendMessage(prefix, "JOIN", [targetChannel.name], joinTags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addClient(client: Client) {
|
||||
this.clients.add(client);
|
||||
if (this.client.isRegistered) {
|
||||
this.client.closeConnectionWithError("New client connected");
|
||||
}
|
||||
this.client = client;
|
||||
if (this.initialSync) {
|
||||
for (const channel of this.channels.values()) {
|
||||
this.joinNewIRCClient(client, channel);
|
||||
}
|
||||
this.invitedChannels.forEach(roomId => this.sendToAll(this.name, "INVITE", [this.ourMatrixUser.nick, roomId], new Map()));
|
||||
this.invitedChannels.forEach(roomId => this.client.sendMessage(this.name, "INVITE", [this.ourMatrixUser.nick, roomId], new Map()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,10 +313,14 @@ export class Server {
|
|||
return;
|
||||
}
|
||||
if (presence === 'online') {
|
||||
this.sendToAllWithCap('away-notify', matrixUser.getMask(), 'AWAY', []);
|
||||
if (this.client.enabledCaps.has('away-notify')) {
|
||||
this.client.sendMessage(matrixUser.getMask(), 'AWAY', []);
|
||||
}
|
||||
} else {
|
||||
if (this.client.enabledCaps.has('away-notify')) {
|
||||
const awayMessage = (status === '') ? "user is away" : status;
|
||||
this.sendToAllWithCap('away-notify', matrixUser.getMask(), 'AWAY', [awayMessage]);
|
||||
this.client.sendMessage(matrixUser.getMask(), 'AWAY', [awayMessage]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,7 +342,9 @@ export class Server {
|
|||
if (this.eventIDToLabel.has(event["event_id"])) {
|
||||
tags.set("label", this.eventIDToLabel.get(event["event_id"]) || "")
|
||||
}
|
||||
this.sendToAllWithCap("message-tags", sourceUser.getMask(), "TAGMSG", [targetChannel.name], tags)
|
||||
if (this.client.enabledCaps.has('message-tags')) {
|
||||
this.client.sendMessage(sourceUser.getMask(), "TAGMSG", [targetChannel.name], tags)
|
||||
}
|
||||
}
|
||||
|
||||
updateRoomName(newNameEvent: any, targetChannel: Channel) {
|
||||
|
@ -349,15 +357,13 @@ export class Server {
|
|||
this.channels.delete(oldName);
|
||||
targetChannel.name = newName;
|
||||
this.channels.set(newName, targetChannel);
|
||||
this.clients.forEach(client => {
|
||||
if (client.enabledCaps.has("draft/channel-rename")) {
|
||||
client.sendMessage(this.name, "RENAME", [oldName, targetChannel.name, "New channel name set"], new Map());
|
||||
if (this.client.enabledCaps.has("draft/channel-rename")) {
|
||||
this.client.sendMessage(this.name, "RENAME", [oldName, targetChannel.name, "New channel name set"], new Map());
|
||||
}
|
||||
else {
|
||||
client.sendMessage(this.getMask(), "PART", [oldName, `Renaming channel to ${newName}`], new Map());
|
||||
this.joinNewIRCClient(client, targetChannel);
|
||||
this.client.sendMessage(this.getMask(), "PART", [oldName, `Renaming channel to ${newName}`], new Map());
|
||||
this.joinNewIRCClient(this.client, targetChannel);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleEncryptedMessage(event: any, targetChannel: Channel) {
|
||||
|
@ -367,9 +373,7 @@ export class Server {
|
|||
messageTags.set('msgid', event["event_id"]);
|
||||
messageTags.set('time', new Date(event["origin_server_ts"]).toISOString());
|
||||
messageTags.set('account', sourceUser.accountName);
|
||||
this.clients.forEach((channel) => {
|
||||
channel.sendMessage(sourceUser.getMask(), 'NOTICE', [targetChannel.name, "Sent an encrypted message, use another client to view"], messageTags);
|
||||
});
|
||||
this.client.sendMessage(sourceUser.getMask(), 'NOTICE', [targetChannel.name, "Sent an encrypted message, use another client to view"], messageTags);
|
||||
}
|
||||
|
||||
handleMatrixGuestAccess(event: any, targetChannel: Channel) {
|
||||
|
@ -412,15 +416,13 @@ export class Server {
|
|||
messageTags.set('account', sourceUser.accountName);
|
||||
if (membershipStatus === "invite") {
|
||||
const reason = content["reason"];
|
||||
this.clients.forEach(c => {
|
||||
if (c.enabledCaps.has('invite-notify')) {
|
||||
if (c.enabledCaps.has('reflectionircd.chat/extended-invite')) {
|
||||
c.sendMessage(sourceUser.getMask(), 'INVITE', [targetUser.nick, targetChannel.name, reason], messageTags)
|
||||
if (this.client.enabledCaps.has('invite-notify')) {
|
||||
if (this.client.enabledCaps.has('reflectionircd.chat/extended-invite')) {
|
||||
this.client.sendMessage(sourceUser.getMask(), 'INVITE', [targetUser.nick, targetChannel.name, reason], messageTags)
|
||||
} else {
|
||||
c.sendMessage(sourceUser.getMask(), 'INVITE', [targetUser.nick, targetChannel.name], messageTags)
|
||||
this.client.sendMessage(sourceUser.getMask(), 'INVITE', [targetUser.nick, targetChannel.name], messageTags)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (membershipStatus === "join") {
|
||||
this.checkForLazyJoin(event, sourceUser, targetChannel);
|
||||
|
@ -430,15 +432,11 @@ export class Server {
|
|||
return;
|
||||
if (targetUser.mxid === sourceUser.mxid) {
|
||||
const reason = content["reason"] || 'User left';
|
||||
this.clients.forEach((client) => {
|
||||
client.sendMessage(sourceUser.getMask(), 'PART', [targetChannel.name, reason], messageTags);
|
||||
});
|
||||
this.client.sendMessage(sourceUser.getMask(), 'PART', [targetChannel.name, reason], messageTags);
|
||||
}
|
||||
else {
|
||||
const reason = content["reason"] || 'User was kicked';
|
||||
this.clients.forEach((client) => {
|
||||
client.sendMessage(sourceUser.getMask(), 'KICK', [targetChannel.name, targetUser.nick, reason], messageTags);
|
||||
});
|
||||
this.client.sendMessage(sourceUser.getMask(), 'KICK', [targetChannel.name, targetUser.nick, reason], messageTags);
|
||||
}
|
||||
targetChannel.matrixUsers.delete(targetUser.nick)
|
||||
}
|
||||
|
@ -446,9 +444,7 @@ export class Server {
|
|||
if (!targetChannel.matrixUsers.has(targetUser.nick))
|
||||
return;
|
||||
const reason = content["reason"] || 'User was banned';
|
||||
this.clients.forEach((channel) => {
|
||||
channel.sendMessage(sourceUser.getMask(), 'KICK', [targetChannel.name, targetUser.nick, reason], messageTags);
|
||||
});
|
||||
this.client.sendMessage(sourceUser.getMask(), 'KICK', [targetChannel.name, targetUser.nick, reason], messageTags);
|
||||
targetChannel.matrixUsers.delete(targetUser.nick)
|
||||
}
|
||||
else {
|
||||
|
@ -494,43 +490,35 @@ export class Server {
|
|||
if (!msg) {
|
||||
return;
|
||||
}
|
||||
this.clients.forEach((client) => {
|
||||
if (this.eventIdStore.get(event["event_id"]) === client) {
|
||||
if (client.enabledCaps.has('echo-message')) {
|
||||
client.sendMessage(sourceUser.getMask(), ircCommand, [targetChannel.name, msg], tags)
|
||||
if (this.eventIdStore.get(event["event_id"]) === this.client) {
|
||||
if (this.client.enabledCaps.has('echo-message')) {
|
||||
this.client.sendMessage(sourceUser.getMask(), ircCommand, [targetChannel.name, msg], tags)
|
||||
}
|
||||
} else {
|
||||
client.sendMessage(sourceUser.getMask(), ircCommand, [targetChannel.name, msg], tags)
|
||||
this.client.sendMessage(sourceUser.getMask(), ircCommand, [targetChannel.name, msg], tags)
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.clients.forEach(client => {
|
||||
if (this.eventIdStore.get(event["event_id"]) === client && !client.enabledCaps.has('echo-message')) {
|
||||
if (this.eventIdStore.get(event["event_id"]) === this.client && !this.client.enabledCaps.has('echo-message')) {
|
||||
return;
|
||||
}
|
||||
if (client.enabledCaps.has('draft/multiline')) {
|
||||
if (this.client.enabledCaps.has('draft/multiline')) {
|
||||
const batchLabel = Math.random().toString(36).substring(2,7);
|
||||
const batchTags = new Map();
|
||||
batchTags.set('batch', batchLabel);
|
||||
client.sendMessage(sourceUser.getMask(), 'BATCH', [`+${batchLabel}`, 'draft/multiline', targetChannel.name], tags);
|
||||
this.client.sendMessage(sourceUser.getMask(), 'BATCH', [`+${batchLabel}`, 'draft/multiline', targetChannel.name], tags);
|
||||
for (const msg of msgArray) {
|
||||
if (msg) {
|
||||
this.clients.forEach((client) => {
|
||||
client.sendMessage(sourceUser.getMask(), ircCommand, [targetChannel.name, msg], batchTags)
|
||||
});
|
||||
this.client.sendMessage(sourceUser.getMask(), ircCommand, [targetChannel.name, msg], batchTags)
|
||||
}
|
||||
}
|
||||
client.sendMessage(sourceUser.getMask(), 'BATCH', [`-${batchLabel}`], new Map());
|
||||
this.client.sendMessage(sourceUser.getMask(), 'BATCH', [`-${batchLabel}`], new Map());
|
||||
} else {
|
||||
for (const msg of msgArray) {
|
||||
if (msg) {
|
||||
this.clients.forEach((client) => {
|
||||
client.sendMessage(sourceUser.getMask(), ircCommand, [targetChannel.name, msg], tags)
|
||||
});
|
||||
this.client.sendMessage(sourceUser.getMask(), ircCommand, [targetChannel.name, msg], tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,20 +543,14 @@ export class Server {
|
|||
if (oldPl === undefined) {
|
||||
targetChannel.powerLevels.set(thisMatrixUser.nick, newPl);
|
||||
const modeChange = this.convertPLToMode(newPl, "+");
|
||||
this.clients.forEach(c => {
|
||||
c.sendMessage(sourceUser.getMask(), "MODE", [targetChannel.name, modeChange, thisMatrixUser.nick]);
|
||||
})
|
||||
this.client.sendMessage(sourceUser.getMask(), "MODE", [targetChannel.name, modeChange, thisMatrixUser.nick]);
|
||||
}
|
||||
else if (oldPl !== newPl) {
|
||||
const oldModeChange = this.convertPLToMode(oldPl, "-");
|
||||
this.clients.forEach(c => {
|
||||
c.sendMessage(sourceUser.getMask(), "MODE", [targetChannel.name, oldModeChange, thisMatrixUser.nick]);
|
||||
})
|
||||
this.client.sendMessage(sourceUser.getMask(), "MODE", [targetChannel.name, oldModeChange, thisMatrixUser.nick]);
|
||||
if (newPl !== 0) {
|
||||
const newModeChange = this.convertPLToMode(newPl, "+");
|
||||
this.clients.forEach(c => {
|
||||
c.sendMessage(sourceUser.getMask(), "MODE", [targetChannel.name, newModeChange, thisMatrixUser.nick]);
|
||||
})
|
||||
this.client.sendMessage(sourceUser.getMask(), "MODE", [targetChannel.name, newModeChange, thisMatrixUser.nick]);
|
||||
} else {
|
||||
targetChannel.powerLevels.delete(thisMatrixUser.nick)
|
||||
}
|
||||
|
@ -582,9 +564,7 @@ export class Server {
|
|||
const oldPl = targetChannel.powerLevels.get(pl);
|
||||
if (!oldPl) return;
|
||||
const oldMode = this.convertPLToMode(oldPl, "-");
|
||||
this.clients.forEach(c => {
|
||||
c.sendMessage(sourceUser.getMask(), "MODE", [targetChannel.name, oldMode, nextUser.nick]);
|
||||
})
|
||||
this.client.sendMessage(sourceUser.getMask(), "MODE", [targetChannel.name, oldMode, nextUser.nick]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -598,10 +578,9 @@ export class Server {
|
|||
tags.set('reflectionircd.chat/delete-message', event["redacts"]);
|
||||
tags.set('account', sourceUser.accountName);
|
||||
tags.set('time', new Date(event["origin_server_ts"]).toISOString())
|
||||
this.clients.forEach((client) => {
|
||||
if (client.enabledCaps.has("reflectionircd.chat/edit-message"))
|
||||
client.sendMessage(sourceUser.getMask(), 'DELETEMSG', [targetChannel.name, reason], tags);
|
||||
});
|
||||
if (this.client.enabledCaps.has("reflectionircd.chat/edit-message")) {
|
||||
this.client.sendMessage(sourceUser.getMask(), 'DELETEMSG', [targetChannel.name, reason], tags);
|
||||
}
|
||||
}
|
||||
|
||||
handleMatrixTopic(event: any, targetChannel: Channel) {
|
||||
|
@ -621,9 +600,7 @@ export class Server {
|
|||
if (this.eventIDToLabel.has(event["event_id"])) {
|
||||
messageTags.set("label", this.eventIDToLabel.get(event["event_id"]) || "")
|
||||
}
|
||||
this.clients.forEach((client) => {
|
||||
client.sendMessage(topicSetter.getMask(), 'TOPIC', [targetChannel.name, topicText], messageTags);
|
||||
});
|
||||
this.client.sendMessage(topicSetter.getMask(), 'TOPIC', [targetChannel.name, topicText], messageTags);
|
||||
}
|
||||
|
||||
handleMatrixSticker(event: any, targetChannel: Channel) {
|
||||
|
@ -642,7 +619,7 @@ export class Server {
|
|||
uri = `${this.homeserver}/_matrix/media/v3/download/${mxcregex.groups.servername}/${mxcregex.groups.mediaid}`;
|
||||
}
|
||||
const messageContent = `\x01ACTION sent a sticker: ${altText}: ${uri}\x01`;
|
||||
this.sendToAll(sourceUser.getMask(), 'PRIVMSG', [targetChannel.name, messageContent]);
|
||||
this.client.sendMessage(sourceUser.getMask(), 'PRIVMSG', [targetChannel.name, messageContent]);
|
||||
}
|
||||
|
||||
handleMatrixTyping(event: any, targetChannel: Channel) {
|
||||
|
@ -656,32 +633,9 @@ export class Server {
|
|||
if (matrixUser !== undefined) {
|
||||
const typingTags = new Map();
|
||||
typingTags.set('+typing', 'active');
|
||||
this.sendToAllWithCap('message-tags', matrixUser.getMask(), 'TAGMSG', [targetChannel.name], typingTags);
|
||||
if (this.client.enabledCaps.has('message-tags')) {
|
||||
this.client.sendMessage(matrixUser.getMask(), 'TAGMSG', [targetChannel.name], typingTags);
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
sendToAll(prefix: string, command: string, params: string[], tags: Map<string, string> = new Map(), skipClient: Client|null = null) {
|
||||
this.clients.forEach(client => {
|
||||
if (client !== skipClient) {
|
||||
client.sendMessage(prefix, command, params, tags);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
sendToAllWithCap(cap: string, prefix: string, command: string, params: string[], tags: Map<string, string> = new Map(), skipClient: Client|null = null) {
|
||||
this.clients.forEach(client => {
|
||||
if (client !== skipClient && client.enabledCaps.has(cap)) {
|
||||
client.sendMessage(prefix, command, params, tags);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
sendToAllWithoutCap(cap: string, prefix: string, command: string, params: string[], tags: Map<string, string> = new Map(), skipClient: Client|null = null) {
|
||||
this.clients.forEach(client => {
|
||||
if (client !== skipClient && !client.enabledCaps.has(cap)) {
|
||||
client.sendMessage(prefix, command, params, tags);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue