Format and maybe break code
This commit is contained in:
+27
-10
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"browser": false,
|
|
||||||
"node": true,
|
"node": true,
|
||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
@@ -36,7 +35,6 @@
|
|||||||
"object-curly-newline": [
|
"object-curly-newline": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"minProperties": 5,
|
|
||||||
"consistent": true
|
"consistent": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -65,17 +63,29 @@
|
|||||||
],
|
],
|
||||||
"max-len": [
|
"max-len": [
|
||||||
"warn",
|
"warn",
|
||||||
80
|
120
|
||||||
],
|
],
|
||||||
"camelcase": [
|
"no-underscore-dangle": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"properties": "always"
|
"allowAfterThis": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"vars": "all",
|
||||||
|
"args": "after-used",
|
||||||
|
"varsIgnorePattern": "_"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"space-before-function-paren": [
|
"space-before-function-paren": [
|
||||||
"error",
|
"error",
|
||||||
"never"
|
{
|
||||||
|
"anonymous": "never",
|
||||||
|
"named": "never",
|
||||||
|
"asyncArrow": "always"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"func-style": [
|
"func-style": [
|
||||||
"warn",
|
"warn",
|
||||||
@@ -92,11 +102,11 @@
|
|||||||
"i",
|
"i",
|
||||||
"x",
|
"x",
|
||||||
"y",
|
"y",
|
||||||
"$"
|
"$",
|
||||||
|
"_"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"import/no-nodejs-modules": "error",
|
|
||||||
"import/order": [
|
"import/order": [
|
||||||
"warn",
|
"warn",
|
||||||
{
|
{
|
||||||
@@ -123,7 +133,8 @@
|
|||||||
"warn",
|
"warn",
|
||||||
{
|
{
|
||||||
"newIsCap": true,
|
"newIsCap": true,
|
||||||
"capIsNew": true
|
"capIsNew": true,
|
||||||
|
"capIsNewExceptions": ["MTProto"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"no-empty": [
|
"no-empty": [
|
||||||
@@ -151,6 +162,12 @@
|
|||||||
"no-prototype-builtins": "off",
|
"no-prototype-builtins": "off",
|
||||||
"no-console": "off",
|
"no-console": "off",
|
||||||
"class-methods-use-this": "off",
|
"class-methods-use-this": "off",
|
||||||
"prefer-destructuring": "off"
|
"prefer-destructuring": "off",
|
||||||
|
"camelcase": "off",
|
||||||
|
"spaced-comment": "off",
|
||||||
|
"no-bitwise": "off",
|
||||||
|
"no-case-declarations": "off",
|
||||||
|
"no-template-curly-in-string": "off",
|
||||||
|
"no-await-in-loop": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-58
@@ -13,9 +13,7 @@
|
|||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
const {Bridge} = require("matrix-appservice-bridge")
|
const { Bridge } = require("matrix-appservice-bridge")
|
||||||
const crypto = require("crypto")
|
|
||||||
const YAML = require("yamljs")
|
|
||||||
const commands = require("./commands")
|
const commands = require("./commands")
|
||||||
const MatrixUser = require("./matrix-user")
|
const MatrixUser = require("./matrix-user")
|
||||||
const TelegramUser = require("./telegram-user")
|
const TelegramUser = require("./telegram-user")
|
||||||
@@ -54,9 +52,10 @@ class MautrixTelegram {
|
|||||||
async run() {
|
async run() {
|
||||||
console.log("Appservice listening on port %s", this.config.appservice.port)
|
console.log("Appservice listening on port %s", this.config.appservice.port)
|
||||||
await this.bridge.run(this.config.appservice.port, {})
|
await this.bridge.run(this.config.appservice.port, {})
|
||||||
const userEntries = await this.bridge.getUserStore().select({
|
const userEntries = await this.bridge.getUserStore()
|
||||||
type: "matrix",
|
.select({
|
||||||
})
|
type: "matrix",
|
||||||
|
})
|
||||||
for (const entry of userEntries) {
|
for (const entry of userEntries) {
|
||||||
const user = MatrixUser.fromEntry(this, entry)
|
const user = MatrixUser.fromEntry(this, entry)
|
||||||
this.matrixUsersByID.set(entry.id, user)
|
this.matrixUsersByID.set(entry.id, user)
|
||||||
@@ -89,7 +88,8 @@ class MautrixTelegram {
|
|||||||
if (peer.type === "user") {
|
if (peer.type === "user") {
|
||||||
query.receiverID = peer.receiverID
|
query.receiverID = peer.receiverID
|
||||||
}
|
}
|
||||||
const entries = await this.bridge.getRoomStore().select(query)
|
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)
|
||||||
@@ -123,10 +123,11 @@ class MautrixTelegram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const entries = await this.bridge.getRoomStore().select({
|
const entries = await this.bridge.getRoomStore()
|
||||||
type: "portal",
|
.select({
|
||||||
roomID: id,
|
type: "portal",
|
||||||
})
|
roomID: id,
|
||||||
|
})
|
||||||
|
|
||||||
// Handle possible db query race conditions
|
// Handle possible db query race conditions
|
||||||
portal = this.portalsByRoomID.get(id)
|
portal = this.portalsByRoomID.get(id)
|
||||||
@@ -151,10 +152,11 @@ class MautrixTelegram {
|
|||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
const entries = await this.bridge.getUserStore().select({
|
const entries = await this.bridge.getUserStore()
|
||||||
type: "remote",
|
.select({
|
||||||
id,
|
type: "remote",
|
||||||
})
|
id,
|
||||||
|
})
|
||||||
|
|
||||||
// Handle possible db query race conditions
|
// Handle possible db query race conditions
|
||||||
if (this.telegramUsersByID.has(id)) {
|
if (this.telegramUsersByID.has(id)) {
|
||||||
@@ -176,10 +178,11 @@ class MautrixTelegram {
|
|||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
const entries = this.bridge.getUserStore().select({
|
const entries = this.bridge.getUserStore()
|
||||||
type: "matrix",
|
.select({
|
||||||
id,
|
type: "matrix",
|
||||||
})
|
id,
|
||||||
|
})
|
||||||
|
|
||||||
// Handle possible db query race conditions
|
// Handle possible db query race conditions
|
||||||
if (this.matrixUsersByID.has(id)) {
|
if (this.matrixUsersByID.has(id)) {
|
||||||
@@ -197,30 +200,33 @@ class MautrixTelegram {
|
|||||||
|
|
||||||
putUser(user) {
|
putUser(user) {
|
||||||
const entry = user.toEntry()
|
const entry = user.toEntry()
|
||||||
return this.bridge.getUserStore().upsert({
|
return this.bridge.getUserStore()
|
||||||
type: entry.type,
|
.upsert({
|
||||||
id: entry.id,
|
type: entry.type,
|
||||||
}, entry)
|
id: entry.id,
|
||||||
|
}, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
putRoom(room) {
|
putRoom(room) {
|
||||||
const entry = room.toEntry()
|
const entry = room.toEntry()
|
||||||
return this.bridge.getRoomStore().upsert({
|
return this.bridge.getRoomStore()
|
||||||
type: entry.type,
|
.upsert({
|
||||||
id: entry.id,
|
type: entry.type,
|
||||||
}, entry)
|
id: entry.id,
|
||||||
|
}, entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleMatrixEvent(evt) {
|
async handleMatrixEvent(evt) {
|
||||||
const asBotID = this.bridge.getBot().getUserId()
|
const asBotID = this.bridge.getBot()
|
||||||
|
.getUserId()
|
||||||
if (evt.type === "m.room.member" && evt.state_key === asBotID) {
|
if (evt.type === "m.room.member" && evt.state_key === asBotID) {
|
||||||
if (evt.content.membership === "invite") {
|
if (evt.content.membership === "invite") {
|
||||||
// Accept all invites
|
// Accept all invites
|
||||||
this.botIntent.join(evt.room_id)
|
this.botIntent.join(evt.room_id)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.warn(`Failed to join room ${evt.room_id}:`, err)
|
console.warn(`Failed to join room ${evt.room_id}:`, err)
|
||||||
if (e instanceof Error) {
|
if (err instanceof Error) {
|
||||||
console.warn(e.stack)
|
console.warn(err.stack)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -229,25 +235,27 @@ class MautrixTelegram {
|
|||||||
|
|
||||||
if (evt.sender === asBotID || evt.type !== "m.room.message" || !evt.content) {
|
if (evt.sender === asBotID || evt.type !== "m.room.message" || !evt.content) {
|
||||||
// Ignore own messages and non-message events.
|
// Ignore own messages and non-message events.
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await this.getMatrixUser(evt.sender)
|
const user = await this.getMatrixUser(evt.sender)
|
||||||
|
|
||||||
const cmdprefix = this.config.bridge.commands.prefix
|
const cmdprefix = this.config.bridge.commands.prefix
|
||||||
if (evt.content.body.startsWith(cmdprefix + " ")) {
|
if (evt.content.body.startsWith(`${cmdprefix} `)) {
|
||||||
if (!user.whitelisted) {
|
if (!user.whitelisted) {
|
||||||
this.botIntent.sendText(evt.room_id, "You are not authorized to use this bridge.")
|
this.botIntent.sendText(evt.room_id, "You are not authorized to use this bridge.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefixLength = cmdprefix.length + 1
|
const prefixLength = cmdprefix.length + 1
|
||||||
const args = evt.content.body.substr(prefixLength).split(" ")
|
const args = evt.content.body.substr(prefixLength)
|
||||||
|
.split(" ")
|
||||||
const command = args.shift()
|
const command = args.shift()
|
||||||
commands.run(user, command, args, reply =>
|
commands.run(
|
||||||
this.botIntent.sendText(
|
user, command, args,
|
||||||
evt.room_id,
|
reply => this.botIntent.sendText(
|
||||||
reply.replace("$cmdprefix", cmdprefix)),
|
evt.room_id,
|
||||||
|
reply.replace("$cmdprefix", cmdprefix)),
|
||||||
this)
|
this)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -260,7 +268,6 @@ class MautrixTelegram {
|
|||||||
const portal = await this.getPortalByRoomID(evt.room_id)
|
const portal = await this.getPortalByRoomID(evt.room_id)
|
||||||
if (portal) {
|
if (portal) {
|
||||||
portal.handleMatrixEvent(user, evt)
|
portal.handleMatrixEvent(user, evt)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +277,7 @@ class MautrixTelegram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userID = userID.toLowerCase()
|
userID = userID.toLowerCase()
|
||||||
const userIDCapture = /\@.+\:(.+)/.exec(userID)
|
const userIDCapture = /@.+:(.+)/.exec(userID)
|
||||||
const homeserver = userIDCapture && userIDCapture.length > 1 ? userIDCapture[1] : undefined
|
const homeserver = userIDCapture && userIDCapture.length > 1 ? userIDCapture[1] : undefined
|
||||||
for (let whitelisted of this.config.bridge.whitelist) {
|
for (let whitelisted of this.config.bridge.whitelist) {
|
||||||
whitelisted = whitelisted.toLowerCase()
|
whitelisted = whitelisted.toLowerCase()
|
||||||
@@ -280,25 +287,6 @@ class MautrixTelegram {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/*encrypt(value) {
|
|
||||||
var cipher = crypto.createCipher("aes-256-gcm", this.config.bridge.auth_key_password);
|
|
||||||
var ret = cipher.update(Buffer.from(value), "hex", "base64");
|
|
||||||
ret += cipher.final("base64");
|
|
||||||
|
|
||||||
return [ret, cipher.getAuthTag().toString("base64")];
|
|
||||||
}
|
|
||||||
|
|
||||||
decrypt(value) {
|
|
||||||
if(!value) return value;
|
|
||||||
|
|
||||||
var decipher = crypto.createDecipher("aes-256-gcm", this.config.bridge.auth_key_password);
|
|
||||||
decipher.setAuthTag(new Buffer(value[1], "base64"));
|
|
||||||
var ret = decipher.update(value[0], "base64", "hex");
|
|
||||||
ret += decipher.final("hex");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
};*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = MautrixTelegram
|
module.exports = MautrixTelegram
|
||||||
|
|||||||
+13
-11
@@ -24,13 +24,13 @@ function run(sender, command, args, reply, app) {
|
|||||||
if (command === "cancel") {
|
if (command === "cancel") {
|
||||||
reply(`${sender.commandStatus.action} cancelled.`)
|
reply(`${sender.commandStatus.action} cancelled.`)
|
||||||
sender.commandStatus = undefined
|
sender.commandStatus = undefined
|
||||||
return
|
return undefined
|
||||||
}
|
}
|
||||||
args.unshift(command)
|
args.unshift(command)
|
||||||
return sender.commandStatus.next(sender, args, reply, app)
|
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 undefined
|
||||||
}
|
}
|
||||||
return commandFunc(sender, args, reply, app)
|
return commandFunc(sender, args, reply, app)
|
||||||
}
|
}
|
||||||
@@ -49,8 +49,8 @@ api <method> <args> - Call a Telegram API method. Args is always a JSON object.
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// Authentication handlers //
|
// Authentication handlers //
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,7 +88,8 @@ commands.enterCode = async (sender, args, reply) => {
|
|||||||
reply(`Logged in successfully as @${sender.telegramPuppet.getDisplayName()}.`)
|
reply(`Logged in successfully as @${sender.telegramPuppet.getDisplayName()}.`)
|
||||||
sender.commandStatus = undefined
|
sender.commandStatus = undefined
|
||||||
} else if (data.status === "need-password") {
|
} else if (data.status === "need-password") {
|
||||||
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}
|
||||||
|
Enter your password using "$cmdprefix <password>"`)
|
||||||
sender.commandStatus = {
|
sender.commandStatus = {
|
||||||
action: "Two-factor authentication",
|
action: "Two-factor authentication",
|
||||||
next: commands.enterPassword,
|
next: commands.enterPassword,
|
||||||
@@ -114,11 +115,12 @@ commands.login = async (sender, args, reply) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await sender.sendTelegramCode(args[0])
|
/*const data = */
|
||||||
|
await sender.sendTelegramCode(args[0])
|
||||||
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: commands.enterCode ,
|
next: commands.enterCode,
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
reply(`Failed to send code: ${err}`)
|
reply(`Failed to send code: ${err}`)
|
||||||
@@ -139,13 +141,13 @@ commands.logout = async (sender, args, reply) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
// General command handlers //
|
// General command handlers //
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
// Debug command handlers //
|
// Debug command handlers //
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
|
|
||||||
commands.api = async (sender, args, reply, app) => {
|
commands.api = async (sender, args, reply, app) => {
|
||||||
|
|||||||
+3
-3
@@ -14,7 +14,7 @@
|
|||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
const {AppServiceRegistration} = require("matrix-appservice-bridge")
|
const { AppServiceRegistration } = require("matrix-appservice-bridge")
|
||||||
const commander = require("commander")
|
const commander = require("commander")
|
||||||
const YAML = require("yamljs")
|
const YAML = require("yamljs")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
@@ -41,11 +41,11 @@ if (commander.generateRegistration) {
|
|||||||
namespaces: {
|
namespaces: {
|
||||||
users: [{
|
users: [{
|
||||||
exclusive: true,
|
exclusive: true,
|
||||||
regex: `@${config.bridge.username_template.replace("${ID}", ".+")}:${config.homeserver.domain}`
|
regex: `@${config.bridge.username_template.replace("${ID}", ".+")}:${config.homeserver.domain}`,
|
||||||
}],
|
}],
|
||||||
aliases: [{
|
aliases: [{
|
||||||
exclusive: true,
|
exclusive: true,
|
||||||
regex: `#${config.bridge.alias_template.replace("${NAME}", ".+")}:${config.homeserver.domain}`
|
regex: `#${config.bridge.alias_template.replace("${NAME}", ".+")}:${config.homeserver.domain}`,
|
||||||
}],
|
}],
|
||||||
rooms: [],
|
rooms: [],
|
||||||
},
|
},
|
||||||
|
|||||||
+11
-10
@@ -97,7 +97,7 @@ class MatrixUser {
|
|||||||
|
|
||||||
async syncContacts() {
|
async syncContacts() {
|
||||||
const contacts = await this.telegramPuppet.client("contacts.getContacts", {
|
const contacts = await this.telegramPuppet.client("contacts.getContacts", {
|
||||||
hash: md5(this.contactIDs.join(","))
|
hash: md5(this.contactIDs.join(",")),
|
||||||
})
|
})
|
||||||
if (contacts._ === "contacts.contactsNotModified") {
|
if (contacts._ === "contacts.contactsNotModified") {
|
||||||
return false
|
return false
|
||||||
@@ -112,7 +112,7 @@ class MatrixUser {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncDialogs({createRooms=true} = {}) {
|
async syncDialogs({ createRooms = true } = {}) {
|
||||||
const dialogs = await this.telegramPuppet.client("messages.getDialogs", {})
|
const dialogs = await this.telegramPuppet.client("messages.getDialogs", {})
|
||||||
let changed = false
|
let changed = false
|
||||||
for (const dialog of dialogs.chats.concat(dialogs.users)) {
|
for (const dialog of dialogs.chats.concat(dialogs.users)) {
|
||||||
@@ -122,7 +122,7 @@ class MatrixUser {
|
|||||||
const peer = new TelegramPeer(dialog._, dialog.id, {
|
const peer = new TelegramPeer(dialog._, dialog.id, {
|
||||||
receiverID: dialog._ === "user"
|
receiverID: dialog._ === "user"
|
||||||
? this.telegramPuppet.userID
|
? this.telegramPuppet.userID
|
||||||
: undefined
|
: undefined,
|
||||||
})
|
})
|
||||||
const portal = await this.app.getPortalByPeer(peer)
|
const portal = await this.app.getPortalByPeer(peer)
|
||||||
if (await portal.updateInfo(this.telegramPuppet, dialog)) {
|
if (await portal.updateInfo(this.telegramPuppet, dialog)) {
|
||||||
@@ -130,7 +130,7 @@ class MatrixUser {
|
|||||||
}
|
}
|
||||||
if (createRooms) {
|
if (createRooms) {
|
||||||
try {
|
try {
|
||||||
const {roomID, created} = await portal.createMatrixRoom(this.telegramPuppet, {
|
const { roomID, created } = await portal.createMatrixRoom(this.telegramPuppet, {
|
||||||
invite: [this.userID],
|
invite: [this.userID],
|
||||||
})
|
})
|
||||||
if (!created) {
|
if (!created) {
|
||||||
@@ -143,7 +143,8 @@ class MatrixUser {
|
|||||||
//if (membership !== "join") {
|
//if (membership !== "join") {
|
||||||
try {
|
try {
|
||||||
await intent.invite(roomID, this.userID)
|
await intent.invite(roomID, this.userID)
|
||||||
} catch (_) {}
|
} catch (_) {
|
||||||
|
}
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -159,11 +160,11 @@ class MatrixUser {
|
|||||||
if (this._telegramPuppet && this._telegramPuppet.userID) {
|
if (this._telegramPuppet && this._telegramPuppet.userID) {
|
||||||
throw new Error("You are already logged in. Please log out before logging in again.")
|
throw new Error("You are already logged in. Please log out before logging in again.")
|
||||||
}
|
}
|
||||||
switch(this.telegramPuppet.checkPhone(phoneNumber)) {
|
switch (this.telegramPuppet.checkPhone(phoneNumber)) {
|
||||||
case "unregistered":
|
case "unregistered":
|
||||||
throw new Error("That number has not been registered. Please register it first.")
|
throw new Error("That number has not been registered. Please register it first.")
|
||||||
case "invalid":
|
case "invalid":
|
||||||
throw new Error("Invalid phone number.")
|
throw new Error("Invalid phone number.")
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const result = await this.telegramPuppet.sendCode(phoneNumber)
|
const result = await this.telegramPuppet.sendCode(phoneNumber)
|
||||||
|
|||||||
+10
-10
@@ -79,8 +79,8 @@ class Portal {
|
|||||||
|
|
||||||
const uploaded = await this.app.botIntent.getClient()
|
const uploaded = await this.app.botIntent.getClient()
|
||||||
.uploadContent({
|
.uploadContent({
|
||||||
stream: new Buffer(file.bytes),
|
stream: Buffer.from(file.bytes),
|
||||||
name: name,
|
name,
|
||||||
type: file.mimetype,
|
type: file.mimetype,
|
||||||
}, { rawResponse: false })
|
}, { rawResponse: false })
|
||||||
|
|
||||||
@@ -106,13 +106,13 @@ class Portal {
|
|||||||
|
|
||||||
async handleMatrixEvent(sender, evt) {
|
async handleMatrixEvent(sender, evt) {
|
||||||
switch (evt.content.msgtype) {
|
switch (evt.content.msgtype) {
|
||||||
case "m.notice":
|
case "m.notice":
|
||||||
case "m.text":
|
case "m.text":
|
||||||
await this.loadAccessHash(sender.telegramPuppet)
|
await this.loadAccessHash(sender.telegramPuppet)
|
||||||
sender.telegramPuppet.sendMessage(this.peer, evt.content.body)
|
sender.telegramPuppet.sendMessage(this.peer, evt.content.body)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.log("Unhandled event:", evt)
|
console.log("Unhandled event:", evt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ class Portal {
|
|||||||
return !!this.roomID
|
return !!this.roomID
|
||||||
}
|
}
|
||||||
|
|
||||||
async createMatrixRoom(telegramPOV, {invite = []} = {}) {
|
async createMatrixRoom(telegramPOV, { invite = [] } = {}) {
|
||||||
if (this.roomID) {
|
if (this.roomID) {
|
||||||
return {
|
return {
|
||||||
created: false,
|
created: false,
|
||||||
|
|||||||
+77
-76
@@ -26,19 +26,19 @@ class TelegramPeer {
|
|||||||
|
|
||||||
static fromTelegramData(peer, sender, receiverID) {
|
static fromTelegramData(peer, sender, 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", sender, {
|
return new TelegramPeer("user", sender, {
|
||||||
accessHash: peer.access_hash,
|
accessHash: peer.access_hash,
|
||||||
receiverID,
|
receiverID,
|
||||||
})
|
})
|
||||||
case "peerChannel":
|
case "peerChannel":
|
||||||
return new TelegramPeer("channel", peer.channel_id, {
|
return new TelegramPeer("channel", peer.channel_id, {
|
||||||
accessHash: peer.access_hash,
|
accessHash: peer.access_hash,
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unrecognized peer type ${peer._}`)
|
throw new Error(`Unrecognized peer type ${peer._}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +71,7 @@ class TelegramPeer {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateInfo(dialog) {
|
async updateInfo(dialog) {
|
||||||
@@ -92,82 +93,82 @@ class TelegramPeer {
|
|||||||
let info,
|
let info,
|
||||||
users
|
users
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case "user":
|
case "user":
|
||||||
info = await telegramPOV.client("users.getFullUser", {
|
info = await telegramPOV.client("users.getFullUser", {
|
||||||
id: this.toInputObject(),
|
id: this.toInputObject(),
|
||||||
})
|
})
|
||||||
users = [info.user]
|
users = [info.user]
|
||||||
info = info.user
|
info = info.user
|
||||||
break
|
break
|
||||||
case "chat":
|
case "chat":
|
||||||
info = await telegramPOV.client("messages.getFullChat", {
|
info = await telegramPOV.client("messages.getFullChat", {
|
||||||
chat_id: this.id,
|
chat_id: this.id,
|
||||||
})
|
})
|
||||||
users = info.users
|
users = info.users
|
||||||
info = info.chats[0]
|
info = info.chats[0]
|
||||||
break
|
break
|
||||||
case "channel":
|
case "channel":
|
||||||
info = await telegramPOV.client("channels.getFullChannel", {
|
info = await telegramPOV.client("channels.getFullChannel", {
|
||||||
channel: this.toInputObject(),
|
channel: this.toInputObject(),
|
||||||
})
|
})
|
||||||
info = info.chats[0]
|
info = info.chats[0]
|
||||||
const participants = await telegramPOV.client("channels.getParticipants", {
|
const participants = await telegramPOV.client("channels.getParticipants", {
|
||||||
channel: this.toInputObject(),
|
channel: this.toInputObject(),
|
||||||
filter: { _: "channelParticipantsRecent" },
|
filter: { _: "channelParticipantsRecent" },
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
})
|
})
|
||||||
users = participants.users
|
users = participants.users
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown peer type ${this.type}`)
|
throw new Error(`Unknown peer type ${this.type}`)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
info: info,
|
info,
|
||||||
users,
|
users,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toInputPeer() {
|
toInputPeer() {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case "chat":
|
case "chat":
|
||||||
return {
|
return {
|
||||||
_: "inputPeerChat",
|
_: "inputPeerChat",
|
||||||
chat_id: this.id,
|
chat_id: this.id,
|
||||||
}
|
}
|
||||||
case "user":
|
case "user":
|
||||||
return {
|
return {
|
||||||
_: "inputPeerUser",
|
_: "inputPeerUser",
|
||||||
user_id: this.id,
|
user_id: this.id,
|
||||||
access_hash: this.accessHash,
|
access_hash: this.accessHash,
|
||||||
}
|
}
|
||||||
case "channel":
|
case "channel":
|
||||||
return {
|
return {
|
||||||
_: "inputPeerChannel",
|
_: "inputPeerChannel",
|
||||||
channel_id: this.id,
|
channel_id: this.id,
|
||||||
access_hash: this.accessHash,
|
access_hash: this.accessHash,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unrecognized peer type ${this.type}`)
|
throw new Error(`Unrecognized peer type ${this.type}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toInputObject() {
|
toInputObject() {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case "user":
|
case "user":
|
||||||
return {
|
return {
|
||||||
_: "inputUser",
|
_: "inputUser",
|
||||||
user_id: this.id,
|
user_id: this.id,
|
||||||
access_hash: this.accessHash,
|
access_hash: this.accessHash,
|
||||||
}
|
}
|
||||||
case "channel":
|
case "channel":
|
||||||
return {
|
return {
|
||||||
_: "inputChannel",
|
_: "inputChannel",
|
||||||
channel_id: this.id,
|
channel_id: this.id,
|
||||||
access_hash: this.accessHash,
|
access_hash: this.accessHash,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unrecognized type ${this.type}`)
|
throw new Error(`Unrecognized type ${this.type}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+84
-82
@@ -13,9 +13,8 @@
|
|||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
const pkg = require("../package.json")
|
|
||||||
const os = require("os")
|
|
||||||
const telegram = require("telegram-mtproto")
|
const telegram = require("telegram-mtproto")
|
||||||
|
const pkg = require("../package.json")
|
||||||
const TelegramPeer = require("./telegram-peer")
|
const TelegramPeer = require("./telegram-peer")
|
||||||
|
|
||||||
const META_FROM_FILETYPE = {
|
const META_FROM_FILETYPE = {
|
||||||
@@ -37,7 +36,7 @@ const META_FROM_FILETYPE = {
|
|||||||
* TelegramPuppet represents a Telegram account being controlled from Matrix.
|
* TelegramPuppet represents a Telegram account being controlled from Matrix.
|
||||||
*/
|
*/
|
||||||
class TelegramPuppet {
|
class TelegramPuppet {
|
||||||
constructor(app, {userID, matrixUser, data, api_hash, api_id, server_config, api_config}) {
|
constructor(app, { userID, matrixUser, data, api_hash, api_id, server_config, api_config }) {
|
||||||
this._client = undefined
|
this._client = undefined
|
||||||
this.userID = userID
|
this.userID = userID
|
||||||
this.matrixUser = matrixUser
|
this.matrixUser = matrixUser
|
||||||
@@ -54,20 +53,20 @@ class TelegramPuppet {
|
|||||||
get: async (key) => {
|
get: async (key) => {
|
||||||
let value = this.data[key]
|
let value = this.data[key]
|
||||||
// TODO test and (enable or remove)
|
// TODO test and (enable or remove)
|
||||||
/*if (typeof(value) === "string" && value.startsWith("b64:")) {
|
if (typeof value === "string" && value.startsWith("b64:")) {
|
||||||
value = Array.from(new Buffer(value.substr("b64:".length), "base64"))
|
value = Array.from(Buffer.from(value.substr("b64:".length), "base64"))
|
||||||
}*/
|
}
|
||||||
return value
|
return value
|
||||||
},
|
},
|
||||||
set: async (key, value) => {
|
set: async (key, value) => {
|
||||||
// TODO test and (enable or remove)
|
// TODO test and (enable or remove)
|
||||||
/*if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
console.log("Non-buffer array")
|
console.log("Non-buffer array")
|
||||||
value = `b64:${new Buffer(value).toString("base64")}`
|
value = `b64:${Buffer.from(value).toString("base64")}`
|
||||||
} else if (value instanceof Buffer) {
|
} else if (value instanceof Buffer) {
|
||||||
console.log("Buffer array")
|
console.log("Buffer array")
|
||||||
value = `b64:${value.toString("base64")}`
|
value = `b64:${value.toString("base64")}`
|
||||||
}*/
|
}
|
||||||
console.warn("SET", key, "=", JSON.stringify(value))
|
console.warn("SET", key, "=", JSON.stringify(value))
|
||||||
if (this.data[key] === value) {
|
if (this.data[key] === value) {
|
||||||
return
|
return
|
||||||
@@ -77,7 +76,7 @@ class TelegramPuppet {
|
|||||||
await this.matrixUser.save()
|
await this.matrixUser.save()
|
||||||
},
|
},
|
||||||
remove: async (...keys) => {
|
remove: async (...keys) => {
|
||||||
console.warn("DEL", JSON.stringify(value))
|
console.warn("DEL", JSON.stringify(keys))
|
||||||
keys.forEach((key) => delete this.data[key])
|
keys.forEach((key) => delete this.data[key])
|
||||||
await this.matrixUser.save()
|
await this.matrixUser.save()
|
||||||
},
|
},
|
||||||
@@ -91,8 +90,8 @@ class TelegramPuppet {
|
|||||||
this.apiConfig = Object.assign({}, {
|
this.apiConfig = Object.assign({}, {
|
||||||
app_version: pkg.version,
|
app_version: pkg.version,
|
||||||
lang_code: "en",
|
lang_code: "en",
|
||||||
api_id: api_id,
|
api_id,
|
||||||
initConnection : 0x69796de9,
|
initConnection: 0x69796de9,
|
||||||
layer: 57,
|
layer: 57,
|
||||||
invokeWithLayer: 0xda9b0d0d,
|
invokeWithLayer: 0xda9b0d0d,
|
||||||
}, api_config)
|
}, api_config)
|
||||||
@@ -120,7 +119,6 @@ class TelegramPuppet {
|
|||||||
|
|
||||||
get client() {
|
get client() {
|
||||||
if (!this._client) {
|
if (!this._client) {
|
||||||
const self = this
|
|
||||||
this._client = telegram.MTProto({
|
this._client = telegram.MTProto({
|
||||||
api: this.apiConfig,
|
api: this.apiConfig,
|
||||||
server: this.serverConfig,
|
server: this.serverConfig,
|
||||||
@@ -212,7 +210,7 @@ class TelegramPuppet {
|
|||||||
const result = await this.client("messages.sendMessage", {
|
const result = await this.client("messages.sendMessage", {
|
||||||
peer: peer.toInputPeer(),
|
peer: peer.toInputPeer(),
|
||||||
message,
|
message,
|
||||||
random_id: ~~(Math.random() * (1<<30)),
|
random_id: ~~(Math.random() * (1 << 30)),
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -220,8 +218,8 @@ class TelegramPuppet {
|
|||||||
async sendMedia(peer, media) {
|
async sendMedia(peer, media) {
|
||||||
const result = await this.client("messages.sendMedia", {
|
const result = await this.client("messages.sendMedia", {
|
||||||
peer: peer.toInputPeer(),
|
peer: peer.toInputPeer(),
|
||||||
media: media,
|
media,
|
||||||
random_id: ~~(Math.random() * (1<<30)),
|
random_id: ~~(Math.random() * (1 << 30)),
|
||||||
})
|
})
|
||||||
// TODO use result? (maybe the ID)
|
// TODO use result? (maybe the ID)
|
||||||
return result
|
return result
|
||||||
@@ -241,80 +239,83 @@ class TelegramPuppet {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let peer, portal
|
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)
|
||||||
let status
|
let status
|
||||||
switch(update.status._) {
|
switch (update.status._) {
|
||||||
case "userStatusOnline":
|
case "userStatusOnline":
|
||||||
status = "online"
|
status = "online"
|
||||||
break
|
|
||||||
case "userStatusOffline":
|
|
||||||
default:
|
|
||||||
status = "offline"
|
|
||||||
}
|
|
||||||
|
|
||||||
await user.intent.getClient().setPresence({presence: status})
|
|
||||||
break
|
|
||||||
case "updateUserTyping":
|
|
||||||
peer = new TelegramPeer("user", update.user_id, { receiverID: this.userID })
|
|
||||||
case "updateChatUserTyping":
|
|
||||||
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. Once 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
|
|
||||||
case "updateShortMessage":
|
|
||||||
peer = new TelegramPeer("user", update.user_id, { receiverID: this.userID })
|
|
||||||
case "updateShortChatMessage":
|
|
||||||
peer = peer || new TelegramPeer("chat", update.chat_id)
|
|
||||||
await this.handleMessage({
|
|
||||||
from: update.user_id,
|
|
||||||
to: peer,
|
|
||||||
text: update.message,
|
|
||||||
})
|
|
||||||
break
|
|
||||||
case "updateNewChannelMessage":
|
|
||||||
case "updateNewMessage":
|
|
||||||
// TODO handle other content types
|
|
||||||
update = update.message // Message defined at message#90dddc11 in layer 71
|
|
||||||
await this.handleMessage({
|
|
||||||
from: update.from_id,
|
|
||||||
to: TelegramPeer.fromTelegramData(update.to_id, update.from_id, this.userID),
|
|
||||||
text: update.message,
|
|
||||||
})
|
|
||||||
break
|
break
|
||||||
|
case "userStatusOffline":
|
||||||
default:
|
default:
|
||||||
console.log(`Update of type ${update._} received:\n${JSON.stringify(update, "", " ")}`)
|
status = "offline"
|
||||||
|
}
|
||||||
|
|
||||||
|
await user.intent.getClient()
|
||||||
|
.setPresence({ presence: status })
|
||||||
|
break
|
||||||
|
case "updateUserTyping":
|
||||||
|
peer = new TelegramPeer("user", update.user_id, { receiverID: this.userID })
|
||||||
|
/* falls through */
|
||||||
|
case "updateChatUserTyping":
|
||||||
|
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. Once 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
|
||||||
|
case "updateShortMessage":
|
||||||
|
peer = new TelegramPeer("user", update.user_id, { receiverID: this.userID })
|
||||||
|
/* falls through */
|
||||||
|
case "updateShortChatMessage":
|
||||||
|
peer = peer || new TelegramPeer("chat", update.chat_id)
|
||||||
|
await this.handleMessage({
|
||||||
|
from: update.user_id,
|
||||||
|
to: peer,
|
||||||
|
text: update.message,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case "updateNewChannelMessage":
|
||||||
|
case "updateNewMessage":
|
||||||
|
// TODO handle other content types
|
||||||
|
update = update.message // Message defined at message#90dddc11 in layer 71
|
||||||
|
await this.handleMessage({
|
||||||
|
from: update.from_id,
|
||||||
|
to: TelegramPeer.fromTelegramData(update.to_id, update.from_id, this.userID),
|
||||||
|
text: update.message,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.log(`Update of type ${update._} received:\n${JSON.stringify(update, "", " ")}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpdate(data) {
|
handleUpdate(data) {
|
||||||
try {
|
try {
|
||||||
switch (data._) {
|
switch (data._) {
|
||||||
case "updateShort":
|
case "updateShort":
|
||||||
this.onUpdate(data.update)
|
this.onUpdate(data.update)
|
||||||
break
|
break
|
||||||
case "updates":
|
case "updates":
|
||||||
for (const update of data.updates) {
|
for (const update of data.updates) {
|
||||||
this.onUpdate(update)
|
this.onUpdate(update)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "updateShortMessage":
|
case "updateShortMessage":
|
||||||
case "updateShortChatMessage":
|
case "updateShortChatMessage":
|
||||||
this.onUpdate(data)
|
this.onUpdate(data)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
console.log("Unrecognized update type:", data._)
|
console.log("Unrecognized update type:", data._)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error handling update:", err)
|
console.error("Error handling update:", err)
|
||||||
console.log(e.stack)
|
console.log(err.stack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,8 +356,9 @@ class TelegramPuppet {
|
|||||||
}
|
}
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
const state = this.client("updates.getState", {})
|
|
||||||
// TODO use state?
|
// TODO use state?
|
||||||
|
/*const state = */
|
||||||
|
this.client("updates.getState", {})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error updating state:", err)
|
console.error("Error updating state:", err)
|
||||||
}
|
}
|
||||||
@@ -364,12 +366,12 @@ class TelegramPuppet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getFile(location) {
|
async getFile(location) {
|
||||||
location = Object.assign({}, location, {_: "inputFileLocation"})
|
location = Object.assign({}, location, { _: "inputFileLocation" })
|
||||||
delete location.dc_id
|
delete location.dc_id
|
||||||
const file = await this.client("upload.getFile", {
|
const file = await this.client("upload.getFile", {
|
||||||
location,
|
location,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 100*1024*1024,
|
limit: 100 * 1024 * 1024,
|
||||||
})
|
})
|
||||||
const meta = META_FROM_FILETYPE[file.type._]
|
const meta = META_FROM_FILETYPE[file.type._]
|
||||||
if (meta) {
|
if (meta) {
|
||||||
|
|||||||
@@ -91,8 +91,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()) {
|
||||||
this.intent.setDisplayName(
|
this.intent.setDisplayName(this.app.config.bridge.displayname_template
|
||||||
this.app.config.bridge.displayname_template.replace("${DISPLAYNAME}", this.getDisplayName()))
|
.replace("${DISPLAYNAME}", this.getDisplayName()))
|
||||||
}
|
}
|
||||||
if (updateAvatar && this.updateAvatar(telegramPOV, user)) {
|
if (updateAvatar && this.updateAvatar(telegramPOV, user)) {
|
||||||
changed = true
|
changed = true
|
||||||
@@ -172,8 +172,8 @@ class TelegramUser {
|
|||||||
const name = `${photo.volume_id}_${photo.local_id}.${file.extension}`
|
const name = `${photo.volume_id}_${photo.local_id}.${file.extension}`
|
||||||
|
|
||||||
const uploaded = await this.uploadContent({
|
const uploaded = await this.uploadContent({
|
||||||
stream: new Buffer(file.bytes),
|
stream: Buffer.from(file.bytes),
|
||||||
name: name,
|
name,
|
||||||
type: file.mimetype,
|
type: file.mimetype,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user