Show phone number when username doesn't exist. Fixes #213

This commit is contained in:
Tulir Asokan
2018-09-28 02:46:02 +03:00
parent 8d982b4615
commit a32bc2985a
9 changed files with 90 additions and 36 deletions
@@ -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")
+3 -2
View File
@@ -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:
+1
View File
@@ -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"
+15 -4
View File
@@ -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
+13 -7
View File
@@ -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",
+11 -8
View File
@@ -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:
+7 -1
View File
@@ -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
+11 -10
View File
@@ -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)
+4 -4
View File
@@ -51,25 +51,25 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
</head>
<body>
<main class="container">
% if username:
% if human_tg_id:
% if state == "logged-in":
<h1>Logged in successfully!</h1>
<p>
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.
</p>
% elif state == "bot-logged-in":
<h1>Logged in successfully!</h1>
<p>
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.
</p>
% else:
<h1>You're already logged in!</h1>
<p>
You're logged in as @${username}.
You're logged in as ${human_tg_id}.
</p>
<p>
If you want to log in with another account, log out using the <code>logout</code>