diff --git a/alembic/versions/a9119be92164_add_phone_number_field_to_users.py b/alembic/versions/a9119be92164_add_phone_number_field_to_users.py
new file mode 100644
index 00000000..a8892515
--- /dev/null
+++ b/alembic/versions/a9119be92164_add_phone_number_field_to_users.py
@@ -0,0 +1,25 @@
+"""Add phone number field to users
+
+Revision ID: a9119be92164
+Revises: b54929c22c86
+Create Date: 2018-09-28 02:38:40.626282
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "a9119be92164"
+down_revision = "b54929c22c86"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.add_column("user", sa.Column("tg_phone", sa.String(), nullable=True))
+
+
+def downgrade():
+ with op.batch_alter_table("user") as batch_op:
+ batch_op.drop_column("tg_phone")
diff --git a/mautrix_telegram/commands/auth.py b/mautrix_telegram/commands/auth.py
index 929e19f9..fb4bc0ea 100644
--- a/mautrix_telegram/commands/auth.py
+++ b/mautrix_telegram/commands/auth.py
@@ -173,7 +173,7 @@ async def enter_code_register(evt: CommandEvent) -> Dict:
help_text="Get instructions on how to log in.")
async def login(evt: CommandEvent) -> Optional[Dict]:
if await evt.sender.is_logged_in():
- return await evt.reply("You are already logged in.")
+ return await evt.reply(f"You are already logged in as {evt.sender.human_tg_id}.")
allow_matrix_login = evt.config.get("bridge.allow_matrix_login", True)
if allow_matrix_login:
@@ -300,7 +300,8 @@ async def sign_in(evt: CommandEvent, **sign_in_info) -> Dict:
user = await evt.sender.client.sign_in(**sign_in_info)
asyncio.ensure_future(evt.sender.post_login(user), loop=evt.loop)
evt.sender.command_status = None
- return await evt.reply(f"Successfully logged in as @{user.username}")
+ name = f"@{user.username}" if user.username else f"+{user.phone}"
+ return await evt.reply(f"Successfully logged in as {name}")
except PhoneCodeExpiredError:
return await evt.reply("Phone code expired. Try again with `$cmdprefix+sp login`.")
except PhoneCodeInvalidError:
diff --git a/mautrix_telegram/db.py b/mautrix_telegram/db.py
index b17ef5e8..c78da4a5 100644
--- a/mautrix_telegram/db.py
+++ b/mautrix_telegram/db.py
@@ -81,6 +81,7 @@ class User(Base):
mxid = Column(String, primary_key=True) # type: MatrixUserID
tgid = Column(Integer, nullable=True, unique=True) # type: Optional[TelegramID]
tg_username = Column(String, nullable=True)
+ tg_phone = Column(String, nullable=True)
saved_contacts = Column(Integer, default=0, nullable=False)
contacts = relationship("Contact", uselist=True,
cascade="save-update, merge, delete, delete-orphan"
diff --git a/mautrix_telegram/user.py b/mautrix_telegram/user.py
index e4655425..92efc9d3 100644
--- a/mautrix_telegram/user.py
+++ b/mautrix_telegram/user.py
@@ -48,7 +48,8 @@ class User(AbstractUser):
by_tgid = {} # type: Dict[int, User]
def __init__(self, mxid: MatrixUserID, tgid: Optional[TelegramID] = None,
- username: Optional[str] = None, db_contacts: Optional[List[DBContact]] = None,
+ username: Optional[str] = None, phone: Optional[str] = None,
+ db_contacts: Optional[List[DBContact]] = None,
saved_contacts: int = 0, is_bot: bool = False,
db_portals: Optional[List[DBPortal]] = None,
db_instance: Optional[DBUser] = None) -> None:
@@ -57,6 +58,7 @@ class User(AbstractUser):
self.tgid = tgid # type: TelegramID
self.is_bot = is_bot # type: bool
self.username = username # type: str
+ self.phone = phone # type: str
self.contacts = [] # type: List[pu.Puppet]
self.saved_contacts = saved_contacts # type: int
self.db_contacts = db_contacts # type: List[DBContact]
@@ -86,6 +88,10 @@ class User(AbstractUser):
match = re.compile("@(.+):(.+)").match(self.mxid) # type: Match
return match.group(1)
+ @property
+ def human_tg_id(self) -> str:
+ return f"@{self.username}" if self.username else f"+{self.phone}" or None
+
# TODO replace with proper displayname getting everywhere
@property
def displayname(self) -> str:
@@ -127,7 +133,8 @@ class User(AbstractUser):
def save(self) -> None:
self.db_instance.tgid = self.tgid
- self.db_instance.username = self.username
+ self.db_instance.tg_username = self.username
+ self.db_instance.tg_phone = self.phone
self.db_instance.contacts = self.db_contacts
self.db_instance.saved_contacts = self.saved_contacts
self.db_instance.portals = self.db_portals
@@ -145,8 +152,9 @@ class User(AbstractUser):
@classmethod
def from_db(cls, db_user: DBUser) -> 'User':
- return User(db_user.mxid, db_user.tgid, db_user.tg_username, db_user.contacts,
- db_user.saved_contacts, False, db_user.portals, db_instance=db_user)
+ return User(db_user.mxid, db_user.tgid, db_user.tg_username, db_user.tg_phone,
+ db_user.contacts, db_user.saved_contacts, False, db_user.portals,
+ db_instance=db_user)
# endregion
# region Telegram connection management
@@ -215,6 +223,9 @@ class User(AbstractUser):
if self.username != info.username:
self.username = info.username
changed = True
+ if self.phone != info.phone:
+ self.phone = info.phone
+ changed = True
if self.tgid != info.id:
self.tgid = info.id
self.by_tgid[self.tgid] = self
diff --git a/mautrix_telegram/web/common/auth_api.py b/mautrix_telegram/web/common/auth_api.py
index 5bca5c3e..f4b2da93 100644
--- a/mautrix_telegram/web/common/auth_api.py
+++ b/mautrix_telegram/web/common/auth_api.py
@@ -38,14 +38,15 @@ class AuthAPI(abc.ABC):
@abstractmethod
def get_login_response(self, status: int = 200, state: str = "", username: str = "",
- mxid: str = "", message: str = "", error: str = "",
- errcode: str = "") -> web.Response:
+ phone: str = "", human_tg_id: str = "", mxid: str = "",
+ message: str = "", error: str = "", errcode: str = "") -> web.Response:
raise NotImplementedError()
@abstractmethod
def get_mx_login_response(self, status: int = 200, state: str = "", username: str = "",
- mxid: str = "", message: str = "", error: str = "",
- errcode: str = "") -> web.Response:
+ phone: str = "", human_tg_id: str = "", mxid: str = "",
+ message: str = "", error: str = "", errcode: str = ""
+ ) -> web.Response:
raise NotImplementedError()
async def post_matrix_token(self, user: User, token: str) -> web.Response:
@@ -114,7 +115,8 @@ class AuthAPI(abc.ABC):
if user.command_status and user.command_status["action"] == "Login":
user.command_status = None
return self.get_login_response(mxid=user.mxid, state="logged-in", status=200,
- username=user_info.username)
+ username=user_info.username, phone=None,
+ human_tg_id=f"@{user_info.username}")
except AccessTokenInvalidError:
return self.get_login_response(mxid=user.mxid, state="token", status=401,
errcode="bot_token_invalid",
@@ -135,8 +137,10 @@ class AuthAPI(abc.ABC):
asyncio.ensure_future(user.post_login(user_info), loop=self.loop)
if user.command_status and user.command_status["action"] == "Login":
user.command_status = None
+ human_tg_id = f"@{user_info.username}" if user_info.username else f"+{user_info.phone}"
return self.get_login_response(mxid=user.mxid, state="logged-in", status=200,
- username=user_info.username)
+ username=user_info.username, phone=user_info.phone,
+ human_tg_id=human_tg_id)
except PhoneCodeInvalidError:
return self.get_login_response(mxid=user.mxid, state="code", status=401,
errcode="phone_code_invalid",
@@ -168,8 +172,10 @@ class AuthAPI(abc.ABC):
asyncio.ensure_future(user.post_login(user_info), loop=self.loop)
if user.command_status and user.command_status["action"] == "Login (password entry)":
user.command_status = None
+ human_tg_id = f"@{user_info.username}" if user_info.username else f"+{user_info.phone}"
return self.get_login_response(mxid=user.mxid, state="logged-in", status=200,
- username=user_info.username)
+ username=user_info.username, phone=user_info.phone,
+ human_tg_id=human_tg_id)
except PasswordEmptyError:
return self.get_login_response(mxid=user.mxid, state="password", status=400,
errcode="password_empty",
diff --git a/mautrix_telegram/web/provisioning/__init__.py b/mautrix_telegram/web/provisioning/__init__.py
index 1ff81995..4124054c 100644
--- a/mautrix_telegram/web/provisioning/__init__.py
+++ b/mautrix_telegram/web/provisioning/__init__.py
@@ -75,7 +75,7 @@ class ProvisioningAPI(AuthAPI):
return self.get_error_response(404, "portal_not_found",
"Portal with given Matrix ID not found.")
user, _ = await self.get_user(request.query.get("user_id", None), expect_logged_in=None,
- require_puppeting=False)
+ require_puppeting=False)
return web.json_response({
"mxid": portal.mxid,
"chat_id": get_peer_id(portal.peer),
@@ -102,7 +102,7 @@ class ProvisioningAPI(AuthAPI):
return self.get_error_response(404, "portal_not_found",
"Portal to given Telegram chat not found.")
user, _ = await self.get_user(request.query.get("user_id", None), expect_logged_in=None,
- require_puppeting=False)
+ require_puppeting=False)
return web.json_response({
"mxid": portal.mxid,
"chat_id": get_peer_id(portal.peer),
@@ -380,16 +380,18 @@ class ProvisioningAPI(AuthAPI):
"errcode": errcode,
}, status=status)
- def get_mx_login_response(self, status=200, state="", username="", mxid="", message="",
- error="", errcode=""):
+ def get_mx_login_response(self, status=200, state="", username="", phone="", human_tg_id="",
+ mxid="", message="", error="", errcode=""):
raise NotImplementedError()
- def get_login_response(self, status=200, state="", username="", mxid="", message="", error="",
- errcode="") -> web.Response:
- if username:
+ def get_login_response(self, status=200, state="", username="", phone: str = "",
+ human_tg_id: str = "", mxid="", message="", error="", errcode=""
+ ) -> web.Response:
+ if username or phone:
resp = {
"state": "logged-in",
"username": username,
+ "phone": phone,
}
elif message:
resp = {
@@ -436,7 +438,8 @@ class ProvisioningAPI(AuthAPI):
if expect_logged_in is not None:
logged_in = await user.is_logged_in()
if not expect_logged_in and logged_in:
- return user, self.get_login_response(username=user.username, status=409,
+ return user, self.get_login_response(username=user.username, phone=user.phone,
+ status=409,
error="You are already logged in.",
errcode="already_logged_in")
elif expect_logged_in and not logged_in:
diff --git a/mautrix_telegram/web/provisioning/spec.yaml b/mautrix_telegram/web/provisioning/spec.yaml
index 6fe3bcc9..155bbe9b 100644
--- a/mautrix_telegram/web/provisioning/spec.yaml
+++ b/mautrix_telegram/web/provisioning/spec.yaml
@@ -716,6 +716,9 @@ responses:
username:
type: string
description: The Telegram username the user is logged in as.
+ phone:
+ type: string
+ description: The phone number of the account the user is logged into.
BadRequest:
description: Invalid JSON.
schema:
@@ -800,7 +803,7 @@ definitions:
example: A.
phone:
type: string
- example: +123456789
+ example: 123456789
is_bot:
type: boolean
example: false
@@ -858,6 +861,9 @@ definitions:
username:
type: string
description: The Telegram username the user is logged in as. Only applicable if state=logged-in
+ phone:
+ type: string
+ description: The phone number of the account the user logged into. Only applicable if state=logged-in
HumanReadableError:
type: string
diff --git a/mautrix_telegram/web/public/__init__.py b/mautrix_telegram/web/public/__init__.py
index ec6a643e..36717872 100644
--- a/mautrix_telegram/web/public/__init__.py
+++ b/mautrix_telegram/web/public/__init__.py
@@ -84,7 +84,7 @@ class PublicBridgeWebsite(AuthAPI):
if not await user.is_logged_in():
return self.get_login_response(mxid=user.mxid, state=state)
- return self.get_login_response(mxid=user.mxid, username=user.username)
+ return self.get_login_response(mxid=user.mxid, human_tg_id=user.human_tg_id)
async def get_matrix_login(self, request: web.Request) -> web.Response:
mxid = self.verify_token(request.rel_url.query.get("token", None), endpoint="/matrix-login")
@@ -109,18 +109,19 @@ class PublicBridgeWebsite(AuthAPI):
return self.get_mx_login_response(mxid=user.mxid)
def get_login_response(self, status: int = 200, state: str = "", username: str = "",
- mxid: str = "", message: str = "", error: str = "",
- errcode: str = "") -> web.Response:
+ phone: str = "", human_tg_id: str = "", mxid: str = "",
+ message: str = "", error: str = "", errcode: str = "") -> web.Response:
return web.Response(status=status, content_type="text/html",
- text=self.login.render(username=username, state=state, error=error,
- message=message, mxid=mxid))
+ text=self.login.render(human_tg_id=human_tg_id, state=state,
+ error=error, message=message, mxid=mxid))
def get_mx_login_response(self, status: int = 200, state: str = "", username: str = "",
- mxid: str = "", message: str = "", error: str = "",
- errcode: str = "") -> web.Response:
+ phone: str = "", human_tg_id: str = "", mxid: str = "",
+ message: str = "", error: str = "", errcode: str = ""
+ ) -> web.Response:
return web.Response(status=status, content_type="text/html",
- text=self.mx_login.render(username=username, state=state, error=error,
- message=message, mxid=mxid))
+ text=self.mx_login.render(human_tg_id=human_tg_id, state=state,
+ error=error, message=message, mxid=mxid))
async def post_matrix_login(self, request: web.Request) -> web.Response:
mxid = self.verify_token(request.rel_url.query.get("token", None), endpoint="/matrix-login")
@@ -157,7 +158,7 @@ class PublicBridgeWebsite(AuthAPI):
return self.get_login_response(mxid=user.mxid, error="You are not whitelisted.",
status=403)
elif await user.is_logged_in():
- return self.get_login_response(mxid=user.mxid, username=user.username)
+ return self.get_login_response(mxid=user.mxid, human_tg_id=user.human_tg_id)
await user.ensure_started(even_if_no_session=True)
diff --git a/mautrix_telegram/web/public/login.html.mako b/mautrix_telegram/web/public/login.html.mako
index 96db7bac..b92718fc 100644
--- a/mautrix_telegram/web/public/login.html.mako
+++ b/mautrix_telegram/web/public/login.html.mako
@@ -51,25 +51,25 @@ along with this program. If not, see .
- % if username:
+ % if human_tg_id:
% if state == "logged-in":
Logged in successfully!
- Logged in as @${username}.
+ Logged in as ${human_tg_id}.
You can now close this page.
You should be invited to Telegram portals on Matrix momentarily.
% elif state == "bot-logged-in":
Logged in successfully!
- Logged in as @${username}.
+ Logged in as ${human_tg_id}.
You can now close this page.
You should be invited to Telegram portals on Matrix momentarily.
% else:
You're already logged in!
- You're logged in as @${username}.
+ You're logged in as ${human_tg_id}.
If you want to log in with another account, log out using the logout