• Как получить chat_id из обьекта query | Aiogram?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Очевидно, в данном случае запрос делается из группового чата, а у групповых чатов user_id не совпадает с chat_id. Это вообще очень распространённая ошибка. Я вообще рекомендую всем всегда явно использовать chat_id, потому что в теории Телеграм может когда-нибудь даже для личных чатов начать использовать id, отличные от id пользователей.

    В query есть объект query.message, который содержит исходное сообщение, в котором нажали на кнопку. А в нём, соответственно, есть query.message.chat с объектом чата. А в query.message.chat уже есть id.
    Ответ написан
    Комментировать
  • Как сделать меню с inline - стрелками влево - вправо?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Например, пусть у нас есть каруселька из N элементов с номерами от 1 до N. Тогда можно просто показывать элемент 1, а у него две кнопки "влево" с callback=item:N и "вправо" с item:2. При нажатии на кнопку с callback=item:k редактировать это сообщение, выводя в него содержание элемента k и клавиатуру с кнопками item:(k-1) и item:(k+1).

    Это в общих чертах. Конкретная реализация может сильно различаться в зависимости от уровня хотелок, структуры данных итд итп. Например, можно показывать список категорий (в том числе с постраничным выводом), а в категориях уже позволять листать элементы категорий. Или можно позволить сделать поиск и устроить карусель из результатов поиска. Человеческая фантазия практически безгранична.
    Ответ написан
    Комментировать
  • Проблема с python?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Потому что надо учить язык, читать документацию и официальные примеры, а не по видео пытаться что-то там сделать. Где в этом коде делается поллинг? Естественно, без него скрипт будет сразу завершаться и работать не начнёт.
    Ответ написан
    2 комментария
  • Как запретить давать атрибуты функции без ключей?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    def func(param1, param2, *, param3, param4, ...):

    * означает, что дальше будут только keyword-аргументы. До звёздочки будут обычные позиционные аргументы.
    Ответ написан
    Комментировать
  • Как сделать скрипт на python запускающий батник?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Надо было лучше гуглить.

    os.system
    os.startfile
    Ответ написан
    Комментировать
  • Telegram Bot API как боту отправить контакт?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Бот может отправить пользователю кнопку, при нажатии на которую пользователь разрешает передать боту свои контактные данные.

    См. опцию request_contact в документации.
    Ответ написан
    5 комментариев
  • Как работает asyncio.sleep?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Есть такой системный вызов select (а также подобные ему poll, epoll итд), суть которого - передать массив файловых дескрипторов (частный случай - сетевых соединений) и затем при получении событий ввода-вывода получить список тех дескрипторов, в которых произошёл ввод-вывод. Важно понимать, что при это программа "засыпает", передаёт управление ОС и не тратит ресурсов. ОС сама разбудит программу при наступлении нужных событий (записался файл, пришли новые сетевые байтики итд). Высокопроизводительные сетевые приложения (типа nginx, haproxy итд) используют подобный подход для того, чтобы эффективно обрабатывать большое количество сетевого трафика одновременно.

    asyncio работает как раз примерно по тому же принципу. Когда случается ввод-вывод, нужная функция "засыпает", а управление передаётся потоку событий. Соответственно, он либо находит задачу, которая ожидает выполнения и передаёт ей управление, либо видит, что все задачи уже одидают какого-нибудь ввода-вывода и запускает select на все ожидающие дескрипторы (возможно, в реальности используется не select, а какой-то из его аналогов, но это для нас сейчас непринципиально). Как только приходит событие, программа просыпается, поток событий находит нужное событие и передаёт управление соответствующей задаче, которая его ожидала. Это позволяет очень эффективно в один поток работать с задачами, которые много ожидают ввода-вывода, но мало выполняют реальной процессорной работы.

    Обычный ввод/вывод является блокирующим: пока не будет выполнена операция (чтение/запись/передача/приём), программа приостанавливает свою работу в ожидании. В asyncio весь ввод-вывод является неблокирующим: операции ввода-вывода не приостанавливают работу программы, а позволяют перейти к другим ожидающим задачам.

    Обычный вызов sleep приводит к приостановке и засыпанию программы на указанное время (с передачей управления ОС), и в ней в это время ничего не выполняется. Как только время истечёт, ОС вернёт управление программе. Всё это время программа не работает, события ввода-вывода не обрабатывает.

    В то же время asyncio.sleep возвращает управление потоку событий, а не ОС, что позволяет переключиться на выполнение других задач, обработать новые события итд итп. Программа не останавливается и управление ОС не передаёт (ну, кроме сна в процессе исполнения select), поэтому asyncio.sleep приводит к неблокирующему засыпанию, не мешающему выполнять задачи, которым ждать окончания сна одной конкретной задачи не нужно. Когда истечёт не менее чем указанное в asyncio.sleep время, поток событий вернёт управление приостановленной задаче.

    Важно отметить, что и sleep, и asyncio.sleep не гарантируют, что функция возобновит работу через указанное число секунд, а не позже, но при этом sleep делает это значительно точнее. Потому что возврат в вызвавшую asyncio.sleep функцию может произойти только из потока событий и только при условии, что поток событий не вернёт управление какой-то другой задаче.

    Например, пусть есть функция, которая делает asyncio.sleep(1), затем три секунды работает числомолотилка без ввода-вывода. Тогда если у нас выполняются подряд с интервалами 0.1 с три таких функции, то первая задержит на 2.9 секунд возврат управления второй, а вторая - на 5.8 с третьей.

    Это общий принцип, разумеется, там много нюансов и особенностей реализации.
    Ответ написан
    Комментировать
  • Если сменить номер телефона в Telegram, сменится ли api_id и api_hash?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    api_id и api_hash - это реквизиты не аккаунта, а клиента Telegram. Соответственно, используя их, войти в твой аккаунт не получится. Конечно, если вместе с ними утёк дамп сессии, то это уже другое дело.

    Сменить api_id и api_hash нельзя. Только удалить свой аккаунт целиком и создать новый. Собственно, на странице https://my.telegram.org/apps предупреждают, что их надо хранить в секрете.
    Ответ написан
  • Почему декоратор не работает с функциями которые принимаю переменные?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    *args в приведённом коде это аргументы самого декоратора. А аргументы функции надо описывать в wrapper.

    Сама суть декоратора в том, что вместо исходной функции получается другая функция wrapper.
    Ответ написан
    Комментировать
  • Как объединить чат WhatsApp и Telegram в 1 чат?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Легального способа, увы, нет. И тут узкое место сам WhatsApp.

    WhatsApp предлагает доступ к API только для бизнесов - это первый рубеж, но не последний. В первых версиях Business API были какие-то функции для работы с группами, но их уже давно удалили, и сейчас API позволяет общение только с бизнес-аккаунтом напрямую.

    В WhatsApp есть интергации на основе реверс-инжиниринга web-клиента, но их использование нелегально и сопряжено с рисками бана. Если в такое и играть, то по крайней мере не своим основным аккаунтом, чтобы не было потом очень обидно. Также такая интеграция работает негарантированно и может сломаться в любой момент. Но её в теории достаточно для реализации моста.

    Я бы рекомендовал просто отпинать всех ретроградов, которые ещё не зарегались в Телеграме, чтобы исправили это досадное упущение.
    Ответ написан
    Комментировать
  • Как написать бот для создания ботов?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Конечно же можно. Но это весьма нетиповая задача, и поэтому нельзя ожидать готовых решений.

    Как одно из простейших, можно сделать бота на вебхуках и обойтись одним скриптом. При запуске инициализируем управляющего бота и по очереди всех клиентских ботов, вызываем от них метод set_webhook. Например, делаем что-то типа site.ru/webhook/control и site.ru/webhook/{bot_token}. Затем запускаем основной метод используемого фреймворка (например, flask или aiohttp).

    Описываем два обработчика, /webhook/control для управляющего бота и /wehook/{bot_token} для клиентских ботов. В управляющем обрабатываем запросы на добавление/изменение/удаление. В клиентском из пути в запросе берём токен бота, создаём объект бота с токеном (вариант - можно держать готовый словарь из объектов бота с ключом по токену, маленькая оптимизация) и вызываем от него метод process_new_updates (или какой там используется в любимой библиотеке).

    Это общий план, не руководство к обязательному исполнению. Разумеется, многое может сильно зависеть от задачи. Например, если выполняемые ботом операции делают много процессорных операций, то может быть будет лучше запускать ботов отдельными скриптами или использовать треды. Если требуют времени, но зависят от ввода-вывода (базы данных, сторонние API итд) - asyncio. Нужно много памяти/процессора - запускать в kubernetes/swarm/виртуалках/итд. Сложный проект может потребовать сложных решений.
    Ответ написан
    Комментировать
  • Как отправить сообщение по его id, aiogram?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Прислать повторно - нет. Bot API не позволяет получить историческую информацию по ранее отправленным сообщениям. Но можно переслать его в тот же чат с помощью метода forward_message, правда, будет выглядеть не очень красиво. Лучше хранить в базе не просто id сообщения, а исходные данные, на которых оно основано (фотографию и подпись).
    Ответ написан
    Комментировать
  • Как исправить неправильное сложение десятичных дробей при помощи sympify в Python?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Скорее всего, дело в том, что десятичный разделитель - это точка, а не запятая. А запятая тут превращает строку в кортеж двух чисел. Получается, что 5+5,1 это число 10 и число 1, а 5,1+5 - число 5 и число 6.
    Ответ написан
    Комментировать
  • Как удалить сообщения который содержат Х в PyTeleBot?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Bot API не позволяет получать историю чата, только новые сообщения по мере их поступления. Читать историю можно только с помощью клиентского API (см. в сторону библиотек telethon или pyrogram).
    Ответ написан
    Комментировать
  • Как держать контейнер работающим после запуске стартового скрипта .sh?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Надо убрать & из команды. Серьёзно, при запуске приложения в контейнере вообще не нужно никак уводить его выполнение в фоновый режим.

    До кучи, при запуске в контейнере не имеет смысла перенаправлять вывод в /dev/null, поскольку чаще всего удобнее, что можно посмотреть вывод приложения командой docker logs.
    Ответ написан
    1 комментарий
  • Как переименовать файлы с определенным условием в Linux?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    В Linux есть замечательная утилита rename. Ну, точнее, их две: одна входит в пакет util-linux, другая - скрипт на базе perl-модуля File::Rename. В deb-дистрибутивах нужный нам скрипт устанавливается в составе пакета rename и также имеет имя второе file-rename (а версия из util-linux имеет имя rename.ul). В других дистрибутивах может быть иначе, поэтому смотрим внимательнее параметры запуска (можно запустить rename без параметров и убедиться, что это нужный вариант).

    Синтаксис предельно простой: rename регулярное_выражение_замены_perl имена_файлов

    Синтаксис rename.ul для сравнения: rename что_заменить на_что_заменить имена_файлов

    Вот что можно сделать для решения задачи (в предположении, что других точек, кроме как перед расширением, в имени файла нет):

    rename 's/-\w+\././' *

    Этот скрипт намного удобнее многоэтажных однострочников, да и пригодится в жизни ещё не раз.
    Ответ написан
    7 комментариев
  • Если в чате ввести символ @ - появится список ботов, как запретить пользоваться ними не отключая "стикеры и GIF"?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Если задача состоит в том, чтобы пользователи в конкретной группе не оставляли таких сообщений, то можно их удалять с помощью бота с админскими правами. Например:

    @bot.message_handler(content_types=['text'])
    def keep_chat_clean(message):
        msg = message.text
        if "via_bot" in message.json and message.json["via_bot"]["username"] in BLACKLISTED_BOTS:
        try:
            bot.delete_message(message.chat.id, message.message_id)
        except telebot.apihelper.ApiException:
            # обрабатываем ситуацию, когда удаление не удалось
            pass
    Ответ написан
    Комментировать
  • Как правильно сделать структуру базы данных?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Обычная задача, делаем промежуточную таблицу grapes_for_wine (wine_id, grape_id).
    Ответ написан
    Комментировать
  • Как отловить удалённое сообщение в Telebot?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Telegram Bot API не поддерживает сообщение о фактах удаления сообщений. Также через API нельзя читать историю сообщений. Поэтому эта задача не имеет решения с помощью ботов.

    Это можно сделать только с помощью клиентского API (MTProto), имитирующего реального пользователя ("юзербот").
    Ответ написан
  • Как сделать await purge(limit=1) в disnake?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Обработчик on_message принимает в качестве параметра сообщение (тип Message), а не interaction context. А у сообщения есть метод delete. Так что использование purge тут совершенно не к месту. Открою секрет: удалять вообще что бы то ни было единичное с помощью purge - плохая идея. Пока бот обрабатывает событие, в канале могут появиться новые сообщения. Purge нужно только для масштабной очистки.

    @client.event
    async def on_message(message):
        await message.delete()
        temporary_message = message.reply(...)
        await asyncio.sleep(30)
        await temporary_message.delete()
    Ответ написан
    5 комментариев