Yet another sync commit

This commit is contained in:
Tulir Asokan
2017-11-16 22:05:08 +02:00
parent e95873b4ec
commit 8a1ed209f1
6 changed files with 91 additions and 50 deletions
+6 -2
View File
@@ -82,10 +82,14 @@ class MautrixTelegram {
return portal return portal
} }
const entries = await this.bridge.getRoomStore().select({ const query = {
type: "portal", type: "portal",
id: peer.id, id: peer.id,
}) }
if (peer.type === "user") {
query.receiverID = peer.receiverID
}
const entries = await this.bridge.getRoomStore().select(query)
// Handle possible db query race conditions // Handle possible db query race conditions
portal = this.portalsByPeerID.get(peer.id) portal = this.portalsByPeerID.get(peer.id)
+13 -13
View File
@@ -18,17 +18,17 @@ const makePasswordHash = require("telegram-mtproto").plugins.makePasswordHash
const commands = {} const commands = {}
function run(sender, command, args, reply, app) { function run(sender, command, args, reply, app) {
if (sender.commandStatus) {
if (command === "cancel") {
reply(`${sender.commandStatus.action} cancelled.`)
sender.commandStatus = undefined
return
}
args.unshift(command)
return sender.commandStatus.next(sender, args, reply, app)
}
command = this.commands[command] command = this.commands[command]
if (!command) { if (!command) {
if (sender.commandStatus) {
if (command === "cancel") {
reply(`${sender.commandStatus.action} cancelled.`)
sender.commandStatus = undefined
return
}
args.unshift(command)
return sender.commandStatus.next(sender, args, reply, app)
}
reply("Unknown command. Try \"$cmdprefix help\" for help.") reply("Unknown command. Try \"$cmdprefix help\" for help.")
return return
} }
@@ -60,7 +60,7 @@ syncUsers <type> <id> - Sync user info and join status in the given portal. Same
/** /**
* Two-factor authentication handler. * Two-factor authentication handler.
*/ */
const enterPassword = async (sender, args, reply) => { commands.enterPassword = async (sender, args, reply) => {
if (args.length === 0) { if (args.length === 0) {
reply("Usage: $cmdprefix <password>") reply("Usage: $cmdprefix <password>")
return return
@@ -80,7 +80,7 @@ const enterPassword = async (sender, args, reply) => {
/* /*
* Login code send handler. * Login code send handler.
*/ */
const enterCode = async (sender, args, reply) => { commands.enterCode = async (sender, args, reply) => {
if (args.length === 0) { if (args.length === 0) {
reply("Usage: $cmdprefix <authentication code>") reply("Usage: $cmdprefix <authentication code>")
return return
@@ -95,7 +95,7 @@ const enterCode = async (sender, args, reply) => {
reply(`You have two-factor authentication enabled. Password hint: ${data.hint}\nEnter your password using "$cmdprefix <password>"`) reply(`You have two-factor authentication enabled. Password hint: ${data.hint}\nEnter your password using "$cmdprefix <password>"`)
sender.commandStatus = { sender.commandStatus = {
action: "Two-factor authentication", action: "Two-factor authentication",
next: enterPassword, next: commands.enterPassword,
salt: data.salt, salt: data.salt,
} }
} else { } else {
@@ -122,7 +122,7 @@ commands.login = async (sender, args, reply) => {
reply(`Login code sent to ${args[0]}.\nEnter the code using "$cmdprefix <code>"`) reply(`Login code sent to ${args[0]}.\nEnter the code using "$cmdprefix <code>"`)
sender.commandStatus = { sender.commandStatus = {
action: "Phone code authentication", action: "Phone code authentication",
next: enterCode, next: commands.enterCode ,
} }
} catch (err) { } catch (err) {
reply(`Failed to send code: ${err}`) reply(`Failed to send code: ${err}`)
+22 -6
View File
@@ -29,6 +29,10 @@ class Portal {
return this.peer.id return this.peer.id
} }
get receiverID() {
return this.peer.receiverID
}
static fromEntry(app, entry) { static fromEntry(app, entry) {
if (entry.type !== "portal") { if (entry.type !== "portal") {
throw new Error("MatrixUser can only be created from entry type \"portal\"") throw new Error("MatrixUser can only be created from entry type \"portal\"")
@@ -51,7 +55,7 @@ class Portal {
} }
for (const userData of users) { for (const userData of users) {
const user = await this.app.getTelegramUser(userData.id) const user = await this.app.getTelegramUser(userData.id)
await user.updateInfo(telegramPOV, userData) await user.updateInfo(telegramPOV, userData, true)
await user.intent.join(this.roomID) await user.intent.join(this.roomID)
} }
return true return true
@@ -92,11 +96,20 @@ class Portal {
return undefined return undefined
} }
const {info, users} = await this.peer.getInfo(telegramPOV) let title, info, users
if (this.peer.type !== "user") {
({info, users} = await this.peer.getInfo(telegramPOV))
title = info.title
} else {
({info} = await this.peer.getInfo(telegramPOV))
users = await this.app.getTelegramUser(info.id)
await users.updateInfo(telegramPOV, info)
title = users.getDisplayName()
}
const room = await this.app.botIntent.createRoom({ const room = await this.app.botIntent.createRoom({
options: { options: {
name: info.title, name: title,
visibility: "private", visibility: "private",
} }
}) })
@@ -104,9 +117,11 @@ class Portal {
this.roomID = room.room_id this.roomID = room.room_id
this.app.portalsByRoomID.set(this.roomID, this) this.app.portalsByRoomID.set(this.roomID, this)
await this.save() await this.save()
if (this.peer.type !== "user") {
await this.syncTelegramUsers(telegramPOV, users) await this.syncTelegramUsers(telegramPOV, users)
} else {
await users.intent.join(this.roomID)
}
return this.roomID return this.roomID
} catch (err) { } catch (err) {
console.error(err) console.error(err)
@@ -134,6 +149,7 @@ class Portal {
return { return {
type: this.type, type: this.type,
id: this.id, id: this.id,
receiverID: this.receiverID,
data: { data: {
roomID: this.roomID, roomID: this.roomID,
peer: this.peer.toSubentry(), peer: this.peer.toSubentry(),
+29 -15
View File
@@ -15,20 +15,21 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
class TelegramPeer { class TelegramPeer {
constructor(type, id, accessHash) { constructor(type, id, accessHash, receiverID) {
this.type = type this.type = type
this.id = id this.id = id
this.accessHash = accessHash this.accessHash = accessHash
this.receiverID = receiverID
this.username = undefined this.username = undefined
this.title = undefined this.title = undefined
} }
static fromTelegramData(peer) { static fromTelegramData(peer, receiverID) {
switch(peer._) { switch(peer._) {
case "peerChat": case "peerChat":
return new TelegramPeer("chat", peer.chat_id) return new TelegramPeer("chat", peer.chat_id)
case "peerUser": case "peerUser":
return new TelegramPeer("user", peer.user_id, peer.access_hash) return new TelegramPeer("user", peer.user_id, peer.access_hash, receiverID)
case "peerChannel": case "peerChannel":
return new TelegramPeer("channel", peer.channel_id, peer.access_hash) return new TelegramPeer("channel", peer.channel_id, peer.access_hash)
default: default:
@@ -86,7 +87,11 @@ class TelegramPeer {
let info, users let info, users
switch(this.type) { switch(this.type) {
case "user": case "user":
throw new Error("Can't get chat info of user") info = await telegramPOV.client("users.getFullUser", {
id: this.toInputObject()
})
users = [info.user]
info = info.user
case "chat": case "chat":
info = await telegramPOV.client("messages.getFullChat", { info = await telegramPOV.client("messages.getFullChat", {
chat_id: this.id, chat_id: this.id,
@@ -95,10 +100,10 @@ class TelegramPeer {
break break
case "channel": case "channel":
info = await telegramPOV.client("channels.getFullChannel", { info = await telegramPOV.client("channels.getFullChannel", {
channel: this.toInputChannel(), channel: this.toInputObject(),
}) })
const participants = await telegramPOV.client("channels.getParticipants", { const participants = await telegramPOV.client("channels.getParticipants", {
channel: this.toInputChannel(), channel: this.toInputObject(),
filter: { _: "channelParticipantsRecent" }, filter: { _: "channelParticipantsRecent" },
offset: 0, offset: 0,
limit: 1000, limit: 1000,
@@ -138,15 +143,22 @@ class TelegramPeer {
} }
} }
toInputChannel() { toInputObject() {
if (this.type !== "channel") { switch(this.type) {
throw new Error(`Cannot convert peer of type ${this.type} into an inputChannel`) case "user":
} return {
_: "inputUser",
return { user_id: this.id,
_: "inputChannel", access_hash: this.accessHash,
channel_id: this.id, }
access_hash: this.accessHash, case "channel":
return {
_: "inputChannel",
channel_id: this.id,
access_hash: this.accessHash,
}
default:
throw new Error(`Unrecognized type ${this.type}`)
} }
} }
@@ -154,6 +166,7 @@ class TelegramPeer {
const peer = new TelegramPeer(entry.type, entry.id) const peer = new TelegramPeer(entry.type, entry.id)
peer.username = entry.username peer.username = entry.username
peer.title = entry.title peer.title = entry.title
peer.receiverID = entry.receiverID
return peer return peer
} }
@@ -163,6 +176,7 @@ class TelegramPeer {
id: this.id, id: this.id,
username: this.username, username: this.username,
title: this.title, title: this.title,
receiverID: this.receiverID,
} }
} }
+21 -12
View File
@@ -56,6 +56,7 @@ class TelegramPuppet {
return value return value
}, },
set: async (key, value) => { set: async (key, value) => {
console.warn("SET", key, "=", JSON.stringify(value))
if (this.data[key] === value) { if (this.data[key] === value) {
return return
} }
@@ -64,10 +65,12 @@ class TelegramPuppet {
await this.matrixUser.save() await this.matrixUser.save()
}, },
remove: async (...keys) => { remove: async (...keys) => {
console.warn("DEL", JSON.stringify(value))
keys.forEach((key) => delete this.data[key]) keys.forEach((key) => delete this.data[key])
await this.matrixUser.save() await this.matrixUser.save()
}, },
clear: async () => { clear: async () => {
console.warn("CLR")
this.data = {} this.data = {}
await this.matrixUser.save() await this.matrixUser.save()
}, },
@@ -225,6 +228,7 @@ class TelegramPuppet {
console.log("Oh noes! Empty update") console.log("Oh noes! Empty update")
return return
} }
let peer, portal
switch(update._) { switch(update._) {
case "updateUserStatus": case "updateUserStatus":
const user = await this.app.getTelegramUser(update.user_id) const user = await this.app.getTelegramUser(update.user_id)
@@ -237,25 +241,29 @@ class TelegramPuppet {
default: default:
status = "offline" status = "offline"
} }
user.intent.getClient().setPresence({presence: status})
await user.intent.getClient().setPresence({presence: status})
break break
case "updateUserTyping": case "updateUserTyping":
console.log(update.user_id, "is typing in a 1-1 chat") peer = new TelegramPeer("user", update.user_id, undefined, this.userID)
break
case "updateChatUserTyping": case "updateChatUserTyping":
console.log(update.user_id, "is typing in", update.chat_id) peer = peer || new TelegramPeer("chat", update.chat_id)
portal = await this.app.getPortalByPeer(peer)
if (portal.isMatrixRoomCreated()) {
const sender = await this.app.getTelegramUser(update.user_id)
// The Intent API currently doesn't allow you to set the
// typing timeout. If it does, we should set it to ~5.5s as
// Telegram resends typing notifications every 5 seconds.
await sender.intent.sendTyping(portal.roomID, true/*, 5500*/)
}
break break
case "updateShortMessage": case "updateShortMessage":
await this.handleMessage({ peer = new TelegramPeer("user", update.user_id, undefined, this.userID)
from: update.user_id,
to: new TelegramPeer("user", update.user_id),
text: update.message,
})
break
case "updateShortChatMessage": case "updateShortChatMessage":
peer = peer || new TelegramPeer("chat", update.chat_id)
await this.handleMessage({ await this.handleMessage({
from: update.user_id, from: update.user_id,
to: new TelegramPeer("chat", update.chat_id), to: peer,
text: update.message, text: update.message,
}) })
break break
@@ -265,7 +273,7 @@ class TelegramPuppet {
update = update.message // Message defined at message#90dddc11 in layer 71 update = update.message // Message defined at message#90dddc11 in layer 71
await this.handleMessage({ await this.handleMessage({
from: update.from_id, from: update.from_id,
to: TelegramPeer.fromTelegramData(update.to_id), to: TelegramPeer.fromTelegramData(update.to_id, this.userID),
text: update.message, text: update.message,
}) })
break break
@@ -275,6 +283,7 @@ class TelegramPuppet {
} }
handleUpdate(data) { handleUpdate(data) {
console.log("UPDATE", data)
try { try {
switch (data._) { switch (data._) {
case "updateShort": case "updateShort":
-2
View File
@@ -88,10 +88,8 @@ class TelegramUser {
const userInfo = await this.intent.getProfileInfo(this.mxid, "displayname") const userInfo = await this.intent.getProfileInfo(this.mxid, "displayname")
if (userInfo.displayname !== this.getDisplayName()) { if (userInfo.displayname !== this.getDisplayName()) {
console.log(userInfo.displayname)
this.intent.setDisplayName( this.intent.setDisplayName(
this.app.config.bridge.displayname_template.replace("${DISPLAYNAME}", this.getDisplayName())) this.app.config.bridge.displayname_template.replace("${DISPLAYNAME}", this.getDisplayName()))
console.log((await this.intent.getProfileInfo(this.mxid, "displayname")).displayname)
} }
if (!dontUpdateAvatar && this.updateAvatarImageFrom(telegramPOV, user)) { if (!dontUpdateAvatar && this.updateAvatarImageFrom(telegramPOV, user)) {
changed = true changed = true