mirror of
https://git.sr.ht/~emerson/reflectionircd
synced 2025-08-05 16:59:10 +00:00
basic classes
This commit is contained in:
parent
a84ac146a0
commit
43e974f4c6
8 changed files with 545 additions and 0 deletions
50
package-lock.json
generated
Normal file
50
package-lock.json
generated
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"name": "reflectionircd",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"lockfileVersion": 2,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "reflectionircd",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "GPL-3.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^16.11.11",
|
||||||
|
"typescript": "^4.5.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "16.11.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz",
|
||||||
|
"integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "4.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
|
||||||
|
"integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": {
|
||||||
|
"version": "16.11.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.11.tgz",
|
||||||
|
"integrity": "sha512-KB0sixD67CeecHC33MYn+eYARkqTheIRNuu97y2XMjR7Wu3XibO1vaY6VBV6O/a89SPI81cEUIYT87UqUWlZNw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"version": "4.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
|
||||||
|
"integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
src/Channel.ts
Normal file
31
src/Channel.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { Server } from "./Server.js";
|
||||||
|
import { MatrixUser } from "./MatrixUser.js";
|
||||||
|
import { IRCUser } from "./IRCUser.js";
|
||||||
|
import { IRCMessage } from "./Message.js";
|
||||||
|
|
||||||
|
export class Channel {
|
||||||
|
public name: string
|
||||||
|
private matrixUsers: Map<string, MatrixUser>
|
||||||
|
private ircUsers: Map<string, IRCUser>
|
||||||
|
private nickToMXid: Map<string, string>
|
||||||
|
private powerLevels: Map<string, number>
|
||||||
|
private topic: Map<string, string>;
|
||||||
|
private memberCount: number;
|
||||||
|
private modes: Map<string, string>
|
||||||
|
private messages: Map<string, IRCMessage>;
|
||||||
|
private tsToEventId: Map<number, string>;
|
||||||
|
constructor(public roomId: string, private server: Server, initialIRCUser: IRCUser) {
|
||||||
|
this.name = roomId;
|
||||||
|
this.matrixUsers = new Map();
|
||||||
|
this.ircUsers = new Map();
|
||||||
|
this.ircUsers.set(initialIRCUser.nick, initialIRCUser);
|
||||||
|
this.nickToMXid = new Map();
|
||||||
|
this.powerLevels = new Map();
|
||||||
|
this.topic = new Map();
|
||||||
|
this.memberCount = 0;
|
||||||
|
this.modes = new Map();
|
||||||
|
this.modes.set('n', '');
|
||||||
|
this.messages = new Map();
|
||||||
|
this.tsToEventId = new Map();
|
||||||
|
}
|
||||||
|
}
|
198
src/Client.ts
Normal file
198
src/Client.ts
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
import { Socket } from 'net';
|
||||||
|
import { IRCUser } from './IRCUser.js';
|
||||||
|
import { IRCMessage, parseIRCMessage } from './Message.js';
|
||||||
|
import numerics from './numerics.js';
|
||||||
|
import { Server } from './Server.js';
|
||||||
|
|
||||||
|
export class Client {
|
||||||
|
user: IRCUser|null
|
||||||
|
capVersion: string
|
||||||
|
enabledCaps: Map<string, string>
|
||||||
|
allCaps: Map<string, string>
|
||||||
|
localNick: string
|
||||||
|
localUsername: string
|
||||||
|
localRealname: string
|
||||||
|
constructor(private socket: Socket, public server: Server) {
|
||||||
|
this.user = null;
|
||||||
|
this.capVersion = '301';
|
||||||
|
this.enabledCaps = new Map();
|
||||||
|
this.allCaps = new Map([
|
||||||
|
["account-tag", ""],
|
||||||
|
["batch", ""],
|
||||||
|
["draft/chathistory", ""],
|
||||||
|
["echo-message", ""],
|
||||||
|
["draft/event-playback", ""],
|
||||||
|
["invite-notify", ""],
|
||||||
|
["message-tags", ""],
|
||||||
|
["sasl", "PLAIN"],
|
||||||
|
["server-time", ""],
|
||||||
|
]);
|
||||||
|
this.localNick = 'none';
|
||||||
|
this.localUsername = 'none';
|
||||||
|
this.localRealname = 'none';
|
||||||
|
this.socket.on('data', (data) => this.receiveData(data));
|
||||||
|
//this.socket.on('close', (e) => {if (this.user) this.user.handleClientClose(this, e)});
|
||||||
|
}
|
||||||
|
|
||||||
|
receiveData(data: Buffer|String) {
|
||||||
|
const dataArray = data.toString().split('\r\n');
|
||||||
|
dataArray.forEach(m => {
|
||||||
|
const trimmedMsg = m.replace('\r', '').replace('\n', '');
|
||||||
|
if (trimmedMsg !== '')
|
||||||
|
this.routeMessage(trimmedMsg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
routeMessage(data: string) {
|
||||||
|
const message = parseIRCMessage(data);
|
||||||
|
switch (message.command.toUpperCase()) {
|
||||||
|
case 'AUTHENTICATE': {
|
||||||
|
this.doAUTHENTICATE(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'CAP': {
|
||||||
|
this.doCAP(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCapString(capVersion: string) {
|
||||||
|
let capArray = [];
|
||||||
|
for (const [key, value] of this.allCaps.entries()) {
|
||||||
|
if (capVersion === '301' || value.length === 0) {
|
||||||
|
capArray.push(key);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
capArray.push(`${key}=${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return capArray.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
doCAP(message: IRCMessage) {
|
||||||
|
switch (message.params[0]) {
|
||||||
|
case 'LS': {
|
||||||
|
if (message.params.length === 2) {
|
||||||
|
this.capVersion = message.params[1];
|
||||||
|
}
|
||||||
|
this.sendMessage(this.server.name, "CAP", ["*", "LS", this.getCapString(this.capVersion)], message.tags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'LIST': {
|
||||||
|
this.sendMessage(this.server.name, "CAP", ["*", "LIST", this.getCapString(this.capVersion)], message.tags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'REQ': {
|
||||||
|
const capsToChange = (message.params[1].indexOf(' ') === -1) ? [message.params[1]] : message.params[1].split(' ');
|
||||||
|
const capsEnabled: string[] = [];
|
||||||
|
capsToChange.forEach(cap => {
|
||||||
|
if (cap in this.allCaps) {
|
||||||
|
this.enabledCaps.set(cap, '');
|
||||||
|
capsEnabled.push(cap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.sendMessage(this.server.name, "CAP", ["*", "ACK", capsEnabled.join(' ')], message.tags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'END': {
|
||||||
|
if (this.user !== null) {
|
||||||
|
this.doRegistration(message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.closeConnectionWithError("You must use SASL to connect to this server");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doAUTHENTICATE(message: IRCMessage) {
|
||||||
|
if (message.params[0] === "PLAIN") {
|
||||||
|
this.sendMessage("", "AUTHENTICATE", ["+"], message.tags);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const authArray = Buffer.from(message.params[0], 'base64').toString('utf-8').split('\0');
|
||||||
|
if (!authArray || authArray.length !== 3) {
|
||||||
|
this.sendMessage(this.server.name, '904', numerics['904']('*'), message.tags)
|
||||||
|
this.closeConnectionWithError('Invalid authentication')
|
||||||
|
}
|
||||||
|
const mxid = authArray[1];
|
||||||
|
const accessToken = authArray[2];
|
||||||
|
const thisIRCUser = this.server.ircUsers.get(mxid) || new IRCUser(this, mxid, accessToken);
|
||||||
|
if (thisIRCUser.isAuthed) {
|
||||||
|
if (!thisIRCUser.verifyCredentials(accessToken)) {
|
||||||
|
this.sendMessage(this.server.name, '904', numerics['904']('*'), message.tags)
|
||||||
|
this.closeConnectionWithError('Invalid authentication')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.user = thisIRCUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doRegistration(message: IRCMessage) {
|
||||||
|
if (this.user === null) {
|
||||||
|
this.closeConnectionWithError('Authentication failed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.sendMessage(this.server.name, '001', numerics['001'](this.user.nick, this.server.name), message.tags);
|
||||||
|
this.sendMessage(this.server.name, '002', numerics['002'](this.user.nick, this.server.name, '0.0.1'), message.tags);
|
||||||
|
this.sendMessage(this.server.name, '003', numerics['003'](this.user.nick, 'yesterday'), message.tags);
|
||||||
|
this.sendMessage(this.server.name, '004', numerics['004'](this.user.nick, this.server.name, '0.0.1', 'i', 'Lhionpsv'), message.tags);
|
||||||
|
const iSupportArray = [
|
||||||
|
'CASEMAPPING=ascii',
|
||||||
|
'CHANMODES=,,,Linps',
|
||||||
|
'CHANTYPES=#&!',
|
||||||
|
'MAXTARGETS=1',
|
||||||
|
'PREFIX=(ohv)@%+',
|
||||||
|
]
|
||||||
|
if (this.enabledCaps.has('draft/chathistory')) {
|
||||||
|
iSupportArray.push('CHATHISTORY=50');
|
||||||
|
}
|
||||||
|
this.sendMessage(this.server.name, '005', numerics['005'](this.user.nick, iSupportArray), message.tags);
|
||||||
|
|
||||||
|
this.sendMessage(this.server.name, '375', numerics['375'](this.user.nick), message.tags);
|
||||||
|
this.sendMessage(this.server.name, '372', numerics['372'](this.user.nick, "It's an MOTD"), message.tags);
|
||||||
|
this.sendMessage(this.server.name, '376', numerics['376'](this.user.nick), message.tags);
|
||||||
|
|
||||||
|
this.sendMessage(this.user.nick, 'MODE', [this.user.nick, '+i'], message.tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(prefix: string, command: string, params: string[], tags: Map<string, string>) {
|
||||||
|
const capTagMapping = new Map([
|
||||||
|
['account', 'account-tag'],
|
||||||
|
['label', 'labeled-response'],
|
||||||
|
['msgid', 'message-tags'],
|
||||||
|
['reflectionircd.chat/delete-message', 'reflectionircd.chat/delete-message'],
|
||||||
|
['reflectionircd.chat/edit-message', 'reflectionircd.chat/edit-message'],
|
||||||
|
['time', 'server-time'],
|
||||||
|
])
|
||||||
|
const ourTags: Map<string, string> = new Map();
|
||||||
|
if (this.enabledCaps.has('server-time') && !tags.has('time'))
|
||||||
|
ourTags.set('time', new Date().toISOString());
|
||||||
|
|
||||||
|
tags.forEach((v, k) => {
|
||||||
|
if (k.startsWith('+')) {
|
||||||
|
if (this.enabledCaps.has('message-tags')) {
|
||||||
|
ourTags.set(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const capToCheck = capTagMapping.get(k) || '';
|
||||||
|
if (this.enabledCaps.has(capToCheck)) {
|
||||||
|
ourTags.set(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const newMsg = new IRCMessage(ourTags, prefix, command, params);
|
||||||
|
const msgToSend = newMsg.toString();
|
||||||
|
console.log(`SENT: ${msgToSend}`);
|
||||||
|
this.socket.write(`${msgToSend}\r\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeConnectionWithError(message: string) {
|
||||||
|
this.sendMessage(this.server.name, 'ERROR', [message], new Map());
|
||||||
|
this.socket.destroy();
|
||||||
|
}
|
||||||
|
}
|
35
src/IRCUser.ts
Normal file
35
src/IRCUser.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { Channel } from "./Channel.js";
|
||||||
|
import { Client } from "./Client.js";
|
||||||
|
import { MatrixUser } from "./MatrixUser.js";
|
||||||
|
import { IRCMessage } from "./Message.js";
|
||||||
|
import { Server } from "./Server.js";
|
||||||
|
|
||||||
|
export class IRCUser {
|
||||||
|
private clients: Set<Client>
|
||||||
|
private channels: Map<string, Channel>
|
||||||
|
private server: Server
|
||||||
|
private matrixUser: MatrixUser|null
|
||||||
|
public nick: string
|
||||||
|
private ident: string
|
||||||
|
private hostname: string
|
||||||
|
public accountName: string
|
||||||
|
public isAuthed: boolean
|
||||||
|
private txnIdStore: Set<string>
|
||||||
|
constructor(private initialClient: Client, public mxid: string, private accessToken: string) {
|
||||||
|
this.clients = new Set([initialClient]);
|
||||||
|
this.channels = new Map();
|
||||||
|
this.server = initialClient.server;
|
||||||
|
this.matrixUser = null;
|
||||||
|
const mxidSplit = mxid.split(':')
|
||||||
|
this.nick = mxidSplit[0].substr(1);
|
||||||
|
this.ident = this.nick;
|
||||||
|
this.hostname = mxidSplit[1];
|
||||||
|
this.accountName = mxid.slice(1);
|
||||||
|
this.isAuthed = false;
|
||||||
|
this.txnIdStore = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyCredentials(accessToken: string): boolean {
|
||||||
|
return accessToken === this.accessToken;
|
||||||
|
}
|
||||||
|
}
|
32
src/MatrixUser.ts
Normal file
32
src/MatrixUser.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { Channel } from "./Channel.js"
|
||||||
|
|
||||||
|
export class MatrixUser {
|
||||||
|
public ident: string
|
||||||
|
public hostname: string
|
||||||
|
public realname: string
|
||||||
|
public accountName: string
|
||||||
|
public channels: Set<Channel>
|
||||||
|
constructor(public mxid: string, public nick: string) {
|
||||||
|
const mxidSplit = this.mxid.split(':')
|
||||||
|
this.ident = mxidSplit[0].substr(1);
|
||||||
|
this.hostname = mxidSplit[1];
|
||||||
|
this.realname = this.mxid;
|
||||||
|
this.accountName = this.mxid.slice(1);
|
||||||
|
this.channels = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
getMask(): string {
|
||||||
|
return `${this.nick}!${this.ident}@${this.hostname}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
addChannel(channel: Channel) {
|
||||||
|
this.channels.add(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
setRealname(realname: string) {
|
||||||
|
if (this.realname === realname.substring(0, 64))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.realname = realname.substring(0, 64);
|
||||||
|
}
|
||||||
|
}
|
123
src/Message.ts
Normal file
123
src/Message.ts
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
export class IRCMessage {
|
||||||
|
constructor(
|
||||||
|
public tags: Map<string, string>,
|
||||||
|
public prefix: string,
|
||||||
|
public command: string,
|
||||||
|
public params: string[],
|
||||||
|
) {}
|
||||||
|
toString() {
|
||||||
|
let messageString = '';
|
||||||
|
if (this.tags.size !== 0) {
|
||||||
|
let tagArray = [];
|
||||||
|
for (const [key, value] of this.tags) {
|
||||||
|
if (value === '') {
|
||||||
|
tagArray.push(key);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tagArray.push(`${key}=${encodeTag(value)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messageString = `@${tagArray.join(";")} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.prefix) {
|
||||||
|
messageString = `${messageString}:${this.prefix} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
messageString = `${messageString}${this.command}`;
|
||||||
|
|
||||||
|
const params = this.params.slice();
|
||||||
|
|
||||||
|
let lastParam = params.pop();
|
||||||
|
if (lastParam) {
|
||||||
|
if (lastParam.indexOf(' ') !== -1) {
|
||||||
|
lastParam = `:${lastParam}`;
|
||||||
|
}
|
||||||
|
params.push(lastParam);
|
||||||
|
}
|
||||||
|
messageString = `${messageString} ${params.join(' ')}`;
|
||||||
|
return messageString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeTag(value: string): string {
|
||||||
|
const tagDecodeMap = new Map([
|
||||||
|
['\\:', ';'],
|
||||||
|
['\\s', ' '],
|
||||||
|
['\\\\', '\\'],
|
||||||
|
['\\r', '\r'],
|
||||||
|
['\\n', '\n'],
|
||||||
|
['\\', ''],
|
||||||
|
]);
|
||||||
|
return value.replace(/\\:|\\s|\\\\|\\r|\\n|\\/gi, t => tagDecodeMap.get(t) || '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeTag(value: string): string {
|
||||||
|
const tagEncodeMap = new Map([
|
||||||
|
[';', '\\:'],
|
||||||
|
[' ', '\\s'],
|
||||||
|
['\\', '\\\\'],
|
||||||
|
['\r', '\\r'],
|
||||||
|
['\n', '\\n'],
|
||||||
|
]);
|
||||||
|
return value.replace(/;| |\\|\r|\n/gi, t => tagEncodeMap.get(t) || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToTags(key: string): boolean {
|
||||||
|
const tagsToPass = [
|
||||||
|
'batch',
|
||||||
|
'label',
|
||||||
|
]
|
||||||
|
return (tagsToPass.includes(key) || key.startsWith('+'));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseIRCMessage(rawLine: string) {
|
||||||
|
console.log(`RAW: ${rawLine}`);
|
||||||
|
let restOfMessage = rawLine;
|
||||||
|
let parsedTags: Map<string, string> = new Map();
|
||||||
|
let prefix = '';
|
||||||
|
let command = '';
|
||||||
|
let params: string[] = [];
|
||||||
|
if (rawLine.startsWith('@')) {
|
||||||
|
const tags = restOfMessage.substr(0, restOfMessage.indexOf(' '));
|
||||||
|
restOfMessage = restOfMessage.substr(restOfMessage.indexOf(' ')+1);
|
||||||
|
|
||||||
|
for (const tag in tags.split(';')) {
|
||||||
|
const valueSplit = tag.indexOf('=');
|
||||||
|
if (valueSplit === -1 && addToTags(tag)) {
|
||||||
|
parsedTags.set(tag, '');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const key = tag.substr(0, valueSplit);
|
||||||
|
const value = tag.substr(valueSplit);
|
||||||
|
if (addToTags(key))
|
||||||
|
parsedTags.set(key, decodeTag(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (restOfMessage.startsWith(':')) {
|
||||||
|
prefix = restOfMessage.substr(0, restOfMessage.indexOf(' '));
|
||||||
|
restOfMessage = restOfMessage.substr(restOfMessage.indexOf(' ')+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restOfMessage.indexOf(' ') === -1) {
|
||||||
|
command = restOfMessage;
|
||||||
|
console.log(parsedTags, prefix, command, params);
|
||||||
|
return new IRCMessage(parsedTags, prefix, command, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
command = restOfMessage.substr(0, restOfMessage.indexOf(' '));
|
||||||
|
restOfMessage = restOfMessage.substr(restOfMessage.indexOf(' ') + 1);
|
||||||
|
|
||||||
|
let lastParam = '';
|
||||||
|
if (restOfMessage.indexOf(' :') !== -1) {
|
||||||
|
lastParam = restOfMessage.substr(restOfMessage.indexOf(' :') + 2);
|
||||||
|
restOfMessage = restOfMessage.substr(0, restOfMessage.indexOf(' :'));
|
||||||
|
}
|
||||||
|
|
||||||
|
params = restOfMessage.split(' ');
|
||||||
|
if (lastParam !== '') {
|
||||||
|
params.push(lastParam);
|
||||||
|
}
|
||||||
|
console.log(parsedTags, prefix, command, params);
|
||||||
|
return new IRCMessage(parsedTags, prefix, command, params);
|
||||||
|
}
|
24
src/Server.ts
Normal file
24
src/Server.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { Channel } from "./Channel.js"
|
||||||
|
import { IRCUser } from "./IRCUser.js"
|
||||||
|
import { MatrixUser } from "./MatrixUser.js"
|
||||||
|
|
||||||
|
export class Server {
|
||||||
|
public name: string
|
||||||
|
// <roomId, Channel>
|
||||||
|
public matrixRooms: Map<string, Channel>
|
||||||
|
// <roomAlias (fallback to roomId), Channel>
|
||||||
|
public ircChannels: Map<string, Channel>
|
||||||
|
// <mxid, MatrixUser>
|
||||||
|
public matrixUsers: Map<string, MatrixUser>
|
||||||
|
// <mxid, IRCUser>
|
||||||
|
public ircUsers: Map<string, IRCUser>
|
||||||
|
public nickToMxid: Map<string, string>
|
||||||
|
constructor(public config: any) {
|
||||||
|
this.name = this.config.serverName;
|
||||||
|
this.matrixRooms = new Map();
|
||||||
|
this.ircChannels = new Map();
|
||||||
|
this.matrixUsers = new Map();
|
||||||
|
this.ircUsers = new Map();
|
||||||
|
this.nickToMxid = new Map();
|
||||||
|
}
|
||||||
|
}
|
52
src/numerics.ts
Normal file
52
src/numerics.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
const numerics = {
|
||||||
|
"001": (nick: string, serverName: string) => {
|
||||||
|
return [nick, `Welcome to the ${serverName} network, ${nick}`]
|
||||||
|
},
|
||||||
|
"002": (nick: string, serverName: string, version: string) => {
|
||||||
|
return [nick, `Your host is ${serverName}, running version ${version}`]
|
||||||
|
},
|
||||||
|
"003": (nick: string, createdTime: string) => {
|
||||||
|
return [nick, `This server was created ${createdTime}`]
|
||||||
|
},
|
||||||
|
"004": (nick: string, serverName: string, version: string, umodes: string, cmodes: string) => {
|
||||||
|
return [nick, serverName, version, umodes, cmodes]
|
||||||
|
},
|
||||||
|
"005": (nick: string, isupportArray: string[]) => {
|
||||||
|
return [nick, `${isupportArray.join(' ')} :are supported by this server`]
|
||||||
|
},
|
||||||
|
"221": (nick: string, umode: string) => {
|
||||||
|
return [nick, umode]
|
||||||
|
},
|
||||||
|
"324": (nick: string, channel: string, modeString: string) => {
|
||||||
|
return [nick, channel, modeString];
|
||||||
|
},
|
||||||
|
"353": (nick: string, symbol: string, channel: string, nameArray: string[]) => {
|
||||||
|
return [nick, symbol, channel, `${nameArray.join(' ')}`]
|
||||||
|
},
|
||||||
|
"366": (nick: string, channel: string) => {
|
||||||
|
return [nick, channel, "End of /NAMES list"]
|
||||||
|
},
|
||||||
|
"372": (nick: string, motdLine: string) => {
|
||||||
|
return [nick, motdLine]
|
||||||
|
},
|
||||||
|
"375": (nick: string) => {
|
||||||
|
return [nick, `- Start of MOTD`]
|
||||||
|
},
|
||||||
|
"376": (nick: string) => {
|
||||||
|
return [nick, "End of MOTD"]
|
||||||
|
},
|
||||||
|
"433": (nick: string, otherNick: string) => {
|
||||||
|
return [nick, otherNick, "You can't change nicks on IRC yet"]
|
||||||
|
},
|
||||||
|
"900": (mask: string, nick: string) => {
|
||||||
|
return [nick, mask, `You are now logged in as ${nick}`]
|
||||||
|
},
|
||||||
|
"903": (nick: string) => {
|
||||||
|
return [nick, "SASL authentication successful"]
|
||||||
|
},
|
||||||
|
"904": (nick: string) => {
|
||||||
|
return [nick, "SASL authentication failed"]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default numerics;
|
Loading…
Add table
Reference in a new issue