diff --git a/example-config.yaml b/example-config.yaml index 9b82f22e..7d426c46 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -19,6 +19,8 @@ appservice: bridge: # ${ID} is replaced with the user ID of the Telegram user. username_template: "telegram_${ID}" + # ${DISPLAYNAME} is replaced with the display name of the Telegram user. + displayname_template: "${DISPLAYNAME} (Telegram)" bot_username: telegrambot # The displayname to set to the bot automatically. bot_displayname: Telegram Bridge diff --git a/src/app.js b/src/app.js index 516f76af..8bf807cb 100644 --- a/src/app.js +++ b/src/app.js @@ -56,6 +56,7 @@ class MautrixTelegram { const user = MatrixUser.fromEntry(this, entry) this.matrixUsersByID.set(entry.id, user) } + // FIXME this doesn't work for setting the displayname of the bot. // .then(() => // this.botIntent.setDisplayName(this.config.bridge.bot_displayname)) } @@ -198,13 +199,13 @@ class MautrixTelegram { putRoom(room) { const entry = room.toEntry() - return this.bridge.getUserStore().upsert({ + return this.bridge.getRoomStore().upsert({ type: entry.type, id: entry.id, }, entry) } - handleMatrixEvent(evt) { + async handleMatrixEvent(evt) { const asBotID = this.bridge.getBot().getUserId() if (evt.type === "m.room.member" && evt.state_key === asBotID) { if (evt.content.membership === "invite") { @@ -219,27 +220,34 @@ class MautrixTelegram { } return } + if (evt.sender === asBotID || evt.type !== "m.room.message" || !evt.content) { // Ignore own messages and non-message events. return; } + const cmdprefix = this.config.bridge.command_prefix if (evt.content.body.startsWith(cmdprefix + " ")) { - this.getMatrixUser(evt.sender).then(user => { - if (!user.whitelisted) { - this.botIntent.sendText(evt.room_id, "You are not authorized to use this bridge.") - return - } + const user = await this.getMatrixUser(evt.sender) + if (!user.whitelisted) { + this.botIntent.sendText(evt.room_id, "You are not authorized to use this bridge.") + return + } - const prefixLength = cmdprefix.length + 1 - const args = evt.content.body.substr(prefixLength).split(" ") - const command = args.shift() - commands.run(user, command, args, reply => + const prefixLength = cmdprefix.length + 1 + const args = evt.content.body.substr(prefixLength).split(" ") + const command = args.shift() + commands.run(user, command, args, reply => this.botIntent.sendText( evt.room_id, reply.replace("$cmdprefix", cmdprefix)), - this) - }) + this) + return + } + + const portal = await this.getPortalByRoomID(evt.room_id) + if (portal) { + portal.handleMatrixEvent(evt) return } } diff --git a/src/commands.js b/src/commands.js index 0fc5f615..98ba8faa 100644 --- a/src/commands.js +++ b/src/commands.js @@ -113,7 +113,6 @@ commands.login = async (sender, args, reply) => { action: "Phone code authentication", next: enterCode, } - console.log(data) } catch (err) { reply(`Failed to send code: ${err}`) console.log(err) diff --git a/src/matrix-user.js b/src/matrix-user.js index 16b6bbb9..284939e3 100644 --- a/src/matrix-user.js +++ b/src/matrix-user.js @@ -116,13 +116,19 @@ class MatrixUser { async syncDialogs() { const dialogs = await this.telegramPuppet.client("messages.getDialogs", {}) + let changed = false for (const dialog of dialogs.chats) { + if (dialog._ === "chatForbidden" || dialog.deactivated) { + continue + } const peer = new TelegramPeer(dialog._, dialog.id) const portal = await this.app.getPortalByPeer(peer) if (portal.updateInfo(this.telegramPuppet, dialog)) { portal.save() + changed = true } } + return changed } async sendTelegramCode(phoneNumber) { diff --git a/src/portal.js b/src/portal.js index 2fac2c2e..d26dc075 100644 --- a/src/portal.js +++ b/src/portal.js @@ -25,6 +25,10 @@ class Portal { this.accessHashes = new Map() } + get id() { + return this.peer.id + } + static fromEntry(app, entry) { if (entry.type !== "portal") { throw new Error("MatrixUser can only be created from entry type \"portal\"") @@ -37,43 +41,74 @@ class Portal { return portal } + async syncParticipants(participants) { + for (const participant of participants) { + const user = this.app.getTelegramUser(participant.id) + if (user.updateInfo(participant)) { + user.save() + } + user.intent.join(this.roomID) + } + } + + handleMatrixEvent(evt) { + console.log("Received message from Matrix to portal with room ID", this.roomID) + console.log(evt) + } + async createMatrixRoom(telegramPOV) { if (this.roomID) { - return + return this.roomID } try { - await this.peer.getInfo(telegramPOV) + const {info, participants} = await this.peer.getInfo(telegramPOV) + console.log(JSON.stringify(info, "", " ")) + console.log(JSON.stringify(participants, "", " ")) + + const room = await this.app.botIntent.createRoom({ + options: { + name: info.title, + visibility: "private", + } + }) + + this.roomID = room.room_id + this.app.portalsByRoomID.set(this.roomID, this) + await this.save() + + // TODO other things? + + return this.roomID } catch (err) { console.error(err) console.error(err.stack) + return undefined } } updateInfo(telegramPOV, dialog) { let changed = false if (this.peer.type === "channel") { - + if (telegramPOV && this.accessHashes.get(telegramPOV.userID) !== +dialog.access_hash) { + this.accessHashes.set(telegramPOV.userID, +dialog.access_hash) + changed = true + } } - if (telegramPOV && this.accessHashes.get(telegramPOV.userID) !== +dialog.access_hash) { - this.accessHashes.set(telegramPOV.userID, +dialog.access_hash) - changed = true - } - if (this.title !== dialog.title) { - this.title = dialog.title - changed = true - } - return changed + return this.peer.updateInfo(dialog) || changed } toEntry() { return { type: this.type, - id: this.roomID, - peer: this.peer.toSubentry(), - accessHashes: this.peer.type === "channel" - ? Array.from(this.accessHashes) - : undefined, + id: this.id, + data: { + roomID: this.roomID, + peer: this.peer.toSubentry(), + accessHashes: this.peer.type === "channel" + ? Array.from(this.accessHashes) + : undefined, + } } } diff --git a/src/telegram-peer.js b/src/telegram-peer.js index b62048ca..67ce30b2 100644 --- a/src/telegram-peer.js +++ b/src/telegram-peer.js @@ -19,6 +19,8 @@ class TelegramPeer { this.type = type this.id = id this.accessHash = +(accessHash || 0) + this.username = undefined + this.title = undefined } static fromTelegramData(peer) { @@ -52,7 +54,21 @@ class TelegramPeer { } return false } + } + updateInfo(dialog) { + let changed = false + if (this.type === "channel") { + if (this.username !== dialog.username) { + this.username = dialog.username + changed = true + } + } + if (this.title !== dialog.title) { + this.title = dialog.title + changed = true + } + return changed } async getInfo(telegramPOV) { @@ -64,6 +80,7 @@ class TelegramPeer { info = await telegramPOV.client("messages.getFullChat", { chat_id: this.id, }) + participants = info.users break case "channel": // FIXME I'm broken (Error: CHANNEL_INVALID) @@ -80,8 +97,7 @@ class TelegramPeer { default: throw new Error(`Unknown peer type ${this.type}`) } - console.log(JSON.stringify(info, "", " ")) - console.log(JSON.stringify(participants, "", " ")) + return { info, participants } } toInputPeer() { @@ -121,13 +137,18 @@ class TelegramPeer { } static fromSubentry(entry) { - return new TelegramPeer(entry.type, entry.id) + const peer = new TelegramPeer(entry.type, entry.id) + peer.username = entry.username + peer.title = entry.title + return peer } toSubentry() { return { type: this.type, id: this.id, + username: this.username, + title: this.title, } } diff --git a/src/telegram-puppet.js b/src/telegram-puppet.js index 1108cf68..d03a9e82 100644 --- a/src/telegram-puppet.js +++ b/src/telegram-puppet.js @@ -136,7 +136,8 @@ class TelegramPuppet { }) return this.signInComplete(result) } catch (err) { - if (err.message !== "SESSION_PASSWORD_NEEDED") { + if (err.type !== "SESSION_PASSWORD_NEEDED" && err.message !== "SESSION_PASSWORD_NEEDED") { + console.error("Unknown login error:", JSON.stringify(err, "", " ")) throw err } const password = await @@ -156,7 +157,8 @@ class TelegramPuppet { getDisplayName() { if (this.data.firstName || this.data.lastName) { - return `${this.data.firstName} ${this.data.lastName}` + return [this.firstName, this.lastName].filter(s => !!s) + .join(" ") } else if (this.data.username) { return this.data.username } @@ -285,10 +287,15 @@ class TelegramPuppet { console.error("Failed to update contacts:", err) } try { - console.log("Syncing dialogs...") - await this.matrixUser.syncDialogs() + console.log("Updating dialogs...") + const changed = await this.matrixUser.syncDialogs() + if (!changed) { + console.log("Dialogs were up-to-date") + } else { + console.log("Dialogs updated") + } } catch (err) { - console.error("Failed to sync dialogs:", err) + console.error("Failed to update dialogs:", err) } setInterval(async () => { try {