Получить имя пользователя из локальной сессии Telethon

Я использую библиотеку телетонов для сканирования некоторых телеграммных каналов. Во время сканирования мне нужно разрешить много ссылок на соединения, имена пользователей и идентификаторы каналов. Чтобы решить эти вопросы, я использовал метод client.get_entity() но через некоторое время серверы telegram забанили мой сканер за разрешение слишком большого числа имен пользователей. Я искал вокруг и нашел из этой проблемы, я должен использовать get_input_entity() вместо get_entity(), На самом деле Telethon сохраняет сущности внутри локального файла SQLite и всякий раз, когда вызов get_input_entity() После этого сначала выполняется поиск в локальной базе данных SQLite, если совпадение не найдено, а затем отправляется запрос на серверы Telegram. Пока все хорошо, но у меня есть две проблемы с этим подходом:

  1. get_input_entity() просто возвращает два атрибута: ID и хеш, но есть другие столбцы, такие как имя пользователя, телефон и имя в базе данных SQLite. Мне нужен метод, чтобы не просто вернуть идентификатор и хэш, но и другие столбцы.
  2. Мне нужно контролировать количество запросов на разрешение, отправленных на сервер telegram, но get_input_entity() отправляет запрос на серверы телеграмм, когда не находит совпадений в локальной базе данных. Проблема в том, что я не могу контролировать этот метод, когда запрашивать серверы телеграмм. На самом деле мне нужен логический аргумент для этого метода, указывающий, должен ли метод отправлять запрос серверам телеграмм, если не найдено совпадение в локальной базе данных.

Я прочитал некоторые из исходных кодов Telethon, в основном get_input_entity() и написал свою собственную версию get_input_entity():

def my_own_get_input_entity(self, target, with_info: bool = False):
    if self._client:
        if target in ('me', 'self'):
            return types.InputPeerSelf()
        def get_info():
            nonlocal self, result
            res_id = 0
            if isinstance(result, InputPeerChannel):
                res_id = result.channel_id
            elif isinstance(result, InputPeerChat):
                res_id = result.chat_id
            elif isinstance(result, InputPeerUser):
                res_id = result.user_id
            return self._sqlite_session._execute(
                'select username, name from entities where id = ?', res_id
            )
        try:
            result = self._client.session.get_input_entity(target)
            info = get_info() if with_info else None
            return result, info
        except ValueError:
            record_current_time()

        try:
            # when we are here, we are actually going to
            # send request to telegram servers
            if not check_if_appropriate_time_elapsed_from_last_telegram_request():
                return None
            result = self._client.get_input_entity(target)
            info = get_info() if with_info else None
            return result, info
        except ChannelPrivateError:
            pass
        except ValueError:
            pass
        except Exception:
            pass

Но мой код почему-то проблематичен с производительностью, потому что он делает избыточные запросы к базе данных SQLite. Например, если target на самом деле сущность внутри локальной базы данных и with_info является Trueсначала он запрашивает локальную базу данных в строке self._client.session.get_input_entity(target) и проверяет, если with_info является True, затем снова запрашивает базу данных, чтобы получить имя пользователя и имя столбца. В другой ситуации, если target не найден внутри локальной базы данных, вызывая self._client.get_input_entity(target) делает избыточный вызов локальной базы данных.

Зная эти проблемы с производительностью, я углубился в исходные коды телемарафона, но, поскольку я не очень разбираюсь в asyncio, я не смог бы написать ничего лучше, чем выше.

Есть идеи, как решить проблемы?

1 ответ

Решение

client.session.get_input_entity не будет вызывать API (это невозможно) и завершится неудачей, если в локальной базе данных нет совпадений, что, вероятно, и является желаемым поведением.

Теперь вы можете получить доступ к client.session._conn закрытый атрибут Это sqlite3.Connection объект, так что вы можете использовать это, чтобы сделать все запросы, которые вы хотите. Обратите внимание, что это может привести к поломке, так как вы получаете доступ к приватному члену, хотя никаких изменений в ближайшее время не ожидается. В идеале, вы должны создать подкласс файла сессии в соответствии с вашими потребностями. См. Файлы сессий в документации.

Другие вопросы по тегам