Remove more Python 3.5 compatibility
This commit is contained in:
@@ -73,7 +73,7 @@ if args.generate_registration:
|
||||
sys.exit(0)
|
||||
|
||||
logging.config.dictConfig(copy.deepcopy(config["logging"]))
|
||||
log = logging.getLogger("mau.init") # type: logging.Logger
|
||||
log: logging.Logger = logging.getLogger("mau.init")
|
||||
log.debug(f"Initializing mautrix-telegram {__version__}")
|
||||
|
||||
db_engine = sql.create_engine(config["appservice.database"] or "sqlite:///mautrix-telegram.db")
|
||||
@@ -91,7 +91,7 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
loop = asyncio.get_event_loop() # type: asyncio.AbstractEventLoop
|
||||
loop: asyncio.AbstractEventLoop = asyncio.get_event_loop()
|
||||
|
||||
state_store = SQLStateStore()
|
||||
mebibyte = 1024 ** 2
|
||||
@@ -123,7 +123,7 @@ if config["metrics.enabled"]:
|
||||
if prometheus:
|
||||
prometheus.start_http_server(config["metrics.listen_port"])
|
||||
else:
|
||||
log.warn("Metrics are enabled in the config, but prometheus-async is not installed.")
|
||||
log.warn("Metrics are enabled in the config, but prometheus_client is not installed.")
|
||||
|
||||
with appserv.run(config["appservice.hostname"], config["appservice.port"]) as start:
|
||||
start_ts = time()
|
||||
@@ -131,9 +131,9 @@ with appserv.run(config["appservice.hostname"], config["appservice.port"]) as st
|
||||
init_abstract_user(context)
|
||||
init_formatter(context)
|
||||
init_portal(context)
|
||||
startup_actions = (init_puppet(context) +
|
||||
init_user(context) +
|
||||
[start, context.mx.init_as_bot()]) # type: List[Awaitable[Any]]
|
||||
startup_actions: List[Awaitable[Any]] = (init_puppet(context) +
|
||||
init_user(context) +
|
||||
[start, context.mx.init_as_bot()])
|
||||
|
||||
if context.bot:
|
||||
startup_actions.append(context.bot.start())
|
||||
|
||||
@@ -42,9 +42,9 @@ if TYPE_CHECKING:
|
||||
from .config import Config
|
||||
from .bot import Bot
|
||||
|
||||
config = None # type: Config
|
||||
config: Optional['Config'] = None
|
||||
# Value updated from config in init()
|
||||
MAX_DELETIONS = 10 # type: int
|
||||
MAX_DELETIONS: int = 10
|
||||
|
||||
UpdateMessage = Union[UpdateShortChatMessage, UpdateShortMessage, UpdateNewChannelMessage,
|
||||
UpdateNewMessage, UpdateEditMessage, UpdateEditChannelMessage]
|
||||
@@ -59,26 +59,41 @@ except ImportError:
|
||||
Histogram = None
|
||||
UPDATE_TIME = None
|
||||
|
||||
|
||||
class AbstractUser(ABC):
|
||||
session_container = None # type: AlchemySessionContainer
|
||||
loop = None # type: asyncio.AbstractEventLoop
|
||||
log = None # type: logging.Logger
|
||||
az = None # type: AppService
|
||||
bot = None # type: Bot
|
||||
ignore_incoming_bot_events = True # type: bool
|
||||
session_container: AlchemySessionContainer = None
|
||||
loop: asyncio.AbstractEventLoop = None
|
||||
log: logging.Logger
|
||||
az: AppService
|
||||
bot: 'Bot'
|
||||
ignore_incoming_bot_events: bool = True
|
||||
|
||||
client: Optional[MautrixTelegramClient]
|
||||
mxid: Optional[MatrixUserID]
|
||||
|
||||
tgid: Optional[TelegramID]
|
||||
username: Optional['str']
|
||||
is_bot: bool
|
||||
|
||||
is_relaybot: bool
|
||||
relaybot: Optional['Bot']
|
||||
|
||||
puppet_whitelisted: bool
|
||||
whitelisted: bool
|
||||
relaybot_whitelisted: bool
|
||||
matrix_puppet_whitelisted: bool
|
||||
is_admin: bool
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.is_admin = False # type: bool
|
||||
self.matrix_puppet_whitelisted = False # type: bool
|
||||
self.puppet_whitelisted = False # type: bool
|
||||
self.whitelisted = False # type: bool
|
||||
self.relaybot_whitelisted = False # type: bool
|
||||
self.client = None # type: MautrixTelegramClient
|
||||
self.tgid = None # type: TelegramID
|
||||
self.mxid = None # type: MatrixUserID
|
||||
self.is_relaybot = False # type: bool
|
||||
self.is_bot = False # type: bool
|
||||
self.relaybot = None # type: Optional[Bot]
|
||||
self.is_admin = False
|
||||
self.matrix_puppet_whitelisted = False
|
||||
self.puppet_whitelisted = False
|
||||
self.whitelisted = False
|
||||
self.relaybot_whitelisted = False
|
||||
self.client = None
|
||||
self.is_relaybot = False
|
||||
self.is_bot = False
|
||||
self.relaybot = None
|
||||
|
||||
@property
|
||||
def connected(self) -> bool:
|
||||
@@ -367,7 +382,6 @@ class AbstractUser(ABC):
|
||||
message.delete()
|
||||
number_left = DBMessage.count_spaces_by_mxid(message.mxid, message.mx_room)
|
||||
if number_left == 0:
|
||||
portal = po.Portal.get_by_mxid(message.mx_room)
|
||||
await self._try_redact(message)
|
||||
|
||||
async def delete_channel_message(self, update: UpdateDeleteChannelMessages) -> None:
|
||||
@@ -414,7 +428,7 @@ class AbstractUser(ABC):
|
||||
# endregion
|
||||
|
||||
|
||||
def init(context: "Context") -> None:
|
||||
def init(context: 'Context') -> None:
|
||||
global config, MAX_DELETIONS
|
||||
AbstractUser.az, config, AbstractUser.loop, AbstractUser.relaybot = context.core
|
||||
AbstractUser.ignore_incoming_bot_events = config["bridge.relaybot.ignore_own_incoming_events"]
|
||||
|
||||
+25
-17
@@ -35,32 +35,40 @@ from . import puppet as pu, portal as po, user as u
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .config import Config
|
||||
from .context import Context
|
||||
|
||||
config = None # type: Config
|
||||
config: Optional['Config'] = None
|
||||
|
||||
ReplyFunc = Callable[[str], Awaitable[Message]]
|
||||
|
||||
|
||||
class Bot(AbstractUser):
|
||||
log = logging.getLogger("mau.bot") # type: logging.Logger
|
||||
mxid_regex = re.compile("@.+:.+") # type: Pattern
|
||||
log: logging.Logger = logging.getLogger("mau.bot")
|
||||
mxid_regex: Pattern = re.compile("@.+:.+")
|
||||
|
||||
token: str
|
||||
chats: Dict[int, str]
|
||||
tg_whitelist: List[int]
|
||||
whitelist_group_admins: bool
|
||||
_me_info: Optional[User]
|
||||
_me_mxid: Optional[MatrixUserID]
|
||||
|
||||
def __init__(self, token: str) -> None:
|
||||
super().__init__()
|
||||
self.token = token # type: str
|
||||
self.puppet_whitelisted = True # type: bool
|
||||
self.whitelisted = True # type: bool
|
||||
self.relaybot_whitelisted = True # type: bool
|
||||
self.username = None # type: str
|
||||
self.is_relaybot = True # type: bool
|
||||
self.is_bot = True # type: bool
|
||||
self.chats = {} # type: Dict[int, str]
|
||||
self.tg_whitelist = [] # type: List[int]
|
||||
self.token = token
|
||||
self.tgid = None
|
||||
self.mxid = None
|
||||
self.puppet_whitelisted = True
|
||||
self.whitelisted = True
|
||||
self.relaybot_whitelisted = True
|
||||
self.username = None
|
||||
self.is_relaybot = True
|
||||
self.is_bot = True
|
||||
self.chats = {}
|
||||
self.tg_whitelist = []
|
||||
self.whitelist_group_admins = (config["bridge.relaybot.whitelist_group_admins"]
|
||||
or False) # type: bool
|
||||
self._me_info = None # type: Optional[User]
|
||||
self._me_mxid = None # type: Optional[MatrixUserID]
|
||||
or False)
|
||||
self._me_info = None
|
||||
self._me_mxid = None
|
||||
|
||||
async def get_me(self, use_cache: bool = True) -> Tuple[User, MatrixUserID]:
|
||||
if not use_cache or not self._me_mxid:
|
||||
@@ -91,7 +99,7 @@ class Bot(AbstractUser):
|
||||
async def post_login(self) -> None:
|
||||
await self.init_permissions()
|
||||
info = await self.client.get_me()
|
||||
self.tgid = info.id
|
||||
self.tgid = TelegramID(info.id)
|
||||
self.username = info.username
|
||||
self.mxid = pu.Puppet.get_mxid_from_id(self.tgid)
|
||||
|
||||
|
||||
+21
-13
@@ -19,13 +19,15 @@ from ruamel.yaml.comments import CommentedMap
|
||||
import random
|
||||
import string
|
||||
|
||||
yaml = YAML() # type: YAML
|
||||
yaml: YAML = YAML()
|
||||
yaml.indent(4)
|
||||
|
||||
|
||||
class DictWithRecursion:
|
||||
_data: CommentedMap
|
||||
|
||||
def __init__(self, data: Optional[CommentedMap] = None) -> None:
|
||||
self._data = data or CommentedMap() # type: CommentedMap
|
||||
self._data = data or CommentedMap()
|
||||
|
||||
@staticmethod
|
||||
def _parse_key(key: str) -> Tuple[str, Optional[str]]:
|
||||
@@ -102,14 +104,20 @@ class DictWithRecursion:
|
||||
|
||||
|
||||
class Config(DictWithRecursion):
|
||||
path: str
|
||||
registration_path: str
|
||||
base_path: str
|
||||
_registration: Optional[Dict[str, Any]]
|
||||
_overrides: Dict[str, Any]
|
||||
|
||||
def __init__(self, path: str, registration_path: str, base_path: str,
|
||||
overrides: Dict[str, Any] = None) -> None:
|
||||
super().__init__()
|
||||
self.path = path # type: str
|
||||
self.registration_path = registration_path # type: str
|
||||
self.base_path = base_path # type: str
|
||||
self._registration = None # type: Optional[Dict]
|
||||
self._overrides = overrides or {} # type: Dict[str, Any]
|
||||
self.path = path
|
||||
self.registration_path = registration_path
|
||||
self.base_path = base_path
|
||||
self._registration = None
|
||||
self._overrides = overrides or {}
|
||||
|
||||
def __getitem__(self, key: str) -> Any:
|
||||
try:
|
||||
@@ -327,10 +335,10 @@ class Config(DictWithRecursion):
|
||||
def generate_registration(self) -> None:
|
||||
homeserver = self["homeserver.domain"]
|
||||
|
||||
username_format = self.get("bridge.username_template", "telegram_{userid}") \
|
||||
.format(userid=".+")
|
||||
alias_format = self.get("bridge.alias_template", "telegram_{groupname}") \
|
||||
.format(groupname=".+")
|
||||
username_format = self.get("bridge.username_template",
|
||||
"telegram_{userid}").format(userid=".+")
|
||||
alias_format = self.get("bridge.alias_template",
|
||||
"telegram_{groupname}").format(groupname=".+")
|
||||
|
||||
self.set("appservice.as_token", self._new_token())
|
||||
self.set("appservice.hs_token", self._new_token())
|
||||
@@ -354,5 +362,5 @@ class Config(DictWithRecursion):
|
||||
"rate_limited": False
|
||||
}
|
||||
if self["appservice.community_id"]:
|
||||
self._registration["namespaces"]["users"][0]["group_id"] \
|
||||
= self["appservice.community_id"]
|
||||
self._registration["namespaces"]["users"][0]["group_id"] = self[
|
||||
"appservice.community_id"]
|
||||
|
||||
@@ -28,16 +28,25 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class Context:
|
||||
az: 'AppService'
|
||||
config: 'Config'
|
||||
loop: 'asyncio.AbstractEventLoop'
|
||||
bot: Optional['Bot']
|
||||
mx: Optional['MatrixHandler']
|
||||
session_container: 'AlchemySessionContainer'
|
||||
public_website: Optional['PublicBridgeWebsite']
|
||||
provisioning_api: Optional['ProvisioningAPI']
|
||||
|
||||
def __init__(self, az: 'AppService', config: 'Config', loop: 'asyncio.AbstractEventLoop',
|
||||
session_container: 'AlchemySessionContainer', bot: Optional['Bot']) -> None:
|
||||
self.az = az # type: AppService
|
||||
self.config = config # type: Config
|
||||
self.loop = loop # type: asyncio.AbstractEventLoop
|
||||
self.bot = bot # type: Optional[Bot]
|
||||
self.mx = None # type: Optional[MatrixHandler]
|
||||
self.session_container = session_container # type: AlchemySessionContainer
|
||||
self.public_website = None # type: Optional[PublicBridgeWebsite]
|
||||
self.provisioning_api = None # type: Optional[ProvisioningAPI]
|
||||
self.az = az
|
||||
self.config = config
|
||||
self.loop = loop
|
||||
self.bot = bot
|
||||
self.mx = None
|
||||
self.session_container = session_container
|
||||
self.public_website = None
|
||||
self.provisioning_api = None
|
||||
|
||||
@property
|
||||
def core(self) -> Tuple['AppService', 'Config', 'asyncio.AbstractEventLoop', Optional['Bot']]:
|
||||
|
||||
+34
-24
@@ -26,6 +26,9 @@ from . import user as u, portal as po, puppet as pu, commands as com
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .context import Context
|
||||
from .config import Config
|
||||
from .bot import Bot
|
||||
from mautrix_appservice import AppService
|
||||
|
||||
try:
|
||||
from prometheus_client import Histogram
|
||||
@@ -38,12 +41,17 @@ except ImportError:
|
||||
|
||||
|
||||
class MatrixHandler:
|
||||
log = logging.getLogger("mau.mx") # type: logging.Logger
|
||||
log: logging.Logger = logging.getLogger("mau.mx")
|
||||
az: 'AppService'
|
||||
config: 'Config'
|
||||
bot: 'Bot'
|
||||
commands: 'com.CommandProcessor'
|
||||
previously_typing: Dict[MatrixRoomID, Set[MatrixUserID]]
|
||||
|
||||
def __init__(self, context: 'Context') -> None:
|
||||
self.az, self.config, _, self.tgbot = context.core
|
||||
self.commands = com.CommandProcessor(context) # type: com.CommandProcessor
|
||||
self.previously_typing = [] # type: List[MatrixUserID]
|
||||
self.commands = com.CommandProcessor(context)
|
||||
self.previously_typing = {}
|
||||
|
||||
self.az.matrix_event_handler(self.handle_event)
|
||||
|
||||
@@ -372,14 +380,16 @@ class MatrixHandler:
|
||||
return
|
||||
await user.set_presence(presence == "online")
|
||||
|
||||
async def handle_typing(self, room_id: MatrixRoomID, now_typing: List[MatrixUserID]) -> None:
|
||||
async def handle_typing(self, room_id: MatrixRoomID, now_typing: Set[MatrixUserID]) -> None:
|
||||
portal = po.Portal.get_by_mxid(room_id)
|
||||
if not portal:
|
||||
return
|
||||
|
||||
for user_id in set(self.previously_typing + now_typing):
|
||||
previously_typing = self.previously_typing.get(room_id, set())
|
||||
|
||||
for user_id in set(previously_typing | now_typing):
|
||||
is_typing = user_id in now_typing
|
||||
was_typing = user_id in self.previously_typing
|
||||
was_typing = user_id in previously_typing
|
||||
if is_typing and was_typing:
|
||||
continue
|
||||
|
||||
@@ -389,7 +399,7 @@ class MatrixHandler:
|
||||
|
||||
await portal.set_typing(user, is_typing)
|
||||
|
||||
self.previously_typing = now_typing
|
||||
self.previously_typing[room_id] = now_typing
|
||||
|
||||
def filter_matrix_event(self, event: MatrixEvent) -> bool:
|
||||
sender = event.get("sender", None)
|
||||
@@ -405,38 +415,38 @@ class MatrixHandler:
|
||||
self.log.exception("Error handling manually received Matrix event")
|
||||
|
||||
async def handle_ephemeral_event(self, evt: MatrixEvent) -> None:
|
||||
evt_type = evt.get("type", "m.unknown") # type: str
|
||||
room_id = evt.get("room_id", None) # type: Optional[MatrixRoomID]
|
||||
sender = evt.get("sender", None) # type: Optional[MatrixUserID]
|
||||
content = evt.get("content", {}) # type: Dict
|
||||
evt_type: str = evt.get("type", "m.unknown")
|
||||
room_id: Optional[MatrixRoomID] = evt.get("room_id", None)
|
||||
sender: Optional[MatrixUserID] = evt.get("sender", None)
|
||||
content: Dict = evt.get("content", {})
|
||||
if evt_type == "m.receipt":
|
||||
await self.handle_read_receipts(room_id, self.parse_read_receipts(content))
|
||||
elif evt_type == "m.presence":
|
||||
await self.handle_presence(sender, content.get("presence", "offline"))
|
||||
elif evt_type == "m.typing":
|
||||
await self.handle_typing(room_id, content.get("user_ids", []))
|
||||
await self.handle_typing(room_id, set(content.get("user_ids", [])))
|
||||
|
||||
async def handle_event(self, evt: MatrixEvent) -> None:
|
||||
if self.filter_matrix_event(evt):
|
||||
return
|
||||
start_time = time.time()
|
||||
self.log.debug("Received event: %s", evt)
|
||||
evt_type = evt.get("type", "m.unknown") # type: str
|
||||
room_id = evt.get("room_id", None) # type: Optional[MatrixRoomID]
|
||||
event_id = evt.get("event_id", None) # type: Optional[MatrixEventID]
|
||||
sender = evt.get("sender", None) # type: Optional[MatrixUserID]
|
||||
evt_type: str = evt.get("type", "m.unknown")
|
||||
room_id: Optional[MatrixRoomID] = evt.get("room_id", None)
|
||||
event_id: Optional[MatrixEventID] = evt.get("event_id", None)
|
||||
sender: Optional[MatrixUserID] = evt.get("sender", None)
|
||||
state_key = evt.get("state_key", None)
|
||||
content = evt.get("content", {}) # type: Dict
|
||||
content: Dict = evt.get("content", {})
|
||||
if state_key is not None:
|
||||
if evt_type == "m.room.member":
|
||||
prev_content = evt.get("unsigned", {}).get("prev_content", {}) # type: Dict
|
||||
membership = content.get("membership", "") # type: str
|
||||
prev_membership = prev_content.get("membership", "leave") # type: str
|
||||
prev_content: Dict = evt.get("unsigned", {}).get("prev_content", {})
|
||||
membership: str = content.get("membership", "")
|
||||
prev_membership: str = prev_content.get("membership", "leave")
|
||||
if membership == prev_membership:
|
||||
match = re.compile("@(.+):(.+)").match(state_key) # type: Match
|
||||
mxid = match.group(0) # type: str
|
||||
displayname = content.get("displayname", None) or mxid # type: str
|
||||
prev_displayname = prev_content.get("displayname", None) or mxid # type: str
|
||||
match: Match = re.compile("@(.+):(.+)").match(state_key)
|
||||
mxid: str = match.group(0)
|
||||
displayname: str = content.get("displayname", None) or mxid
|
||||
prev_displayname: str = prev_content.get("displayname", None) or mxid
|
||||
if displayname != prev_displayname:
|
||||
await self.handle_name_change(room_id, state_key, displayname,
|
||||
prev_displayname, event_id)
|
||||
|
||||
@@ -23,10 +23,13 @@ from .db import RoomState, UserProfile
|
||||
|
||||
|
||||
class SQLStateStore(StateStore):
|
||||
profile_cache: Dict[Tuple[str, str], UserProfile]
|
||||
room_state_cache: Dict[str, RoomState]
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.profile_cache = {} # type: Dict[Tuple[str, str], UserProfile]
|
||||
self.room_state_cache = {} # type: Dict[str, RoomState]
|
||||
self.profile_cache = {}
|
||||
self.room_state_cache = {}
|
||||
|
||||
@staticmethod
|
||||
def is_registered(user: MatrixUserID) -> bool:
|
||||
|
||||
@@ -21,9 +21,12 @@ from telethon.tl.types import (
|
||||
InputMediaUploadedDocument, InputMediaUploadedPhoto, TypeDocumentAttribute, TypeInputMedia,
|
||||
TypeInputPeer, TypeMessageEntity, TypeMessageMedia, TypePeer)
|
||||
from telethon.tl.patched import Message
|
||||
from telethon.sessions.abstract import Session
|
||||
|
||||
|
||||
class MautrixTelegramClient(TelegramClient):
|
||||
session: Session
|
||||
|
||||
async def upload_file_direct(self, file: bytes, mime_type: str = None,
|
||||
attributes: List[TypeDocumentAttribute] = None,
|
||||
file_name: str = None, max_image_size: float = 10 * 1000 ** 2,
|
||||
|
||||
+26
-17
@@ -13,7 +13,8 @@
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from typing import Awaitable, Dict, List, Iterable, Match, NewType, Optional, Tuple, TYPE_CHECKING
|
||||
from typing import (Awaitable, Dict, List, Iterable, Match, NewType, Optional, Tuple, Any,
|
||||
TYPE_CHECKING)
|
||||
import logging
|
||||
import asyncio
|
||||
import re
|
||||
@@ -35,15 +36,23 @@ if TYPE_CHECKING:
|
||||
from .config import Config
|
||||
from .context import Context
|
||||
|
||||
config = None # type: Config
|
||||
config: Optional['Config'] = None
|
||||
|
||||
SearchResult = NewType('SearchResult', Tuple['pu.Puppet', int])
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
log = logging.getLogger("mau.user") # type: logging.Logger
|
||||
by_mxid = {} # type: Dict[str, User]
|
||||
by_tgid = {} # type: Dict[int, User]
|
||||
log: logging.Logger = logging.getLogger("mau.user")
|
||||
by_mxid: Dict[str, 'User'] = {}
|
||||
by_tgid: Dict[int, 'User'] = {}
|
||||
|
||||
phone: Optional[str]
|
||||
contacts: List['pu.Puppet']
|
||||
saved_contacts: int
|
||||
portals: Dict[Tuple[TelegramID, TelegramID], 'po.Portal']
|
||||
command_status: Optional[Dict[str, Any]]
|
||||
|
||||
_db_instance: Optional[DBUser]
|
||||
|
||||
def __init__(self, mxid: MatrixUserID, tgid: Optional[TelegramID] = None,
|
||||
username: Optional[str] = None, phone: Optional[str] = None,
|
||||
@@ -52,19 +61,19 @@ class User(AbstractUser):
|
||||
db_portals: Optional[Iterable[Tuple[TelegramID, TelegramID]]] = None,
|
||||
db_instance: Optional[DBUser] = None) -> None:
|
||||
super().__init__()
|
||||
self.mxid = mxid # type: MatrixUserID
|
||||
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.mxid = mxid
|
||||
self.tgid = tgid
|
||||
self.is_bot = is_bot
|
||||
self.username = username
|
||||
self.phone = phone
|
||||
self.contacts = []
|
||||
self.saved_contacts = saved_contacts
|
||||
self.db_contacts = db_contacts
|
||||
self.portals = {} # type: Dict[Tuple[TelegramID, TelegramID], po.Portal]
|
||||
self.portals = {}
|
||||
self.db_portals = db_portals or []
|
||||
self._db_instance = db_instance # type: Optional[DBUser]
|
||||
self._db_instance = db_instance
|
||||
|
||||
self.command_status = None # type: Optional[Dict]
|
||||
self.command_status = None
|
||||
|
||||
(self.relaybot_whitelisted,
|
||||
self.whitelisted,
|
||||
@@ -83,7 +92,7 @@ class User(AbstractUser):
|
||||
|
||||
@property
|
||||
def mxid_localpart(self) -> str:
|
||||
match = re.compile("@(.+):(.+)").match(self.mxid) # type: Match
|
||||
match: Match = re.compile("@(.+):(.+)").match(self.mxid)
|
||||
return match.group(1)
|
||||
|
||||
@property
|
||||
@@ -228,7 +237,7 @@ class User(AbstractUser):
|
||||
self.phone = info.phone
|
||||
changed = True
|
||||
if self.tgid != info.id:
|
||||
self.tgid = info.id
|
||||
self.tgid = TelegramID(info.id)
|
||||
self.by_tgid[self.tgid] = self
|
||||
if changed:
|
||||
self.save()
|
||||
|
||||
Reference in New Issue
Block a user