Задать вопрос
  • Как сделать так, чтобы бот, написанный на модуле telebot, включал у пользователя режим ответа на cообщение?

    Vindicar
    @Vindicar
    RTFM!
    А доки на телебот читать пробовал? Нет? Ну так попробуй, там много интересного.

    send_message(...)
    reply_markup (telebot.types.InlineKeyboardMarkup or telebot.types.ReplyKeyboardMarkup or telebot.types.ReplyKeyboardRemove or telebot.types.ForceReply) – Additional interface options. A JSON-serialized object for an inline keyboard, custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user.

    Выделение моё.
    Т.е. вместо клавиатуры передаёшь специальный объект. А дальше тыкаешь по ссылке ForceReply и смотришь что это за объект и как его сконструировать.
    Ответ написан
  • Как запустить uvicorn не блокируя основной поток?

    Vindicar
    @Vindicar
    RTFM!
    Возможно, придётся заглянуть с другой стороны. uvicorn вроде позволяет запускать корутины при старте сервера? Оформи бота как корутину и используй этот механизм, чтобы uvicorn запускал бота, а не наоборот.
    Ответ написан
  • Создание переменных в тг боте аиограм?

    Vindicar
    @Vindicar
    RTFM!
    Словарь, где ключ - id пользователя, а значение - данные, которые ты хочешь хранить для этого пользователя.
    Но такой словарь не переживёт перезапуска бота.
    Ответ написан
  • Как сделать прокси который будет работать только на одном сайте?

    Vindicar
    @Vindicar
    RTFM!
    Во-первых, нужно чётко описать цепочку трафика. Вот первый вариант:
    [клиент] --> [решающий прокси] --> [реальный прокси] --> [целевой сайт]
                         \--> [другие сайты]

    Т.е. клиент всегда ходит через решающий прокси (тот, который принимает решение, как обращаться к сайту), а далее трафик идёт по одной из двух веток.
    Плюсы: у клиента фиксированная конфигурация, требуется только поддержка прокси, не требуется доп. ПО на кленте
    Минусы: решающий прокси тащит на себе весь трафик клиента.

    Если это недопустимо, т.е. тебе позарез нужно что-то такое:
    [клиент] --> [решающий прокси] --> [реальный прокси] --> [целевой сайт]
        \--> [другие сайты]

    То решение нужно принимать на стороне клиента. Тут есть два варианта.
    Вариант А: решающий прокси разворачивается на клиенте. Так работает nekoray, например.
    [клиент --> решающий прокси] --> [реальный прокси] --> [целевой сайт]
                         \--> [другие сайты]

    Плюсы: ПО на клиенте всё ещё требует только поддержку прокси, и всё.
    Минусы: на клиенте ставится доп. ПО, управление производится на машине клиента (неудобно если их несколько)

    Вариант Б, которым я сам пользуюсь: использовать PAC-файл. Он выполняется в контексте браузера и содержит логику на JS, которая определяет, как посылать каждый запрос. Разумеется, генератор и прокси могут быть на одном узле.
    [Генератор и хостинг для PAC-файла]
       ^
       |
    [клиентский браузер] --> [реальный прокси] --> [целевой сайт]
        \--> [другие сайты]

    Плюсы: на клиенте фиксированная конфигурация и нет доп. ПО. Все решения принимаются сервером, который периодиченки обновляет PAC-файл.
    Минусы: работает ТОЛЬКО с браузером, если прокси недоступен, браузер может начать пытаться слать запросы напрямую. Трудно повлиять на то, как часто клиент будет перезагружать PAC-файл.

    Вариант В: использовать маршрутизацию совместно VPN, чтобы завернуть все пакеты на целевые хосты в VPN, и только их.
    Плюсы: работает с любым ПО, даже если оно не умеет прокси.
    Минусы: Требует полноценный VPN с виртуальным сетевым адаптером, а не прокси. Определить подсети по имени сайта нетривиально, ибо CDN могут менять адреса без предупреждения. Раздавать 100500 маршрутов на клиенты тоже нетривиально.
    Ответ написан
    Комментировать
  • Бот перестает работать после определенного блока, как исправить?

    Vindicar
    @Vindicar
    RTFM!
    Ну так ты не озаботился изучением работы инлайн-кнопок, вот и результат.
    У тебя две функции отмечены декоратором
    @bot.callback_query_handler(func=lambda call: True)

    Эта проблема встречается регулярно, и на этом сайте уже есть несколько вопросов по ней.
    Вот один из них, с моим ответом.
    Ответ написан
    Комментировать
  • Как исправить ошибку "IndexError: list index out of range" для чат-бота телеграмм на библиотеке aiogram?

    Vindicar
    @Vindicar
    RTFM!
    А голову включить и код по шагам пройти не пробовал?
    dict_data_user = await state.get_data()
    list_data_user = []  # сначала список пуст
    for k, v in dict_data_user.items():  # начинаем цикл, первая итерация
        list_data_user.append(v)  # добавляем в список элемент. Длина списка равна 1.
        user_id = message.from_user.id
        id_product = list_data_user[0]  # читаем из списка элемент с индексом 0. ОК, он есть, мы его только что добавили
        full_name = list_data_user[1]  # откуда бы взяться элементу с индексом 1 в списке из одного элемента?!
        index_adress = list_data_user[2]
        number_phon = list_data_user[3]
        user_db.add_user(id_product=id_product, user_id=user_id, full_name=full_name,
                         index_adress=index_adress, number_phon=number_phon)
        await state.clear()

    Тут вопрос стоит, ты вообще понимаешь, что пишешь? Ты точно имел ввиду распаковку list_data_user, а не v или ещё чего-нибудь?
    Ответ написан
  • Как получать информацию о подарках в прямых эфирах TikTok?

    Vindicar
    @Vindicar
    RTFM!
    Вот трудно вбить в гугл "API tiktok gift"?
    Вторая же ссылка ведёт на гитхаб подходящей под описание библиотеки. А там и примеры есть...
    Вторая ссылка, Карл!
    Ответ написан
    1 комментарий
  • Как выводить значения row[0] в разные опции Select Menu?

    Vindicar
    @Vindicar
    RTFM!
    Ну так голову-то включи.
    for row in records:
        print("ник:", row[0])
        options=[
            discord.SelectOption(label=row[0])
         ]

    Ты options перезаписывашеь каждый раз, вместо того, чтобы создать этот список перед циклом, и добавлять в него элементы.
    Ответ написан
    Комментировать
  • Не могу решить задание ЕГЭ. Почему мой код не работает?

    Vindicar
    @Vindicar
    RTFM!
    Тебе выше правильно намекнули.
    Во-первых, нужно увидеть простую вещь: дописывание нуля в конец двоичного представления - это то же, что умножение на 2. Назовём это операция А. А дописывание единицы - то же, что умножение на 2 и прибавление 1. Назовём это операция Б. Таким образом, можно избавиться от двоичной системы в задании.

    Второе: операция А будет давать в результате только чётные числа, независимо от аргумента. Аналогично, операция Б будет давать только нечётные числа, независимо от аргумента.

    Третье: результат как операции А, так и операции Б всегда больше аргумента. Более того, больший аргумент даёт больший результат. Это легко показать.
    Пусть у нас есть число x. Результат операции A меньше, чем операции Б над тем же числом, поэтому мы должны проверить только один сценарий: когда к x применяется операция А, а к x - 1 - операция Б. Значит, x станет 2 * x, а x - 1 станет 2 * (x - 1) + 1 = 2 * x - 1. Т.е. для x - 1 результат меньше. Т.е. даже в таком, самом спорном случае, меньшее число даёт меньший результат

    Четвёртое: задачу можно решать задом наперёд. У нас есть целевые числа - значит, мы должны применить к ним алгоритм наоборот, чтобы получить исходные. Это и даст нам диапазон для поиска исходных чисел.

    Дальше уже проще. Смотрим на начало целевого диапазона, число 876 544. Оно чётное, значит, оно могло быть создано только операцией А. Значит, чтобы получить исходное число, нужно поделить его на 2. 876 544 / 2 = 438 272. Это число тоже чётное, оно тоже могло быть создано только операцией А. 438 272/ 2 = 219 136. Оно тоже чётное. Значит, третий раз применяем операцию А наоборот: 219 136 / 2 = 109 568. Исходя из пункта 3, можем сказать, что числа, меньше 109 568, не могут дать результат, больший или равный 876 544.

    Аналогично анализируем верхнюю границу целевого диапазона.
    1 234 567 899 = 2 * 617 283 949 + 1 (операция Б)
    617 283 949 = 2 * 308 641 974 + 1 (операция Б)
    308 641 974 = 2 * 154 320 987 (операция А)
    Значит, исходное число должно быть не более 154 320 987. Вот тебе и диапазон для поиска.
    А поскольку большие аргументы дают большие результаты, то это означает, что ни одно число меньше 154 320 987 не даст результат, который выйдет за верхнюю границу диапазона (1 234 567 899). Т.е. искомые числа - это весь диапазон от 109 568 до 154 320 987. Посчитать, сколько чисел в диапазоне, тривиально.
    Ответ написан
    Комментировать
  • Как организовать клиент-клиент соединение на python через socket?

    Vindicar
    @Vindicar
    RTFM!
    Почитай про разницу между потоковыми и датаграммными сокетами.
    В потоковых сокетах всегда есть клиент, устанавливающий подключение, и есть сервер, ожидающий подключение.
    Датаграммные сокеты в этом плане более одноранговы, хотя и там обычно есть узел, который первым проявляет инициативую.
    В любом случае, проблемы у тебя возникнут, когда оба узла находятся в разных сетях, за несколькими слоями NAT каждый. Имея публично доступный сервер, эту проблему решить куда проще.
    Ответ написан
    Комментировать
  • Как заставить бота ждать ответа пользователя?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала твоя лесенка из if-elif-elif-... - плохое решение.
    Сделай нормальную структуру данных вместо пачки переменных. Используй словарь, или ещё что. Например, такого вида:
    # словарь, где ключ - строка с названием пунка отправления, а значение - ещё словарь,
    #   где ключ - строка с названием пункта назначения, а значение - список,
    #     где элементы - пары строк ("время отправления", "время прибытия")
    # Тогда с таким словарём можно будет работать так:
    bus_timetable: dict[str, dict[str, list[tuple[str, str]]]] = {
        # ты словарь заполняешь по результатам парсинга, ну и то хорошо
        # я для примера запишу прямо так
        'Владимир' : {
            'Муром': [
                ('8:00', '11:00'),
                ('9:00', '12:00'),
                ('10:00', '13:00'),
            ],
        },
    }
    point_from = 'Владимир'
    point_to = 'Муром'
    # для примера вывожу в консоль, для бота перепишешь сам
    print(f'Автобус из {point_from} в {point_to}')  
    for departure, arrival in bus_timetable[point_from][point_to]:
        print(f'Отходит в {departure}, прибывает в {arrival}')

    Тогда тебе не потребуется делать кучу веток для разных городов, достаточно лишь определить значения point_from и point_to. Более того, ты можешь просто перечислить ключи внешнего словаря при создании кнопок:
    kbd_from = types.ReplyKeyboardMarkup(resize_keyboard=True)
    buttons = [types.KeyboardButton(point_from) for point_from in bus_timetable]  # список кнопок
    kbd_from.add(*buttons)  # если надо добавить все кнопки сразу

    Похожий приём возможен и для определения кнопок для городов назначения.

    А теперь по тому, как сделать выбор. Тут есть два варианта, но давай пока поговорим о ReplyKeyboardMarkup, которую ты используешь.
    Если ты хочешь использовать ReplyKeyboardMarkup, то стоит помнить - её кнопки дают тот же эффект, как если бы пользователь просто ввёл текст кнопки сам. Поэтому тебе потребуется использовать register_next_step_handler(). Ты не привёл код своей попытки, но в целом идея несложная. В основном обработчике события ты проверяешь, что текст сообщения содержится в bus_timetable (оператор in в помощь) - это значит, что текст сообщения содержит название известного боту города.
    Тогда ты делаешь вызов register_next_step_handler() и ставишь обработчик второго сообщения. Единственное "но": тебе нужно будет обработчику передать первый выбранный город. Это будет выглядеть как-то так:
    @bot.message_handler(content_types=['text'])
    def first_message_handler(message):
        if message.text in bus_timetable:  # назвали известный город? Это пункт отправления
            kbd = ...  # тут определяешь клавиатуру, опираясь на города в bus_timetable[message.text]
            response = bot.reply_to(message, "А теперь назовите пункт назначения", reply_markup=kbd)
            # обрати внимание: все параметры после указания функции second_message_handler будут
            # переданы в эту функцию при её вызове. А вызвана она будет, когда пользователь ответит.
            bot.register_next_step_handler(response, second_message_handler, message.text)
        elif ...  # нам назвали не город - тут можно проверить другие команды
    
    def second_message_handler(message, point_from):
        # а эта функция будет вызвана только когда пользователь уже назвал пункт отправления
        # point_from будет содержать название города, отправленное пользователем.
        if message.text in bus_timetable[point_from]:  # нам назвали допустимый пункт назначения
            bus_rides = bus_timetable[point_from][message.text]  # список рейсов
            ...  # дальше перебираем список bus_rides и выводим его пользователю
        else:  # пользователь назвал что-то другое
            ...  # говорим пользователю, что он дурак
    Ответ написан
    1 комментарий
  • Как определить область в потоке видео и среагировать на обнаружение?

    Vindicar
    @Vindicar
    RTFM!
    Ну так и скажи, "я хочу написать чит для Dead By Daylight, помогите!"
    Сделай полярное преобразование нужного куска экрана. Тогда нужная полоса превратится в вертикальную, и её легко можно будет выбрать обрезкой, а индикатор будет двигаться не по кругу, а по вертикали, и его движение будет проще анализировать.
    Впрочем, приколюхи вроде докторского безумия, которые меняет положение и направление движения индикатора, собьют твой алгоритм. А ещё есть скилл-чеки, у которых просто нет идеальной зоны, только хорошая.
    Ответ написан
    2 комментария
  • Как правильно проксировать запрос при коннекте к aiosmtplib?

    Vindicar
    @Vindicar
    RTFM!
    Погоди-ка, а параметр sock на что? Он есть и у функции send(), и у конструктора SMTP(). Подготовь запроксированный экземпляр socket.socket и отдай туда.
    Судя по упомянутому в комментах issue, нужно будет самому закрыть этот сокет, когда закончишь работу с SMTP.

    Нет, конечно, есть варианты жёстче. Например, отнаследоваться от SMTP, переопределить _create_connection(), и заменить/пропатчить self.loop так, чтобы вместо self.loop.create_connection() вызывалась твоя корутина. В ней уже формировать сокет как тебе надо.
    Но мне кажется, проще поработать с sock.
    Ответ написан
    Комментировать
  • Возможно ли создать асинхронную функцию чтобы отправлять в неё данные постепенно без await?

    Vindicar
    @Vindicar
    RTFM!
    asyncio.Queue в помощь. Чтобы синхронно помещать туда элементы, или используй put_nowait(), или выкручивайся с вызовом put() через create_task().

    Но у меня прямой вопрос: а как ты, собственно, собираешься выполнять синхронную g() в асинхронной программе? Потому что если через потоки, то про совет выше можешь забыть.
    Ответ написан
  • Как использовать SSH вместо VPN?

    Vindicar
    @Vindicar
    RTFM!
    А конфиг сервера позволяет динамический проброс портов (AllowTcpForwarding yes)? Там обновлений не было? Может, несколько конфигов конфликтуют? Например, в autorized_keys можно прописать no-port-forwarding или restrict.

    Ну и самая идея такая себе. Многие провайдеры шейпят SSH, жёстко ограничивая максимальную скорость - в терминале поработать можно, а как туннель использовать - нет. Так что даже если раньше работало, может перестать в любой момент.
    Ответ написан
    Комментировать
  • Почему Telegram бот не отвечает?

    Vindicar
    @Vindicar
    RTFM!
    Ну тебе же английским по белому написано:
    RuntimeError: This Application was not initialized via `Application.initialize`!

    Нужно перед вызовом application.start() добавить аналогичный вызов initialize().

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

    Vindicar
    @Vindicar
    RTFM!
    CUDA тут не нужна, просто не следует перебирать массив numpy питоновскими циклами.
    Если есть возможность - выражай желаемое через операции над массивом в целом. Тогда цикл организует numpy, и он это делает через нативный код, ЕМНИП - работает в разы быстрее.
    Подход ниже сожрёт больше памяти, но зато короткий, выразительный и использует только средства numpy.
    image = cv2.imread("image.png")
    image = image.astype(numpy.uint32)  # смена типа нужна, иначе при сдвиге потеряются разряды
    image_clone = (image[..., 0] << 16) | (image[..., 1] << 8) | image[..., 2]

    Если хочется заморочиться по памяти:
    image = cv2.imread("image.png")
    image_clone = numpy.zeros(image.shape[:2], numpy.uint32)
    # краткие формы операций не создают новый массив, а изменяют существующий по месту
    image_clone |= image[..., 0]
    image_clone <<= 8
    image_clone |= image[..., 1]
    image_clone <<= 8
    image_clone |= image[..., 2]


    P.S.: как там, у imread() починили уже неумение работать с юникодными путями?
    Ответ написан
    Комментировать
  • Как сделать чтобы дискорд бот сидел с телефона?

    Vindicar
    @Vindicar
    RTFM!
    Такой механизм не предусмотрен библиотекой. Быстрый гугл по discord.py phone status показывает, что для этого приходится патчить код класса discord.gateway.DiscordWebSoсket, чтобы при отправке пакета identify устройство/клиент идентифицировалось как 'Discord Android'.
    В связи с этим вопрос: тебе оно вот прямо нужно? Или это просто понты?
    Мненада!11

    Более-менее вменяемым (в плане риска что-то сломать при обновлении библиотеки) мне показался такой вариант со стэковерфлоу.
    from discord.gateway import DiscordWebSocket
    
    
    class MyDiscordWebSocket(DiscordWebSocket):
        async def send_as_json(self, data):
            if data.get('op') == self.IDENTIFY:
                if data.get('d', {}).get('properties', {}).get('$browser') is not None:
                    data['d']['properties']['$browser'] = 'Discord Android'
                    data['d']['properties']['$device'] = 'Discord Android'
            return await super().send_as_json(data)
    
    
    DiscordWebSocket.from_client = MyDiscordWebSocket.from_client

    Пробуй на свой страх и риск.


    И это, завязывай с упоминаниями юзеров в вопросах. Это хороший способ заставить людей тебя игнорировать.
    Ответ написан
    Комментировать
  • Почему возникает ошибка при создании Telegram бота?

    Vindicar
    @Vindicar
    RTFM!
    Открываем официальные примеры, находим простейший эхобот, и начинаем с него.
    В частности, смотрим, каким методом запускается работа бота.
    Спойлер
    bot.infinity_polling()
    Ответ написан
    Комментировать
  • Бот для уведомлений с ютуба не работает больше 3х дней, где я мог ошибиться?

    Vindicar
    @Vindicar
    RTFM!
    Во-первых, научись регистрировать проблемы. Почитай про logging, как его настраивать. Расставь в боте и в парсере try-except и записывай в лог возникающие ошибки. Тогда у тебя будет текст ошибки, с которым уже можно как-то работать.

    Но я подозреваю, что дело в доступе к базе Sqlite из двух процессов. Sqlite, в отличие от "взрослых" БД, не является ни процессо-, ни даже потоко-безопасной. К ней можно безопасно обращаться только строго по очереди.
    Как только звёзды сойдутся так, что оба твоих процесса обращаются к базе одновременно - кому-то из них не повезёт. Если не повезёт парсеру, ты просто не увидишь инфу о новом видео при этом запуске. Если не повезёт боту - бот упадёт.

    Тут вопрос такой: а нужна ли вообще тут БД? Если да, то для чего она нужна?
    yt-dlp, к сожалению, синхронный - но ты ведь можешь выполнять этот код через run_in_executor(). Тогда получится интегрировать парсер прямо в бот, и отправлять сообщения немедленно.

    Альтернативно, можно схитрить так: сделать боту учётку на ютубе, подписать его на нужные каналы и настроить уведомления о новых видео по почте, а в боте мониторить почтовый ящик через email-monitor или подобное.
    Ответ написан
    Комментировать