diff --git a/src/app.js b/src/app.js
index 5ba2d1e6..3559056c 100644
--- a/src/app.js
+++ b/src/app.js
@@ -38,6 +38,7 @@ class MautrixTelegram {
this.telegramUsersByID = new Map()
this.portalsByPeerID = new Map()
this.portalsByRoomID = new Map()
+ this.managementRooms = []
const self = this
this.bridge = new Bridge({
@@ -304,45 +305,69 @@ class MautrixTelegram {
}, entry)
}
+ async getRoomMembers(roomID) {
+ const roomState = await this.botIntent.roomState(roomID)
+ const members = []
+ for (const event of roomState) {
+ if (event.type === "m.room.member" && event.membership === "join") {
+ members.push(event.user_id)
+ }
+ }
+ return members
+ }
+
/**
* Handle a single received Matrix event.
*
* @param evt The Matrix event that occurred.
*/
async handleMatrixEvent(evt) {
- const asBotID = this.bridge.getBot()
- .getUserId()
- if (evt.type === "m.room.member" && evt.state_key === asBotID) {
- if (evt.content.membership === "invite") {
- // Accept all invites
- this.botIntent.join(evt.room_id)
- .catch(err => {
- console.warn(`Failed to join room ${evt.room_id}:`, err)
- if (err instanceof Error) {
- console.warn(err.stack)
- }
- })
+ const user = await this.getMatrixUser(evt.sender)
+ if (!user.whitelisted) {
+ return
+ }
+
+ const asBotID = this.bridge.getBot().getUserId()
+ if (evt.type === "m.room.member" && evt.state_key === asBotID && evt.content.membership === "invite") {
+ // Accept all invites
+ try {
+ await this.botIntent.join(evt.room_id)
+ } catch (err) {
+ console.error(`Failed to join room ${evt.room_id}:`, err)
+ if (err instanceof Error) {
+ console.error(err.stack)
+ }
}
return
}
+ // TODO to handle joining telegram groups and initiating private chats, we need to handle room member events.
if (evt.sender === asBotID || evt.type !== "m.room.message" || !evt.content) {
// Ignore own messages and non-message events.
return
}
- const user = await this.getMatrixUser(evt.sender)
+ const portal = await this.getPortalByRoomID(evt.room_id)
+ if (portal) {
+ portal.handleMatrixEvent(user, evt)
+ return
+ }
- const cmdprefix = this.config.bridge.commands.prefix
- if (evt.content.body.startsWith(`${cmdprefix} `)) {
- if (!user.whitelisted) {
- this.botIntent.sendText(evt.room_id, "You are not authorized to use this bridge.")
- return
+ let isManagement = this.managementRooms.includes(evt.room_id)
+ if (!isManagement) {
+ const roomMembers = await this.getRoomMembers(evt.room_id)
+ if (roomMembers.length === 2 && roomMembers.includes(asBotID)) {
+ this.managementRooms.push(evt.room_id)
+ isManagement = true
}
-
+ }
+ const cmdprefix = this.config.bridge.commands.prefix
+ if (isManagement || evt.content.body.startsWith(`${cmdprefix} `)) {
const prefixLength = cmdprefix.length + 1
- const args = evt.content.body.substr(prefixLength)
- .split(" ")
+ if (evt.content.body.startsWith(`${cmdprefix} `)) {
+ evt.content.body = evt.content.body.substr(prefixLength)
+ }
+ const args = evt.content.body.split(" ")
const command = args.shift()
const replyFunc = (reply, { allowHTML = false, markdown = true } = {}) => {
reply = reply.replace("$cmdprefix", cmdprefix)
@@ -351,7 +376,7 @@ class MautrixTelegram {
}
if (markdown) {
reply = marked(reply, {
- sanitize: allowHTML,
+ sanitize: !allowHTML,
})
}
this.botIntent.sendMessage(
@@ -362,18 +387,7 @@ class MautrixTelegram {
format: "org.matrix.custom.html",
})
}
- commands.run(user, command, args, replyFunc, this)
- return
- }
-
- if (!user.whitelisted) {
- // Non-management command from non-whitelisted user -> fail silently.
- return
- }
-
- const portal = await this.getPortalByRoomID(evt.room_id)
- if (portal) {
- portal.handleMatrixEvent(user, evt)
+ commands.run(user, command, args, replyFunc, this, evt)
}
}
diff --git a/src/commands.js b/src/commands.js
index 6f81974a..8191ddc7 100644
--- a/src/commands.js
+++ b/src/commands.js
@@ -17,7 +17,7 @@ const makePasswordHash = require("telegram-mtproto").plugins.makePasswordHash
const commands = {}
-function run(sender, command, args, reply, app) {
+function run(sender, command, args, reply, app, evt) {
const commandFunc = this.commands[command]
if (!commandFunc) {
if (sender.commandStatus) {
@@ -27,13 +27,13 @@ function run(sender, command, args, reply, app) {
return undefined
}
args.unshift(command)
- return sender.commandStatus.next(sender, args, reply, app)
+ return sender.commandStatus.next(sender, args, reply, app, evt)
}
reply("Unknown command. Try `$cmdprefix help` for help.")
return undefined
}
try {
- return commandFunc(sender, args, reply, app)
+ return commandFunc(sender, args, reply, app, evt)
} catch (err) {
reply(`Error running command: ${err}.`)
if (err instanceof Error) {
@@ -46,17 +46,28 @@ function run(sender, command, args, reply, app) {
commands.cancel = () => "Nothing to cancel."
-commands.help = (sender, args, reply) => {
- reply(`All commands are prefixed with **$cmdprefix**.
-
+commands.help = (sender, args, reply, app, evt) => {
+ let replyMsg = ""
+ if (app.managementRooms.includes(evt.room_id)) {
+ replyMsg += "This is a management room: prefixing commands with `$cmdprefix` is not required.\n"
+ } else {
+ replyMsg += "This is not a management room: you must prefix commands with `$cmdprefix`.\n"
+ }
+ replyMsg += `
**help** - Show this help message.
**cancel** - Cancel an ongoing action (such as login).
-**login** <_phone_> - Request an authentication code.
+**login** <_phone_> - Request an authentication code.
**logout** - Log out from Telegram. Currently broken.
-**api** <_method_> <_args_> - Call a Telegram API method. Args is always a JSON object. Disabled by default.
-`, { allowHTML: true })
+**api** <_method_> <_args_> - Call a Telegram API method. Args is always a JSON object. Disabled by default.
+`
+ reply(replyMsg, { allowHTML: true })
+}
+
+commands.setManagement = async (sender, _, reply, app, evt) => {
+ app.managementRooms.push(evt.room_id)
+ reply("Room marked as management room. You can now run commands without the `$cmdprefix` prefix.")
}
@@ -233,7 +244,6 @@ commands.pm = async (sender, args, reply, app) => {
})
}
-
////////////////////////////
// Debug command handlers //
////////////////////////////
diff --git a/src/telegram-puppet.js b/src/telegram-puppet.js
index bbd5a951..96e2689e 100644
--- a/src/telegram-puppet.js
+++ b/src/telegram-puppet.js
@@ -179,8 +179,7 @@ class TelegramPuppet {
getDisplayName() {
if (this.data.firstName || this.data.lastName) {
- return [this.data.firstName, this.data.lastName].filter(s => !!s)
- .join(" ")
+ return [this.data.firstName, this.data.lastName].filter(s => !!s).join(" ")
} else if (this.data.username) {
return this.data.username
}
diff --git a/src/telegram-user.js b/src/telegram-user.js
index c2d97b87..5dad0e6d 100644
--- a/src/telegram-user.js
+++ b/src/telegram-user.js
@@ -72,19 +72,19 @@ class TelegramUser {
async updateInfo(telegramPOV, user, { updateAvatar = false } = {}) {
let changed = false
- if (this.firstName !== user.first_name) {
+ if (user.first_name && this.firstName !== user.first_name) {
this.firstName = user.first_name
changed = true
}
- if (this.lastName !== user.last_name) {
+ if (user.last_name && this.lastName !== user.last_name) {
this.lastName = user.last_name
changed = true
}
- if (this.username !== user.username) {
+ if (user.username && this.username !== user.username) {
this.username = user.username
changed = true
}
- if (telegramPOV && this.accessHashes.get(telegramPOV.userID) !== user.access_hash) {
+ if (user.access_hash && telegramPOV && this.accessHashes.get(telegramPOV.userID) !== user.access_hash) {
this.accessHashes.set(telegramPOV.userID, user.access_hash)
changed = true
}
@@ -116,8 +116,7 @@ class TelegramUser {
}
getFirstAndLastName() {
- return [this.firstName, this.lastName].filter(s => !!s)
- .join(" ")
+ return [this.firstName, this.lastName].filter(s => !!s).join(" ")
}
getDisplayName() {