Refactor and add region comments
This commit is contained in:
@@ -152,6 +152,8 @@ class IntentAPI:
|
|||||||
else:
|
else:
|
||||||
raise ValueError("IntentAPI#user() is only available for base intent objects.")
|
raise ValueError("IntentAPI#user() is only available for base intent objects.")
|
||||||
|
|
||||||
|
# region User actions
|
||||||
|
|
||||||
def set_display_name(self, name):
|
def set_display_name(self, name):
|
||||||
self._ensure_registered()
|
self._ensure_registered()
|
||||||
return self.client.set_display_name(self.mxid, name)
|
return self.client.set_display_name(self.mxid, name)
|
||||||
@@ -165,9 +167,8 @@ class IntentAPI:
|
|||||||
mime_type = mime_type or magic.from_buffer(photo_data, mime=True)
|
mime_type = mime_type or magic.from_buffer(photo_data, mime=True)
|
||||||
return self.client.media_upload(photo_data, mime_type)
|
return self.client.media_upload(photo_data, mime_type)
|
||||||
|
|
||||||
def set_typing(self, room_id, is_typing=True, timeout=5000):
|
# endregion
|
||||||
self._ensure_joined(room_id)
|
# region Room actions
|
||||||
return self.client.set_typing(room_id, is_typing, timeout)
|
|
||||||
|
|
||||||
def create_room(self, alias=None, is_public=False, name=None, topic=None, is_direct=False,
|
def create_room(self, alias=None, is_public=False, name=None, topic=None, is_direct=False,
|
||||||
invitees=()):
|
invitees=()):
|
||||||
@@ -180,8 +181,6 @@ class IntentAPI:
|
|||||||
}
|
}
|
||||||
if info:
|
if info:
|
||||||
content["info"] = info
|
content["info"] = info
|
||||||
self._ensure_joined(room_id)
|
|
||||||
self._ensure_has_power_level_for(room_id, "m.room.avatar")
|
|
||||||
return self.send_state_event(room_id, "m.room.avatar", content)
|
return self.send_state_event(room_id, "m.room.avatar", content)
|
||||||
|
|
||||||
def set_room_name(self, room_id, name):
|
def set_room_name(self, room_id, name):
|
||||||
@@ -189,6 +188,10 @@ class IntentAPI:
|
|||||||
self._ensure_has_power_level_for(room_id, "m.room.name")
|
self._ensure_has_power_level_for(room_id, "m.room.name")
|
||||||
return self.client.set_room_name(room_id, name)
|
return self.client.set_room_name(room_id, name)
|
||||||
|
|
||||||
|
def set_typing(self, room_id, is_typing=True, timeout=5000):
|
||||||
|
self._ensure_joined(room_id)
|
||||||
|
return self.client.set_typing(room_id, is_typing, timeout)
|
||||||
|
|
||||||
def send_text(self, room_id, text, html=None, notice=False):
|
def send_text(self, room_id, text, html=None, notice=False):
|
||||||
if html:
|
if html:
|
||||||
return self.send_message(room_id, {
|
return self.send_message(room_id, {
|
||||||
@@ -227,6 +230,9 @@ class IntentAPI:
|
|||||||
return [membership["state_key"] for membership in memberships["chunk"] if
|
return [membership["state_key"] for membership in memberships["chunk"] if
|
||||||
membership["content"]["membership"] == "join"]
|
membership["content"]["membership"] == "join"]
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Ensure functions
|
||||||
|
|
||||||
def _ensure_joined(self, room_id, ignore_cache=False):
|
def _ensure_joined(self, room_id, ignore_cache=False):
|
||||||
if not ignore_cache and self.memberships.get(room_id, "") == "join":
|
if not ignore_cache and self.memberships.get(room_id, "") == "join":
|
||||||
return
|
return
|
||||||
@@ -255,4 +261,7 @@ class IntentAPI:
|
|||||||
self.registered = True
|
self.registered = True
|
||||||
|
|
||||||
def _ensure_has_power_level_for(self, room_id, event_type):
|
def _ensure_has_power_level_for(self, room_id, event_type):
|
||||||
|
# TODO implement
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ from .db import Message as DBMessage
|
|||||||
log = None
|
log = None
|
||||||
|
|
||||||
|
|
||||||
|
# region Matrix to Telegram
|
||||||
|
|
||||||
class MessageEntityReply(MessageEntityUnknown):
|
class MessageEntityReply(MessageEntityUnknown):
|
||||||
def __init__(self, offset=0, length=0, msg_id=0):
|
def __init__(self, offset=0, length=0, msg_id=0):
|
||||||
super().__init__(offset, length)
|
super().__init__(offset, length)
|
||||||
@@ -186,6 +188,9 @@ def matrix_to_telegram(html, user_id=None):
|
|||||||
log.exception("Failed to convert Matrix format:\nhtml=%s", html)
|
log.exception("Failed to convert Matrix format:\nhtml=%s", html)
|
||||||
|
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Telegram to Matrix
|
||||||
|
|
||||||
def telegram_event_to_matrix(evt, source):
|
def telegram_event_to_matrix(evt, source):
|
||||||
text = evt.message
|
text = evt.message
|
||||||
html = telegram_to_matrix(evt.message, evt.entities) if evt.entities else None
|
html = telegram_to_matrix(evt.message, evt.entities) if evt.entities else None
|
||||||
@@ -301,6 +306,8 @@ def _telegram_to_matrix(text, entities):
|
|||||||
return "".join(html)
|
return "".join(html)
|
||||||
|
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
def init(context):
|
def init(context):
|
||||||
global log
|
global log
|
||||||
_, _, parent_log, _ = context
|
_, _, parent_log, _ = context
|
||||||
|
|||||||
+56
-33
@@ -40,11 +40,26 @@ class Portal:
|
|||||||
if mxid:
|
if mxid:
|
||||||
self.by_mxid[mxid] = self
|
self.by_mxid[mxid] = self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def peer(self):
|
||||||
|
if self.peer_type == "user":
|
||||||
|
return PeerUser(user_id=self.tgid)
|
||||||
|
elif self.peer_type == "chat":
|
||||||
|
return PeerChat(chat_id=self.tgid)
|
||||||
|
elif self.peer_type == "channel":
|
||||||
|
return PeerChannel(channel_id=self.tgid)
|
||||||
|
|
||||||
|
# region Matrix room info updating
|
||||||
|
|
||||||
def get_main_intent(self):
|
def get_main_intent(self):
|
||||||
direct = self.peer_type == "user"
|
direct = self.peer_type == "user"
|
||||||
puppet = p.Puppet.get(self.tgid) if direct else None
|
puppet = p.Puppet.get(self.tgid) if direct else None
|
||||||
return puppet.intent if direct else self.az.intent
|
return puppet.intent if direct else self.az.intent
|
||||||
|
|
||||||
|
def invite_matrix(self, users=[]):
|
||||||
|
# TODO implement
|
||||||
|
pass
|
||||||
|
|
||||||
def create_room(self, user, entity=None, invites=[], update_if_exists=True):
|
def create_room(self, user, entity=None, invites=[], update_if_exists=True):
|
||||||
self.log.debug("Creating room for %d", self.tgid)
|
self.log.debug("Creating room for %d", self.tgid)
|
||||||
if not entity:
|
if not entity:
|
||||||
@@ -117,6 +132,20 @@ class Portal:
|
|||||||
if changed:
|
if changed:
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
def get_users(self, user, entity):
|
||||||
|
if self.peer_type == "chat":
|
||||||
|
return user.client(GetFullChatRequest(chat_id=self.tgid)).users
|
||||||
|
elif self.peer_type == "channel":
|
||||||
|
participants = user.client(GetParticipantsRequest(
|
||||||
|
entity, ChannelParticipantsRecent(), offset=0, limit=100, hash=0
|
||||||
|
))
|
||||||
|
return participants.users
|
||||||
|
elif self.peer_type == "user":
|
||||||
|
return [entity]
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Matrix event handling
|
||||||
|
|
||||||
def handle_matrix_message(self, sender, message, event_id):
|
def handle_matrix_message(self, sender, message, event_id):
|
||||||
type = message["msgtype"]
|
type = message["msgtype"]
|
||||||
if type == "m.text":
|
if type == "m.text":
|
||||||
@@ -136,10 +165,16 @@ class Portal:
|
|||||||
DBMessage(tgid=response.id, mx_room=self.mxid, mxid=event_id, user=sender.tgid))
|
DBMessage(tgid=response.id, mx_room=self.mxid, mxid=event_id, user=sender.tgid))
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Telegram event handling
|
||||||
|
|
||||||
def handle_telegram_typing(self, user, event):
|
def handle_telegram_typing(self, user, event):
|
||||||
user.intent.set_typing(self.mxid, is_typing=True)
|
user.intent.set_typing(self.mxid, is_typing=True)
|
||||||
|
|
||||||
def handle_telegram_message(self, source, sender, evt):
|
def handle_telegram_message(self, source, sender, evt):
|
||||||
|
if not self.mxid:
|
||||||
|
self.create_room(self, invites=[source.mxid])
|
||||||
|
|
||||||
self.log.debug("Sending %s to %s by %d", evt.message, self.mxid, sender.id)
|
self.log.debug("Sending %s to %s by %d", evt.message, self.mxid, sender.id)
|
||||||
if evt.message:
|
if evt.message:
|
||||||
text, html = formatter.telegram_event_to_matrix(evt, source)
|
text, html = formatter.telegram_event_to_matrix(evt, source)
|
||||||
@@ -148,6 +183,20 @@ class Portal:
|
|||||||
user=source.tgid))
|
user=source.tgid))
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
|
def handle_telegram_action(self, source, sender, action):
|
||||||
|
if not self.mxid:
|
||||||
|
return
|
||||||
|
|
||||||
|
intent = self.get_main_intent()
|
||||||
|
action_type = type(action)
|
||||||
|
if action_type == MessageActionChatEditTitle:
|
||||||
|
if self.update_title(action.title, intent):
|
||||||
|
self.save()
|
||||||
|
elif action_type == MessageActionChatEditPhoto:
|
||||||
|
largest_size = max(action.photo.sizes, key=lambda photo: photo.size)
|
||||||
|
if self.update_avatar(source, largest_size.location, intent):
|
||||||
|
self.save()
|
||||||
|
|
||||||
def update_title(self, title, intent=None):
|
def update_title(self, title, intent=None):
|
||||||
if self.title != title:
|
if self.title != title:
|
||||||
self.title = title
|
self.title = title
|
||||||
@@ -175,39 +224,8 @@ class Portal:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def handle_telegram_action(self, source, sender, action):
|
# endregion
|
||||||
intent = self.get_main_intent()
|
# region Database conversion
|
||||||
action_type = type(action)
|
|
||||||
if action_type == MessageActionChatEditTitle:
|
|
||||||
if self.update_title(action.title, intent):
|
|
||||||
self.save()
|
|
||||||
elif action_type == MessageActionChatEditPhoto:
|
|
||||||
largest_size = max(action.photo.sizes, key=lambda photo: photo.size)
|
|
||||||
if self.update_avatar(source, largest_size.location, intent):
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def peer(self):
|
|
||||||
if self.peer_type == "user":
|
|
||||||
return PeerUser(user_id=self.tgid)
|
|
||||||
elif self.peer_type == "chat":
|
|
||||||
return PeerChat(chat_id=self.tgid)
|
|
||||||
elif self.peer_type == "channel":
|
|
||||||
return PeerChannel(channel_id=self.tgid)
|
|
||||||
|
|
||||||
def get_users(self, user, entity):
|
|
||||||
if self.peer_type == "chat":
|
|
||||||
return user.client(GetFullChatRequest(chat_id=self.tgid)).users
|
|
||||||
elif self.peer_type == "channel":
|
|
||||||
participants = user.client(GetParticipantsRequest(
|
|
||||||
entity, ChannelParticipantsRecent(), offset=0, limit=100, hash=0
|
|
||||||
))
|
|
||||||
return participants.users
|
|
||||||
elif self.peer_type == "user":
|
|
||||||
return [entity]
|
|
||||||
|
|
||||||
def invite_matrix(self, users=[]):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def to_db(self):
|
def to_db(self):
|
||||||
return self.db.merge(DBPortal(tgid=self.tgid, peer_type=self.peer_type, mxid=self.mxid,
|
return self.db.merge(DBPortal(tgid=self.tgid, peer_type=self.peer_type, mxid=self.mxid,
|
||||||
@@ -223,6 +241,9 @@ class Portal:
|
|||||||
return Portal(db_portal.tgid, db_portal.peer_type, db_portal.mxid, db_portal.username,
|
return Portal(db_portal.tgid, db_portal.peer_type, db_portal.mxid, db_portal.username,
|
||||||
db_portal.title, db_portal.photo_id)
|
db_portal.title, db_portal.photo_id)
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Class instance lookup
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_mxid(cls, mxid):
|
def get_by_mxid(cls, mxid):
|
||||||
try:
|
try:
|
||||||
@@ -280,6 +301,8 @@ class Portal:
|
|||||||
raise ValueError(f"Unknown entity type {entity_type.__name__}")
|
raise ValueError(f"Unknown entity type {entity_type.__name__}")
|
||||||
return cls.get_by_tgid(id, type_name)
|
return cls.get_by_tgid(id, type_name)
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
def init(context):
|
def init(context):
|
||||||
global config
|
global config
|
||||||
|
|||||||
+58
-26
@@ -44,6 +44,8 @@ class User:
|
|||||||
def logged_in(self):
|
def logged_in(self):
|
||||||
return self.client.is_user_authorized()
|
return self.client.is_user_authorized()
|
||||||
|
|
||||||
|
# region Database conversion
|
||||||
|
|
||||||
def to_db(self):
|
def to_db(self):
|
||||||
return self.db.merge(DBUser(mxid=self.mxid, tgid=self.tgid, tg_username=self.username))
|
return self.db.merge(DBUser(mxid=self.mxid, tgid=self.tgid, tg_username=self.username))
|
||||||
|
|
||||||
@@ -55,6 +57,9 @@ class User:
|
|||||||
def from_db(cls, db_user):
|
def from_db(cls, db_user):
|
||||||
return User(db_user.mxid, db_user.tgid, db_user.tg_username)
|
return User(db_user.mxid, db_user.tgid, db_user.tg_username)
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Telegram connection management
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.client = TelegramClient(self.mxid,
|
self.client = TelegramClient(self.mxid,
|
||||||
config["telegram.api_id"],
|
config["telegram.api_id"],
|
||||||
@@ -67,6 +72,14 @@ class User:
|
|||||||
self.client.add_update_handler(self.update_catch)
|
self.client.add_update_handler(self.update_catch)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.client.disconnect()
|
||||||
|
self.client = None
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Telegram actions that need custom methods
|
||||||
|
|
||||||
def update_info(self, info=None):
|
def update_info(self, info=None):
|
||||||
info = info or self.client.get_me()
|
info = info or self.client.get_me()
|
||||||
self.username = info.username
|
self.username = info.username
|
||||||
@@ -84,11 +97,6 @@ class User:
|
|||||||
pass
|
pass
|
||||||
return self.client.log_out()
|
return self.client.log_out()
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.client.disconnect()
|
|
||||||
self.client = None
|
|
||||||
self.connected = False
|
|
||||||
|
|
||||||
def send_message(self, entity, message, reply_to=None, entities=None, link_preview=True):
|
def send_message(self, entity, message, reply_to=None, entities=None, link_preview=True):
|
||||||
entity = self.client.get_input_entity(entity)
|
entity = self.client.get_input_entity(entity)
|
||||||
|
|
||||||
@@ -124,6 +132,9 @@ class User:
|
|||||||
portal = po.Portal.get_by_entity(entity)
|
portal = po.Portal.get_by_entity(entity)
|
||||||
portal.create_room(self, entity, invites=[self.mxid])
|
portal.create_room(self, entity, invites=[self.mxid])
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Telegram update handling
|
||||||
|
|
||||||
def update_catch(self, update):
|
def update_catch(self, update):
|
||||||
try:
|
try:
|
||||||
self.update(update)
|
self.update(update)
|
||||||
@@ -132,36 +143,52 @@ class User:
|
|||||||
|
|
||||||
def update(self, update):
|
def update(self, update):
|
||||||
update_type = type(update)
|
update_type = type(update)
|
||||||
|
|
||||||
|
if update_type in {UpdateShortChatMessage, UpdateShortMessage, UpdateNewMessage,
|
||||||
|
UpdateNewChannelMessage}:
|
||||||
|
return self.update_message(update)
|
||||||
|
elif update_type in {UpdateChatUserTyping, UpdateUserTyping}:
|
||||||
|
return self.update_typing(update)
|
||||||
|
elif update_type == UpdateUserStatus:
|
||||||
|
return self.update_status(update)
|
||||||
|
else:
|
||||||
|
self.log.debug("Unhandled update: %s", update)
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_message_details(self, update):
|
||||||
|
update_type = type(update)
|
||||||
if update_type == UpdateShortChatMessage:
|
if update_type == UpdateShortChatMessage:
|
||||||
portal = po.Portal.get_by_tgid(update.chat_id, "chat")
|
portal = po.Portal.get_by_tgid(update.chat_id, "chat")
|
||||||
sender = pu.Puppet.get(update.from_id)
|
sender = pu.Puppet.get(update.from_id)
|
||||||
elif update_type == UpdateShortMessage:
|
elif update_type == UpdateShortMessage:
|
||||||
portal = po.Portal.get_by_tgid(update.user_id, "user")
|
portal = po.Portal.get_by_tgid(update.user_id, "user")
|
||||||
sender = pu.Puppet.get(self.tgid if update.out else update.user_id)
|
sender = pu.Puppet.get(self.tgid if update.out else update.user_id)
|
||||||
elif update_type == UpdateChatUserTyping or update_type == UpdateUserTyping:
|
elif update_type in {UpdateNewMessage, UpdateNewChannelMessage}:
|
||||||
if update_type == UpdateUserTyping:
|
|
||||||
portal = po.Portal.get_by_tgid(update.user_id, "user")
|
|
||||||
else:
|
|
||||||
portal = po.Portal.get_by_tgid(update.chat_id, "chat")
|
|
||||||
sender = pu.Puppet.get(update.user_id)
|
|
||||||
return portal.handle_telegram_typing(sender, update)
|
|
||||||
elif update_type == UpdateUserStatus:
|
|
||||||
puppet = pu.Puppet.get(update.user_id)
|
|
||||||
if isinstance(update.status, UserStatusOnline):
|
|
||||||
puppet.intent.set_presence("online")
|
|
||||||
elif isinstance(update.status, UserStatusOffline):
|
|
||||||
puppet.intent.set_presence("offline")
|
|
||||||
return
|
|
||||||
elif update_type == UpdateNewMessage or update_type == UpdateNewChannelMessage:
|
|
||||||
update = update.message
|
update = update.message
|
||||||
sender = pu.Puppet.get(update.from_id)
|
sender = pu.Puppet.get(update.from_id)
|
||||||
portal = po.Portal.get_by_entity(update.to_id)
|
portal = po.Portal.get_by_entity(update.to_id)
|
||||||
else:
|
return update, sender, portal
|
||||||
self.log.debug("Unhandled update: %s", update)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not portal.mxid:
|
def update_typing(self, update):
|
||||||
portal.create_room(self, invites=[self.mxid])
|
update_type = type(update)
|
||||||
|
if update_type == UpdateUserTyping:
|
||||||
|
portal = po.Portal.get_by_tgid(update.user_id, "user")
|
||||||
|
else:
|
||||||
|
portal = po.Portal.get_by_tgid(update.chat_id, "chat")
|
||||||
|
sender = pu.Puppet.get(update.user_id)
|
||||||
|
return portal.handle_telegram_typing(sender, update)
|
||||||
|
|
||||||
|
def update_status(self, update):
|
||||||
|
puppet = pu.Puppet.get(update.user_id)
|
||||||
|
status = type(update.status)
|
||||||
|
if status == UserStatusOnline:
|
||||||
|
puppet.intent.set_presence("online")
|
||||||
|
elif status == UserStatusOffline:
|
||||||
|
puppet.intent.set_presence("offline")
|
||||||
|
return
|
||||||
|
|
||||||
|
def update_message(self, update):
|
||||||
|
update, sender, portal = self.get_message_details(update)
|
||||||
|
|
||||||
if isinstance(update, MessageService):
|
if isinstance(update, MessageService):
|
||||||
self.log.debug("Handling action portal=%s sender=%s action=%s", portal, sender,
|
self.log.debug("Handling action portal=%s sender=%s action=%s", portal, sender,
|
||||||
@@ -169,9 +196,12 @@ class User:
|
|||||||
portal.handle_telegram_action(self, sender, update.action)
|
portal.handle_telegram_action(self, sender, update.action)
|
||||||
else:
|
else:
|
||||||
self.log.debug("Handling message portal=%s sender=%s update=%s", portal, sender,
|
self.log.debug("Handling message portal=%s sender=%s update=%s", portal, sender,
|
||||||
update)
|
update)
|
||||||
portal.handle_telegram_message(self, sender, update)
|
portal.handle_telegram_message(self, sender, update)
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
# region Class instance lookup
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_mxid(cls, mxid, create=True):
|
def get_by_mxid(cls, mxid, create=True):
|
||||||
try:
|
try:
|
||||||
@@ -215,6 +245,8 @@ class User:
|
|||||||
return cls.from_db(puppet)
|
return cls.from_db(puppet)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
def init(context):
|
def init(context):
|
||||||
global config
|
global config
|
||||||
|
|||||||
Reference in New Issue
Block a user