"""Telegram requests through the client's own session. Sent with ConnectionsManager.sendRequest and a Java RequestDelegate proxy, kept strongly referenced until it fires: on 12.10.6 the SDK's send_request never invoked the callback for these requests. """ import threading import diag from jutil import java_list _pending = set() _lock = threading.Lock() _Delegate = None def _delegate_class(): """A Java RequestDelegate calling a Python function (created once).""" global _Delegate if _Delegate is None: from java import dynamic_proxy from org.telegram.tgnet import RequestDelegate class Delegate(dynamic_proxy(RequestDelegate)): def __init__(self, fn): super().__init__() self.fn = fn def run(self, response, error): self.fn(response, error) _Delegate = Delegate return _Delegate def _send(account, request, callback): from org.telegram.tgnet import ConnectionsManager box = [] def done(response, error): with _lock: for proxy in box: _pending.discard(proxy) try: callback(response, error) except Exception as exc: diag.error("tg callback", exc) delegate = _delegate_class()(done) box.append(delegate) with _lock: _pending.add(delegate) ConnectionsManager.getInstance(account).sendRequest(request, delegate) def _message_ids(ids): """getMessages ids: ArrayList. `jint` forces Integer boxing; a plain Python int (even from Integer.valueOf) is boxed as Long, which fails serialization on the network thread without any callback.""" from java import jint from java.util import ArrayList items = ArrayList() for i in ids: items.add(jint(int(i))) return items def channel_chat(account, channel_id): """The cached TLRPC.Chat of a channel, loading it from storage if needed.""" from org.telegram.messenger import MessagesController, MessagesStorage controller = MessagesController.getInstance(account) chat = controller.getChat(int(channel_id)) if chat is None: try: chat = MessagesStorage.getInstance(account).getChatSync(int(channel_id)) if chat is not None: controller.putChat(chat, True) except Exception as exc: diag.error("tg.channel_chat", exc) return chat def get_messages(account, dialog_id, ids, callback): """Fetches `ids` of a dialog (a channel when `dialog_id` is one). `callback(messages, error_text)`: messages by id (TL_messageEmpty for deleted ones), or None and an error. """ from org.telegram.messenger import ChatObject, MessagesController from org.telegram.tgnet import TLRPC controller = MessagesController.getInstance(account) dialog_id = int(dialog_id) chat = channel_chat(account, -dialog_id) if dialog_id < 0 else None if chat is not None and ChatObject.isChannel(chat): request = TLRPC.TL_channels_getMessages() request.channel = controller.getInputChannel(chat) if request.channel is None or getattr(request.channel, "access_hash", 0) == 0: callback(None, "not a member of that chat") return elif dialog_id < 0 and chat is None: callback(None, "chat not found (not a member?)") return else: request = TLRPC.TL_messages_getMessages() request.id = _message_ids(ids) def done(response, error): if error is not None or response is None: callback(None, getattr(error, "text", None) or "request failed") return try: controller.putUsers(response.users, False) controller.putChats(response.chats, False) except Exception: pass found = {} for message in java_list(response.messages): found[int(message.id)] = message callback(found, None) _send(account, request, done)