Another sync commit with an useless message
This commit is contained in:
Generated
+20
@@ -626,6 +626,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"charenc": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
|
||||
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
|
||||
},
|
||||
"cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
@@ -843,6 +848,11 @@
|
||||
"which": "1.3.0"
|
||||
}
|
||||
},
|
||||
"crypt": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
|
||||
@@ -2471,6 +2481,16 @@
|
||||
"request": "2.83.0"
|
||||
}
|
||||
},
|
||||
"md5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
||||
"integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
|
||||
"requires": {
|
||||
"charenc": "0.0.2",
|
||||
"crypt": "0.0.2",
|
||||
"is-buffer": "1.1.6"
|
||||
}
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
|
||||
|
||||
+2
-1
@@ -15,7 +15,8 @@
|
||||
"matrix-appservice-bridge": "1.x.x",
|
||||
"commander": "2.11.x",
|
||||
"yamljs": "0.3.x",
|
||||
"colors": "1.1.x"
|
||||
"colors": "1.1.x",
|
||||
"md5": "2.2.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "4.11.x",
|
||||
|
||||
+123
-22
@@ -15,9 +15,11 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
const {Bridge} = require("matrix-appservice-bridge")
|
||||
const crypto = require("crypto")
|
||||
const YAML = require("yamljs")
|
||||
const commands = require("./commands")
|
||||
const MatrixUser = require("./matrix-user")
|
||||
const YAML = require("yamljs")
|
||||
const TelegramUser = require("./telegram-user")
|
||||
const Portal = require("./portal")
|
||||
|
||||
class MautrixTelegram {
|
||||
constructor(config) {
|
||||
@@ -25,6 +27,8 @@ class MautrixTelegram {
|
||||
|
||||
this.matrixUsersByID = new Map()
|
||||
this.telegramUsersByID = new Map()
|
||||
this.portalsByPeerID = new Map()
|
||||
this.portalsByRoomID = new Map()
|
||||
|
||||
const self = this
|
||||
this.bridge = new Bridge({
|
||||
@@ -69,38 +73,135 @@ class MautrixTelegram {
|
||||
this.config.bridge.username_template.replace("${ID}", id))
|
||||
}
|
||||
|
||||
getMatrixUser(id) {
|
||||
let user = this.matrixUsersByID.get(id)
|
||||
if (user) {
|
||||
return Promise.resolve(user)
|
||||
async getPortalByPeer(peer) {
|
||||
let portal = this.portalsByPeerID.get(peer.id)
|
||||
if (portal) {
|
||||
return portal
|
||||
}
|
||||
|
||||
return this.bridge.getUserStore().select({
|
||||
const entries = await this.bridge.getRoomStore().select({
|
||||
type: "portal",
|
||||
id: peer.id,
|
||||
})
|
||||
|
||||
// Handle possible db query race conditions
|
||||
portal = this.portalsByPeerID.get(peer.id)
|
||||
if (portal) {
|
||||
return portal
|
||||
}
|
||||
|
||||
if (entries.length) {
|
||||
portal = Portal.fromEntry(this, entries[0])
|
||||
} else {
|
||||
portal = new Portal(this, undefined, peer)
|
||||
}
|
||||
this.portalsByPeerID.set(peer.id, portal)
|
||||
if (portal.roomID) {
|
||||
this.portalsByRoomID.set(portal.roomID, portal)
|
||||
}
|
||||
return portal
|
||||
}
|
||||
|
||||
async getPortalByRoomID(id) {
|
||||
let portal = this.portalsByRoomID.get(id)
|
||||
if (portal) {
|
||||
return portal
|
||||
}
|
||||
|
||||
// Check if we have it stored in the by-peer map
|
||||
for (const [_, portalByPeer] of this.portalsByPeerID) {
|
||||
if (portalByPeer.roomID === id) {
|
||||
this.portalsByRoomID.set(id, portal)
|
||||
return portalByPeer
|
||||
}
|
||||
}
|
||||
|
||||
const entries = await this.bridge.getRoomStore().select({
|
||||
type: "portal",
|
||||
roomID: id,
|
||||
})
|
||||
|
||||
// Handle possible db query race conditions
|
||||
let portal = this.portalsByRoomID.get(id)
|
||||
if (portal) {
|
||||
return portal
|
||||
}
|
||||
|
||||
if (entries.length) {
|
||||
portal = Portal.fromEntry(this, entries[0])
|
||||
} else {
|
||||
// Don't create portals based on room ID
|
||||
return undefined
|
||||
}
|
||||
this.portalsByPeerID.set(portal.id, portal)
|
||||
this.portalsByRoomID.set(id, portal)
|
||||
return portal
|
||||
}
|
||||
|
||||
async getTelegramUser(id) {
|
||||
let user = this.telegramUsersByID.get(id)
|
||||
if (user) {
|
||||
return user
|
||||
}
|
||||
|
||||
const entries = await this.bridge.getUserStore().select({
|
||||
type: "remote",
|
||||
id,
|
||||
})
|
||||
|
||||
// Handle possible db query race conditions
|
||||
if (this.telegramUsersByID.has(id)) {
|
||||
return this.telegramUsersByID.get(id)
|
||||
}
|
||||
|
||||
if (entries.length) {
|
||||
user = TelegramUser.fromEntry(this, entries[0])
|
||||
} else {
|
||||
user = new TelegramUser(this, id)
|
||||
}
|
||||
this.telegramUsersByID.set(id, user)
|
||||
return user
|
||||
}
|
||||
|
||||
async getMatrixUser(id) {
|
||||
let user = this.matrixUsersByID.get(id)
|
||||
if (user) {
|
||||
return user
|
||||
}
|
||||
|
||||
const entries = this.bridge.getUserStore().select({
|
||||
type: "matrix",
|
||||
id,
|
||||
}).then(entries => {
|
||||
this.matrixUsersByID.get(id)
|
||||
if (user) {
|
||||
return Promise.resolve(user)
|
||||
}
|
||||
|
||||
if (entries.length) {
|
||||
user = MatrixUser.fromEntry(this, entries[0])
|
||||
} else {
|
||||
user = new MatrixUser(this, id)
|
||||
}
|
||||
this.matrixUsersByID.set(id, user)
|
||||
return user
|
||||
})
|
||||
|
||||
// Handle possible db query race conditions
|
||||
if (this.matrixUsersByID.has(id)) {
|
||||
return this.matrixUsersByID.get(id)
|
||||
}
|
||||
|
||||
if (entries.length) {
|
||||
user = MatrixUser.fromEntry(this, entries[0])
|
||||
} else {
|
||||
user = new MatrixUser(this, id)
|
||||
}
|
||||
this.matrixUsersByID.set(id, user)
|
||||
return user
|
||||
}
|
||||
|
||||
putUser(user) {
|
||||
const entry = user.toEntry()
|
||||
const query = {
|
||||
return this.bridge.getUserStore().upsert({
|
||||
type: entry.type,
|
||||
id: entry.id,
|
||||
}
|
||||
return this.bridge.getUserStore().upsert(query, entry)
|
||||
}, entry)
|
||||
}
|
||||
|
||||
putRoom(room) {
|
||||
const entry = room.toEntry()
|
||||
return this.bridge.getUserStore().upsert({
|
||||
type: entry.type,
|
||||
id: entry.id,
|
||||
}, entry)
|
||||
}
|
||||
|
||||
handleMatrixEvent(evt) {
|
||||
|
||||
+26
-3
@@ -20,8 +20,8 @@ const commands = {}
|
||||
function run(sender, command, args, reply, app) {
|
||||
if (sender.commandStatus) {
|
||||
if (command === "cancel") {
|
||||
sender.commandStatus = undefined
|
||||
reply(`${sender.commandStatus.action} cancelled.`)
|
||||
sender.commandStatus = undefined
|
||||
return
|
||||
}
|
||||
args.unshift(command)
|
||||
@@ -78,7 +78,6 @@ const enterCode = async (sender, args, reply) => {
|
||||
try {
|
||||
const data = await sender.signInToTelegram(args[0])
|
||||
if (data.status === "ok") {
|
||||
// TODO show who the user logged in as
|
||||
reply(`Logged in successfully as @${sender.telegramPuppet.getDisplayName()}.`)
|
||||
sender.commandStatus = undefined
|
||||
} else if (data.status === "need-password") {
|
||||
@@ -92,8 +91,9 @@ const enterCode = async (sender, args, reply) => {
|
||||
reply(`Unexpected sign in response, status=${data.status}`)
|
||||
}
|
||||
} catch (err) {
|
||||
// TODO login fails somewhere with TypeError: Cannot read property 'status' of undefined
|
||||
reply(`Login failed: ${err}`)
|
||||
console.log(err)
|
||||
console.error(err.stack)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +120,29 @@ commands.login = async (sender, args, reply) => {
|
||||
}
|
||||
}
|
||||
|
||||
commands.register = async (sender, args, reply) => {
|
||||
reply("Registration has not yet been implemented. Please use the offical apps for now.")
|
||||
}
|
||||
|
||||
commands.logout = async (sender, args, reply) => {
|
||||
try {
|
||||
sender.logOutFromTelegram()
|
||||
reply("Logged out successfully.")
|
||||
} catch (err) {
|
||||
reply(`Failed to log out: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// General command handlers //
|
||||
//////////////////////////////
|
||||
|
||||
+74
-6
@@ -13,7 +13,9 @@
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
const md5 = require("md5")
|
||||
const TelegramPuppet = require("./telegram-puppet")
|
||||
const TelegramPeer = require("./telegram-peer")
|
||||
|
||||
/**
|
||||
* MatrixUser represents a Matrix user who probably wants to control their
|
||||
@@ -27,6 +29,7 @@ class MatrixUser {
|
||||
this.phoneNumber = undefined
|
||||
this.phoneCodeHash = undefined
|
||||
this.commandStatus = undefined
|
||||
this.contacts = []
|
||||
this._telegramPuppet = undefined
|
||||
}
|
||||
|
||||
@@ -38,8 +41,10 @@ class MatrixUser {
|
||||
const user = new MatrixUser(app, entry.id)
|
||||
user.phoneNumber = entry.data.phoneNumber
|
||||
user.phoneCodeHash = entry.data.phoneCodeHash
|
||||
user.contactIDs = entry.data.contactIDs
|
||||
if (entry.data.puppet) {
|
||||
user.puppetData = entry.data.puppet
|
||||
// Create the telegram puppet instance
|
||||
user.telegramPuppet
|
||||
}
|
||||
return user
|
||||
@@ -47,7 +52,7 @@ class MatrixUser {
|
||||
|
||||
toEntry() {
|
||||
if (this._telegramPuppet) {
|
||||
this.puppetData = this.telegramPuppet.toSubentry()
|
||||
this.puppetData = this._telegramPuppet.toSubentry()
|
||||
}
|
||||
return {
|
||||
type: "matrix",
|
||||
@@ -55,6 +60,7 @@ class MatrixUser {
|
||||
data: {
|
||||
phoneNumber: this.phoneNumber,
|
||||
phoneCodeHash: this.phoneCodeHash,
|
||||
contactIDs: this.contactIDs,
|
||||
puppet: this.puppetData,
|
||||
},
|
||||
}
|
||||
@@ -76,36 +82,98 @@ class MatrixUser {
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
get contactIDs() {
|
||||
return this.contacts.map(contact => contact.id)
|
||||
}
|
||||
|
||||
set contactIDs(list) {
|
||||
// FIXME This is somewhat dangerous
|
||||
setTimeout(async () => {
|
||||
if (list) {
|
||||
this.contacts = await Promise.all(list.map(id => this.app.getTelegramUser(id)))
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
async syncContacts() {
|
||||
const contacts = await this.telegramPuppet.client("contacts.getContacts", {
|
||||
hash: md5(this.contactIDs.join(","))
|
||||
})
|
||||
if (contacts._ === "contacts.contactsNotModified") {
|
||||
return false
|
||||
}
|
||||
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()
|
||||
}
|
||||
contacts.users[index] = telegramUser
|
||||
}
|
||||
this.contacts = contacts.users
|
||||
await this.save()
|
||||
return true
|
||||
}
|
||||
|
||||
async syncDialogs() {
|
||||
const dialogs = await this.telegramPuppet.client("messages.getDialogs", {})
|
||||
for (const dialog of dialogs.chats) {
|
||||
const peer = new TelegramPeer(dialog._, dialog.id)
|
||||
const portal = await this.app.getPortalByPeer(peer)
|
||||
if (portal.updateInfo(this.telegramPuppet, dialog)) {
|
||||
portal.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async sendTelegramCode(phoneNumber) {
|
||||
// TODO handle existing login?
|
||||
if (this._telegramPuppet && this._telegramPuppet.userID) {
|
||||
throw new Error("You are already logged in. Please log out before logging in again.")
|
||||
}
|
||||
switch(this.telegramPuppet.checkPhone(phoneNumber)) {
|
||||
case "unregistered":
|
||||
throw new Error("That number has not been registered. Please register it first.")
|
||||
case "invalid":
|
||||
throw new Error("Invalid phone number.")
|
||||
}
|
||||
try {
|
||||
const result = await this.telegramPuppet.sendCode(phoneNumber)
|
||||
this.phoneNumber = phoneNumber
|
||||
this.phoneCodeHash = result.phone_code_hash
|
||||
await this.saveChanges()
|
||||
await this.save()
|
||||
return result
|
||||
} catch (err) {
|
||||
return this.parseTelegramError(err)
|
||||
}
|
||||
}
|
||||
|
||||
async logOutFromTelegram() {
|
||||
const ok = await this.telegramPuppet.logOut()
|
||||
if (!ok) {
|
||||
return false
|
||||
}
|
||||
this._telegramPuppet = undefined
|
||||
this.puppetData = undefined
|
||||
await this.save()
|
||||
return true
|
||||
}
|
||||
|
||||
async signInToTelegram(phoneCode) {
|
||||
if (!this.phoneNumber) throw new Error("Phone number not set")
|
||||
if (!this.phoneCodeHash) throw new Error("Phone code not sent")
|
||||
|
||||
const result = await this.telegramPuppet.signIn(this.phoneNumber, this.phoneCodeHash, phoneCode)
|
||||
this.phoneCodeHash = undefined
|
||||
await this.saveChanges()
|
||||
await this.save()
|
||||
return result
|
||||
}
|
||||
|
||||
async checkPassword(password_hash) {
|
||||
const result = await this.telegramPuppet.checkPassword(password_hash)
|
||||
await this.saveChanges()
|
||||
await this.save()
|
||||
return result
|
||||
}
|
||||
|
||||
saveChanges() {
|
||||
save() {
|
||||
return this.app.putUser(this)
|
||||
}
|
||||
}
|
||||
|
||||
+53
-1
@@ -18,9 +18,11 @@ const TelegramPeer = require("./telegram-peer")
|
||||
class Portal {
|
||||
constructor(app, roomID, peer) {
|
||||
this.app = app
|
||||
this.type = "portal"
|
||||
|
||||
this.roomID = roomID
|
||||
this.peer = peer
|
||||
this.accessHashes = new Map()
|
||||
}
|
||||
|
||||
static fromEntry(app, entry) {
|
||||
@@ -28,6 +30,56 @@ class Portal {
|
||||
throw new Error("MatrixUser can only be created from entry type \"portal\"")
|
||||
}
|
||||
|
||||
return new Portal(app, entry.data.roomID, TelegramPeer.fromSubentry(entry.data.peer))
|
||||
const portal = new Portal(app, entry.data.roomID, TelegramPeer.fromSubentry(entry.data.peer))
|
||||
if (portal.peer.type === "channel") {
|
||||
portal.accessHashes = new Map(entry.data.accessHashes)
|
||||
}
|
||||
return portal
|
||||
}
|
||||
|
||||
async createMatrixRoom(telegramPOV) {
|
||||
if (this.roomID) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await this.peer.getInfo(telegramPOV)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
console.error(err.stack)
|
||||
}
|
||||
}
|
||||
|
||||
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 (this.title !== dialog.title) {
|
||||
this.title = dialog.title
|
||||
changed = true
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
toEntry() {
|
||||
return {
|
||||
type: this.type,
|
||||
id: this.roomID,
|
||||
peer: this.peer.toSubentry(),
|
||||
accessHashes: this.peer.type === "channel"
|
||||
? Array.from(this.accessHashes)
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
return this.app.putRoom(this)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Portal
|
||||
|
||||
+57
-7
@@ -18,22 +18,72 @@ class TelegramPeer {
|
||||
constructor(type, id, accessHash) {
|
||||
this.type = type
|
||||
this.id = id
|
||||
this.accessHash = accessHash
|
||||
this.accessHash = +(accessHash || 0)
|
||||
}
|
||||
|
||||
static fromTelegramData(peer) {
|
||||
switch(peer._) {
|
||||
case "peerChat":
|
||||
return new Peer("chat", peer.chat_id)
|
||||
return new TelegramPeer("chat", peer.chat_id)
|
||||
case "peerUser":
|
||||
return new Peer("user", peer.user_id, peer.access_hash)
|
||||
return new TelegramPeer("user", peer.user_id, peer.access_hash || 0)
|
||||
case "peerChannel":
|
||||
return new Peer("channel", peer.channel_id, peer.access_hash)
|
||||
return new TelegramPeer("channel", peer.channel_id, peer.access_hash || 0)
|
||||
default:
|
||||
throw new Error(`Unrecognized peer type ${peer._}`)
|
||||
}
|
||||
}
|
||||
|
||||
async getAccessHash(app, telegramPOV) {
|
||||
if (this.type === "chat" || this.accessHash > 0) {
|
||||
return true
|
||||
} else if (this.type === "user") {
|
||||
const 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)
|
||||
if (portal.accessHashes.has(telegramPOV.userID)) {
|
||||
this.accessHash = portal.accessHashes.get(telegramPOV.userID)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async getInfo(telegramPOV) {
|
||||
let info, participants
|
||||
switch(this.type) {
|
||||
case "user":
|
||||
throw new Error("Can't get chat info of user")
|
||||
case "chat":
|
||||
info = await telegramPOV.client("messages.getFullChat", {
|
||||
chat_id: this.id,
|
||||
})
|
||||
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", {
|
||||
channel: this.toInputChannel(),
|
||||
filter: { _: "channelParticipantsRecent" },
|
||||
offset: 0,
|
||||
limit: 1000,
|
||||
})
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unknown peer type ${this.type}`)
|
||||
}
|
||||
console.log(JSON.stringify(info, "", " "))
|
||||
console.log(JSON.stringify(participants, "", " "))
|
||||
}
|
||||
|
||||
toInputPeer() {
|
||||
switch(this.type) {
|
||||
case "chat":
|
||||
@@ -71,15 +121,13 @@ class TelegramPeer {
|
||||
}
|
||||
|
||||
static fromSubentry(entry) {
|
||||
const accessHash = entry.accessHash ? new Buffer(entry.accessHash) : undefined
|
||||
return new Peer(entry.type, entry.id, accessHash)
|
||||
return new TelegramPeer(entry.type, entry.id)
|
||||
}
|
||||
|
||||
toSubentry() {
|
||||
return {
|
||||
type: this.type,
|
||||
id: this.id,
|
||||
accessHash: this.accessHash.toString(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,3 +135,5 @@ class TelegramPeer {
|
||||
return `${this.type} ${this.id}`
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TelegramPeer
|
||||
|
||||
+121
-24
@@ -16,6 +16,7 @@
|
||||
const pkg = require("../package.json")
|
||||
const os = require("os")
|
||||
const telegram = require("telegram-mtproto")
|
||||
const TelegramPeer = require("./telegram-peer")
|
||||
|
||||
/**
|
||||
* TelegramPuppet represents a Telegram account being controlled from Matrix.
|
||||
@@ -40,18 +41,20 @@ class TelegramPuppet {
|
||||
return value
|
||||
},
|
||||
set: async (key, value) => {
|
||||
if (this.data[key] === value) return Promise.resolve()
|
||||
if (this.data[key] === value) {
|
||||
return
|
||||
}
|
||||
|
||||
this.data[key] = value
|
||||
await this.matrixUser.saveChanges()
|
||||
await this.matrixUser.save()
|
||||
},
|
||||
remove: async (...keys) => {
|
||||
keys.forEach((key) => delete this.data[key])
|
||||
await this.matrixUser.saveChanges()
|
||||
await this.matrixUser.save()
|
||||
},
|
||||
clear: async () => {
|
||||
this.data = {}
|
||||
await this.matrixUser.saveChanges()
|
||||
await this.matrixUser.save()
|
||||
},
|
||||
}
|
||||
|
||||
@@ -97,6 +100,21 @@ class TelegramPuppet {
|
||||
return this._client
|
||||
}
|
||||
|
||||
async checkPhone(phone_number) {
|
||||
try {
|
||||
const status = this.client("auth.checkPhone", { phone_number })
|
||||
if (status.phone_registered) {
|
||||
return "registered"
|
||||
}
|
||||
return "unregistered"
|
||||
} catch (err) {
|
||||
if (err.message === "PHONE_NUMBER_INVALID") {
|
||||
return "invalid"
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
sendCode(phone_number) {
|
||||
return this.client("auth.sendCode", {
|
||||
phone_number,
|
||||
@@ -106,13 +124,17 @@ class TelegramPuppet {
|
||||
})
|
||||
}
|
||||
|
||||
logOut() {
|
||||
return this.client("auth.logOut")
|
||||
}
|
||||
|
||||
async signIn(phone_number, phone_code_hash, phone_code) {
|
||||
try {
|
||||
const result = await
|
||||
this.client("auth.signIn", {
|
||||
phone_number, phone_code, phone_code_hash,
|
||||
})
|
||||
this.signInComplete(result)
|
||||
return this.signInComplete(result)
|
||||
} catch (err) {
|
||||
if (err.message !== "SESSION_PASSWORD_NEEDED") {
|
||||
throw err
|
||||
@@ -147,32 +169,90 @@ class TelegramPuppet {
|
||||
this.data.firstName = data.user.first_name
|
||||
this.data.lastName = data.user.last_name
|
||||
this.data.phoneNumber = data.user.phone_number
|
||||
this.matrixUser.saveChanges()
|
||||
this.matrixUser.save()
|
||||
this.listen()
|
||||
return {
|
||||
status: "ok",
|
||||
}
|
||||
}
|
||||
|
||||
async sendMessage(peer, message) {
|
||||
const result = await this.client("messages.sendMessage", {
|
||||
peer: peer.toInputPeer(),
|
||||
message,
|
||||
random_id: ~~(Math.random() * (1<<30)),
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
handleMessage(message) {
|
||||
console.log(
|
||||
`Received message from ${message.from.id} to ${message.to.type.replace("user", "1-1 chat")}${message.to.type === "user" ? "" : " " + message.to.id}: ${message.text}`)
|
||||
}
|
||||
|
||||
onUpdate(update) {
|
||||
console.log("Update received:", update)
|
||||
if (!update) {
|
||||
console.log("Oh noes! Empty update")
|
||||
return
|
||||
}
|
||||
switch(update._) {
|
||||
case "updateUserStatus":
|
||||
console.log(update.user_id, "is now", update.status._.substr("userStatus".length))
|
||||
break
|
||||
case "updateUserTyping":
|
||||
console.log(update.user_id, "is typing in a 1-1 chat")
|
||||
break
|
||||
case "updateChatUserTyping":
|
||||
console.log(update.user_id, "is typing in", update.chat_id)
|
||||
break
|
||||
case "updateShortMessage":
|
||||
this.handleMessage({
|
||||
from: this.app.getTelegramUser(update.user_id),
|
||||
to: new TelegramPeer("user", update.user_id),
|
||||
text: update.message,
|
||||
})
|
||||
break
|
||||
case "updateShortChatMessage":
|
||||
this.handleMessage({
|
||||
from: this.app.getTelegramUser(update.user_id),
|
||||
to: new TelegramPeer("chat", update.chat_id),
|
||||
text: update.message,
|
||||
})
|
||||
break
|
||||
case "updateNewMessage":
|
||||
update = update.message // Message defined at message#90dddc11 in layer 71
|
||||
this.handleMessage({
|
||||
from: update.from_id,
|
||||
to: TelegramPeer.fromTelegramData(update.to_id),
|
||||
text: update.message,
|
||||
})
|
||||
break
|
||||
default:
|
||||
console.log(`Update of type ${update._} received:\n${JSON.stringify(update, "", " ")}`)
|
||||
}
|
||||
}
|
||||
|
||||
handleUpdate(data) {
|
||||
switch(data._) {
|
||||
case "updateShort":
|
||||
this.onUpdate(data.update)
|
||||
break
|
||||
case "updates":
|
||||
for (const update of data.updates) {
|
||||
this.onUpdate(update)
|
||||
}
|
||||
break
|
||||
case "updateShortChatMessage":
|
||||
this.onUpdate(update)
|
||||
break
|
||||
default:
|
||||
console.log("Unrecognized update type:", data._)
|
||||
try {
|
||||
switch (data._) {
|
||||
case "updateShort":
|
||||
this.onUpdate(data.update)
|
||||
break
|
||||
case "updates":
|
||||
for (const update of data.updates) {
|
||||
this.onUpdate(update)
|
||||
}
|
||||
break
|
||||
case "updateShortMessage":
|
||||
case "updateShortChatMessage":
|
||||
this.onUpdate(data)
|
||||
break
|
||||
default:
|
||||
console.log("Unrecognized update type:", data._)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error handling update:", err)
|
||||
console.log(e.stack)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,18 +265,35 @@ class TelegramPuppet {
|
||||
|
||||
try {
|
||||
console.log("Updating online status...")
|
||||
const statusUpdate = await client("account.updateStatus", { offline: false })
|
||||
console.log(statusUpdate)
|
||||
//const statusUpdate = await client("account.updateStatus", { offline: false })
|
||||
//console.log(statusUpdate)
|
||||
console.log("Fetching initial state...")
|
||||
const state = await client("updates.getState", {})
|
||||
console.log("Initial state:", state)
|
||||
} catch (err) {
|
||||
console.error("Error getting initial state:", err)
|
||||
}
|
||||
try {
|
||||
console.log("Updating contact list...")
|
||||
const changed = await this.matrixUser.syncContacts()
|
||||
if (!changed) {
|
||||
console.log("Contacts were up-to-date")
|
||||
} else {
|
||||
console.log("Contacts updated")
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to update contacts:", err)
|
||||
}
|
||||
try {
|
||||
console.log("Syncing dialogs...")
|
||||
await this.matrixUser.syncDialogs()
|
||||
} catch (err) {
|
||||
console.error("Failed to sync dialogs:", err)
|
||||
}
|
||||
setInterval(async () => {
|
||||
try {
|
||||
const state = client("updates.getState", {})
|
||||
console.log("New state received")
|
||||
// TODO use state?
|
||||
} catch (err) {
|
||||
console.error("Error updating state:", err)
|
||||
}
|
||||
|
||||
+31
-6
@@ -13,6 +13,7 @@
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
const TelegramPeer = require("./telegram-peer")
|
||||
|
||||
/**
|
||||
* TelegramUser represents a Telegram user who probably has an
|
||||
@@ -22,9 +23,10 @@ class TelegramUser {
|
||||
constructor(app, id, user) {
|
||||
this.app = app
|
||||
this.id = id
|
||||
this.accessHashes = new Map()
|
||||
this._intent = undefined
|
||||
if (user) {
|
||||
this.updateInfo(user)
|
||||
this.updateInfo(undefined, user)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +43,12 @@ class TelegramUser {
|
||||
user.phoneNumber = data.phoneNumber
|
||||
user.photo = data.photo
|
||||
user.avatarURL = data.avatarURL
|
||||
user.accessHashes = new Map(data.accessHashes)
|
||||
return user
|
||||
}
|
||||
|
||||
toPeer(telegramPOV) {
|
||||
return new TelegramPeer("user", this.id, this.accessHashes.get(telegramPOV.userID))
|
||||
}
|
||||
|
||||
toEntry() {
|
||||
@@ -54,12 +62,17 @@ class TelegramUser {
|
||||
phoneNumber: this.phoneNumber,
|
||||
photo: this.photo,
|
||||
avatarURL: this.avatarURL,
|
||||
accessHashes: Array.from(this.accessHashes),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
updateFrom(user) {
|
||||
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
|
||||
@@ -68,6 +81,10 @@ class TelegramUser {
|
||||
this.lastName = user.last_name
|
||||
changed = true
|
||||
}
|
||||
if (this.username !== user.username) {
|
||||
this.username = user.username
|
||||
changed = true
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
@@ -94,6 +111,10 @@ class TelegramUser {
|
||||
return this.id
|
||||
}
|
||||
|
||||
save() {
|
||||
return this.app.putUser(this)
|
||||
}
|
||||
|
||||
sendText(roomID, text) {
|
||||
return this.intent.sendText(roomID, text)
|
||||
}
|
||||
@@ -122,18 +143,20 @@ class TelegramUser {
|
||||
})
|
||||
}
|
||||
|
||||
async updateAvatarImageFrom(user, puppet) {
|
||||
if (!user.photo) return Promise.resolve()
|
||||
async updateAvatarImageFrom(telegramPOV, user) {
|
||||
if (!user.photo) {
|
||||
return
|
||||
}
|
||||
|
||||
const photo = user.photo.photo_big
|
||||
if (this.photo && this.avatarURL &&
|
||||
this.photo.dc_id === photo.dc_id &&
|
||||
this.photo.volume_id === photo.volume_id &&
|
||||
this.photo.local_id === photo.local_id) {
|
||||
return Promise.resolve(this.avatarURL)
|
||||
return this.avatarURL
|
||||
}
|
||||
|
||||
const file = await puppet.getFile(photo)
|
||||
const file = await telegramPOV.getFile(photo)
|
||||
const name = `${photo.volume_id}_${photo.local_id}.${file.extension}`
|
||||
|
||||
const uploaded = await this.uploadContent({
|
||||
@@ -153,3 +176,5 @@ class TelegramUser {
|
||||
return this.avatarURL
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TelegramUser
|
||||
|
||||
Reference in New Issue
Block a user