• Как запретить давать атрибуты функции без ключей?

    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()
    Ответ написан
    7 комментариев
  • Как получить смс без связи?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    Можно взять модем типа знаменитого Huawei E3372, к которому можно подключить выносную направленную антенну. Это позволит принимать сообщения за много километров от вышки. Плюс антенну можно поднять повыше, если рельеф и деревья мешают. Обычно это вариант для мобильного Internet на дачу, но и для приёма SMS подойдёт.

    Ещё у некоторых операторов бывают услуги типа "Мультифон" от Мегафона, с которой можно получить дополнительный номер с программным доступом. Но нужно учесть, что на такой номер часто невозможно получать SMS от всяких банков и прочих сервисов - они просто не доходят.

    Также можно разместить модем где-то не у себя, а в зоне уверенного приёма нужного оператора, и ходить этот комп по RDP/VNC/RustDesk/ssh для чтения SMS. К примеру, если находишься на даче, можно оставить этот модем у себя в городской квартире.

    И ещё один вариант: бывают приложения для Android, которые позволяют настроить пересылку SMS, например, на почту. Можно оставить в зоне приёма телефон (разумеется, на зарядке) и пущай пересылает.
    Ответ написан
    Комментировать
  • Как пробросить webhook Telegram бота на домашний компьютер через виртуальный сервер с OpenVPN и Docker?

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

    import config
    
    if __name__ == '__main__':
        if config.POLLING:
            ... start_polling
        else:
            start_webhook(...


    Всё-таки основная часть бота - его бизнес-логика. Каждый раз проверять работу его вебхуков необязательно.

    Если всё же хочется вебхуков, то организуем VPN по любой инструкции, коих тысячи, получаем туннель с парой IP. Например, 10.10.10.1 на сервере и 10.10.10.2 локально. На сервере поднимаем nginx, в котором настраиваем что-то типа
    location /path/to/api_test {
      proxy_pass http://10.10.10.2:порт/path/to/api_test
    }


    Обязательно настраиваем https, можно с Let's Encrypt, но можно и с самоподписанным сертификатом. В последнем случае надо будет загрузить этот сертификат в Telegram через API при настройке вебхука.

    Локально у себя пробросить в докере тот самый порт в нужный контейнер с ботом.

    Возможны разные вариации, не изменяющие общий принцип. Например, можно не настраивать https на сервере, пробросить весь порт "как есть" через nginx stream, haproxy, двойной нат или нат с policy routing, а сам https настраивать у себя локально.
    Ответ написан
    Комментировать
  • Как узнать User_id в inline mode?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    У объекта query, который передаётся в inline query handler, есть поле from_user, содержащее объект пользователя. Там есть и его id. Например:

    @bot.inline_handler(lambda query: len(query.query) > 0)
    def query_text(inline_query):
        print (inline_query.from_user.id)


    (это для телебота, другие библиотеки могут иметь некоторые отличия)

    Надо понимать, что inline mode - это лишь средство помощи в написании сообщения от имени пользователя, а не полноценное средство взаимодействия с пользователем. Результат ответа от бота может кэшироваться на серверах Телеграма и не перезапрашиваться при каждом запросе пользователя. А самое главное, что бот не может узнать, в какой чат отправляется сообщение, да и оно может не отправиться вообще. Единственный вариант - это если бот присутствует в нужном чате и имеет доступ к сообщениям, тогда он может ссылку via_bot с информацией о боте.

    В целом да, можно по user_id генерить реферальный хеш, а в качестве результата отдавать реферальную ссылку. Но нужно учитывать все ограничения режима и не ожидать от него слишком многого.
    Ответ написан