Implement room and user creation

This commit is contained in:
Tulir Asokan
2017-11-16 11:54:01 +02:00
parent eb2de79a4b
commit 9d705bd5c8
6 changed files with 137 additions and 47 deletions
+23 -5
View File
@@ -135,11 +135,29 @@ commands.logout = async (sender, args, reply) => {
const TelegramPeer = require("./telegram-peer")
const Portal = require("./portal")
commands.test = async (sender, args, reply, app) => {
const peer = new TelegramPeer(args[0], +args[1])
const hashFound = await peer.getAccessHash(app, sender.telegramPuppet)
reply("Access hash found: " + hashFound)
new Portal(app, "", peer).createMatrixRoom(sender.telegramPuppet)
commands.createRoom = async (sender, args, reply, app) => {
let peer = new TelegramPeer(args[0], +args[1])
const portal = await app.getPortalByPeer(peer)
const roomID = await portal.createMatrixRoom(sender.telegramPuppet)
if (!roomID) {
reply("Failed to create room.")
return
}
await app.botIntent.invite(roomID, sender.userID)
reply(`Created room ${roomID} and invited ${sender.userID}`)
}
commands.syncUsers = async (sender, args, reply, app) => {
let peer = new TelegramPeer(args[0], +args[1])
const portal = await app.getPortalByPeer(peer)
try {
await portal.syncTelegramUsers(sender.telegramPuppet)
reply("Users synchronized successfully.")
} catch (err) {
reply(`Failed to sync users: ${err}`)
console.error(err)
console.error(err.stack)
}
}
//////////////////////////////
+2 -5
View File
@@ -104,9 +104,7 @@ class MatrixUser {
}
for (const [index, contact] of Object.entries(contacts.users)) {
const telegramUser = await this.app.getTelegramUser(contact.id)
if (telegramUser.updateInfo(this.telegramPuppet, contact)) {
telegramUser.save()
}
await telegramUser.updateInfo(this.telegramPuppet, contact)
contacts.users[index] = telegramUser
}
this.contacts = contacts.users
@@ -123,8 +121,7 @@ class MatrixUser {
}
const peer = new TelegramPeer(dialog._, dialog.id)
const portal = await this.app.getPortalByPeer(peer)
if (portal.updateInfo(this.telegramPuppet, dialog)) {
portal.save()
if (await portal.updateInfo(this.telegramPuppet, dialog)) {
changed = true
}
}
+24 -12
View File
@@ -41,14 +41,24 @@ 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()
async syncTelegramUsers(telegramPOV, users) {
if (!users) {
if (! await this.loadAccessHash(telegramPOV)) {
return false
}
user.intent.join(this.roomID)
const data = await this.peer.getInfo(telegramPOV)
users = data.users
}
for (const userData of users) {
const user = await this.app.getTelegramUser(userData.id)
await user.updateInfo(telegramPOV, userData)
await user.intent.join(this.roomID)
}
return true
}
loadAccessHash(telegramPOV) {
return this.peer.loadAccessHash(this.app, telegramPOV, {portal: this})
}
handleMatrixEvent(evt) {
@@ -62,9 +72,11 @@ class Portal {
}
try {
const {info, participants} = await this.peer.getInfo(telegramPOV)
console.log(JSON.stringify(info, "", " "))
console.log(JSON.stringify(participants, "", " "))
if (! await this.loadAccessHash(telegramPOV)) {
return undefined
}
const {info, users} = await this.peer.getInfo(telegramPOV)
const room = await this.app.botIntent.createRoom({
options: {
@@ -77,7 +89,7 @@ class Portal {
this.app.portalsByRoomID.set(this.roomID, this)
await this.save()
// TODO other things?
await this.syncTelegramUsers(telegramPOV, users)
return this.roomID
} catch (err) {
@@ -90,8 +102,8 @@ class Portal {
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)
if (telegramPOV && this.accessHashes.get(telegramPOV.userID) !== dialog.access_hash) {
this.accessHashes.set(telegramPOV.userID, dialog.access_hash)
changed = true
}
}
+31 -14
View File
@@ -18,7 +18,7 @@ class TelegramPeer {
constructor(type, id, accessHash) {
this.type = type
this.id = id
this.accessHash = +(accessHash || 0)
this.accessHash = accessHash
this.username = undefined
this.title = undefined
}
@@ -28,26 +28,37 @@ class TelegramPeer {
case "peerChat":
return new TelegramPeer("chat", peer.chat_id)
case "peerUser":
return new TelegramPeer("user", peer.user_id, peer.access_hash || 0)
return new TelegramPeer("user", peer.user_id, peer.access_hash)
case "peerChannel":
return new TelegramPeer("channel", peer.channel_id, peer.access_hash || 0)
return new TelegramPeer("channel", peer.channel_id, peer.access_hash)
default:
throw new Error(`Unrecognized peer type ${peer._}`)
}
}
async getAccessHash(app, telegramPOV) {
if (this.type === "chat" || this.accessHash > 0) {
/**
* Load the access hash for a specific puppeted Telegram user from the channel portal or TelegramUser info.
*
* @param {MautrixTelegram} app The instance of {@link MautrixTelegram} to use.
* @param {TelegramPuppet} telegramPOV The puppeted Telegram user for whom the access hash is needed.
* @param {Portal} [portal] Optional channel {@link Portal} instance to avoid calling {@link app#getPortalByPeer(peer)}.
* Only used if {@link #type} is {@linkplain user}.
* @param {TelegramUser} [user] Optional {@link TelegramUser} instance to avoid calling {@link app#getTelegramUser(id)}.
* Only used if {@link #type} is {@linkplain channel}.
* @returns {Promise<boolean>} Whether or not the access hash was found and loaded.
*/
async loadAccessHash(app, telegramPOV, { portal, user }) {
if (this.type === "chat") {
return true
} else if (this.type === "user") {
const user = await app.getTelegramUser(this.id)
user = user || await app.getTelegramUser(this.id)
if (user.accessHashes.has(telegramPOV.userID)) {
this.accessHash = user.accessHashes.get(telegramPOV.userID)
return true
}
return false
} else if (this.type === "channel") {
const portal = await app.getPortalByPeer(this)
portal = portal || await app.getPortalByPeer(this)
if (portal.accessHashes.has(telegramPOV.userID)) {
this.accessHash = portal.accessHashes.get(telegramPOV.userID)
return true
@@ -56,7 +67,7 @@ class TelegramPeer {
}
}
updateInfo(dialog) {
async updateInfo(dialog) {
let changed = false
if (this.type === "channel") {
if (this.username !== dialog.username) {
@@ -68,11 +79,14 @@ class TelegramPeer {
this.title = dialog.title
changed = true
}
if (changed) {
this.save()
}
return changed
}
async getInfo(telegramPOV) {
let info, participants
let info, users
switch(this.type) {
case "user":
throw new Error("Can't get chat info of user")
@@ -80,24 +94,27 @@ class TelegramPeer {
info = await telegramPOV.client("messages.getFullChat", {
chat_id: this.id,
})
participants = info.users
users = info.users
break
case "channel":
// FIXME I'm broken (Error: CHANNEL_INVALID)
info = await telegramPOV.client("channels.getFullChannel", {
channel: this.toInputChannel(),
})
participants = await telegramPOV.client("channels.getParticipants", {
const participants = await telegramPOV.client("channels.getParticipants", {
channel: this.toInputChannel(),
filter: { _: "channelParticipantsRecent" },
offset: 0,
limit: 1000,
})
break
users = participants.users
break
default:
throw new Error(`Unknown peer type ${this.type}`)
}
return { info, participants }
return {
info: info.chats[0],
users
}
}
toInputPeer() {
+31
View File
@@ -18,6 +18,21 @@ const os = require("os")
const telegram = require("telegram-mtproto")
const TelegramPeer = require("./telegram-peer")
const META_FROM_FILETYPE = {
"storage.fileGif": {
mimetype: "image/gif",
extension: "gif",
},
"storage.fileJpeg": {
mimetype: "image/jpeg",
extension: "jpeg",
},
"storage.filePng": {
mimetype: "image/png",
extension: "png",
},
}
/**
* TelegramPuppet represents a Telegram account being controlled from Matrix.
*/
@@ -306,6 +321,22 @@ class TelegramPuppet {
}
}, 5000)
}
async getFile(location) {
location = Object.assign({}, location, {_: "inputFileLocation"})
delete location.dc_id
const file = await this.client("upload.getFile", {
location,
offset: 0,
limit: 100*1024*1024,
})
const meta = META_FROM_FILETYPE[file.type._]
if (meta) {
file.mimetype = meta.mimetype
file.extension = meta.extension
}
return file
}
}
module.exports = TelegramPuppet
+26 -11
View File
@@ -67,12 +67,8 @@ class TelegramUser {
}
}
updateInfo(telegramPOV, user) {
async updateInfo(telegramPOV, user) {
let changed = false
if (telegramPOV && this.accessHashes.get(telegramPOV.userID) !== +user.access_hash) {
this.accessHashes.set(telegramPOV.userID, +user.access_hash)
changed = true
}
if (this.firstName !== user.first_name) {
this.firstName = user.first_name
changed = true
@@ -85,12 +81,31 @@ class TelegramUser {
this.username = user.username
changed = true
}
if (await this.updateAvatarImageFrom(telegramPOV, user)) {
changed = true
}
if (telegramPOV && this.accessHashes.get(telegramPOV.userID) !== user.access_hash) {
this.accessHashes.set(telegramPOV.userID, user.access_hash)
changed = true
}
const userInfo = await this.intent.getProfileInfo(this.mxid, "displayname")
if (userInfo.displayname !== this.getDisplayName()) {
console.log(userInfo.displayname)
this.intent.setDisplayName(
this.app.config.bridge.displayname_template.replace("${DISPLAYNAME}", this.getDisplayName()))
console.log((await this.intent.getProfileInfo(this.mxid, "displayname")).displayname)
}
if (changed) {
this.save()
}
return changed
}
get intent() {
if (!this._intent) {
this._intent = this.app.getIntentForTelegramID(this.id)
this._intent = this.app.getIntentForTelegramUser(this.id)
}
return this._intent
}
@@ -145,7 +160,7 @@ class TelegramUser {
async updateAvatarImageFrom(telegramPOV, user) {
if (!user.photo) {
return
return false
}
const photo = user.photo.photo_big
@@ -153,7 +168,7 @@ class TelegramUser {
this.photo.dc_id === photo.dc_id &&
this.photo.volume_id === photo.volume_id &&
this.photo.local_id === photo.local_id) {
return this.avatarURL
return false
}
const file = await telegramPOV.getFile(photo)
@@ -165,15 +180,15 @@ class TelegramUser {
type: file.mimetype,
})
this.avatarURL = response.content_uri
this.avatarURL = uploaded.content_uri
this.photo = {
dc_id: photo.dc_id,
volume_id: photo.volume_id,
local_id: photo.local_id,
}
await this.app.putUser(this)
return this.avatarURL
await this.intent.setAvatarUrl(this.avatarURL)
return true
}
}