import axios from "axios"; import { randomUUID } from "crypto"; import { Channel } from "./Channel.js"; import { Client } from "./Client.js"; import { IRCMessage } from "./Message.js"; import { Server } from "./Server.js"; export class IRCUser { private clients: Set public channels: Map public nick: string private ident: string private hostname: string public accountName: string public realname: string private txnIdStore: Map public nextBatch: string private initialSync: boolean private syncIntervalID: NodeJS.Timeout; constructor(public mxid: string, private accessToken: string, private server: Server) { this.clients = new Set(); this.channels = new Map(); const mxidSplit = mxid.split(':') this.nick = mxidSplit[0].substr(1); this.ident = this.nick; this.hostname = mxidSplit[1]; this.accountName = mxid.slice(1); this.realname = this.accountName; this.txnIdStore = new Map(); this.nextBatch = ""; this.initialSync = false; this.syncIntervalID = setInterval(this.doSync.bind(this), 15000); } isSynced() { return this.nextBatch !== ""; } getVerification() { return axios.get(`https://matrix.org/_matrix/client/v3/account/whoami?access_token=${this.accessToken}`); } getMask(): string { return `${this.nick}!${this.ident}@${this.hostname}`; } addClient(client: Client, passedTags: Map) { this.clients.add(client); if (this.nextBatch !== "") { for (const channel of this.channels.values()) { channel.joinNewIRCClient(client, passedTags); } } } doSync(): void { if (!this.isSynced()) { console.log("not syncing, initial sync not completed"); return; } const endpoint = `https://matrix.org/_matrix/client/v3/sync?access_token=${this.accessToken}&since=${this.nextBatch}`; axios.get(endpoint).then(response => { const data = response.data; this.nextBatch = data.next_batch; const rooms = data.rooms; if (rooms && rooms['join']) { for (const roomId of Object.keys(rooms.join)) { const targetChannel = this.server.getOrCreateIRCChannel(roomId); rooms.join[roomId].timeline.events.forEach((nextEvent: any) => { targetChannel.routeMatrixEvent(nextEvent) }); } } }) } sendMessageToMatrix(message: IRCMessage, client: Client) { const channel = this.server.matrixRooms.get(message.params[0]); if (!channel) { return; } let msgtype = 'm.text'; let msgbody = message.params[1]; if (message.command === 'NOTICE') msgtype = 'm.notice'; else if (message.params[1].startsWith('\x01')) { msgtype = 'm.emote'; msgbody = msgbody.replace(/\x01(ACTION\s)?/gi, ''); } const roomId = channel.roomId; const content = { "body": msgbody, "msgtype": msgtype, } const newTxnid = randomUUID(); this.txnIdStore.set(newTxnid, client); axios.put(`https://matrix.org/_matrix/client/v3/rooms/${channel.roomId}/send/m.room.message/${newTxnid}?access_token=${this.accessToken}`, content); } sendToAll(prefix: string, command: string, params: string[], tags: Map = 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 = 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 = new Map(), skipClient: Client|null = null) { this.clients.forEach(client => { if (client !== skipClient && !client.enabledCaps.has(cap)) { client.sendMessage(prefix, command, params, tags); } }) } }