• Как исправить повторную отправку сообщения телеграм бота в коде Python Aiogram?

    Vindicar
    @Vindicar
    RTFM!
    Включи голову и пройди программу по шагам.
    # Функция отображения 
    async def show_pages(chat_id, page):
        ...
        # тут ты отправляешь сообщение!
        sent_message = await bot.send_message(chat_id, text='Выберите страницу:', reply_markup=keyboard)
        return sent_message.message_id
    
    # Обработчик кнопок
    async def callback_handler(query: types.CallbackQuery):
        ...
        message_id = await show_page(query.message.chat.id, page=current_page)  # <-- show_page() отправляет сообщение!
        # Редактируем сообщение вместо отправки нового <-- не вместо, а после отправки нового
        await bot.edit_message_text(chat_id=query.message.chat.id, message_id=query.message.message_id, text='Выберите страницу:',
                                    reply_markup=InlineKeyboardMarkup())  # <-- сносишь клавиатуру у старого сообщения


    Я бы переписал show_page() так, чтобы она возвращала подготовленную для пагинации клавиатуру, а потом уже эту клавиатуру использовал в коде, который show_page() вызывает.
    Альтернативно, переделай show_page() так, чтобы она всегда редактировала переданное ей существующее сообщение. Тогда при первом показе ты сначала отправляешь "Подождите..." а потом это сообщение отдаёшь для переделки show_page(). При последующих ты переиспользуешь это сообщение.
    Но нужно иметь ввиду - вроде есть лимит времени на правку сообщения.
    Ответ написан
    Комментировать
  • Почему метод strip в Python так работает?

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

    Не "строку", а "набор символов". Т.е. это указание на то, какие символы удалять, их порядок и число повторений не важно.
    Ответ написан
    Комментировать
  • Как импортировать данные из .docx файла в JavaScript логику?

    Vindicar
    @Vindicar
    RTFM!
    Технически, DOCX можно прочитать - это ZIP архив, в котором лежат XMLки. Библиотека для чтения ZIP архивов под JS (в т.ч. браузерный) есть.
    Но реально проще будет использовать расширенную поиск-замену в Word, чтобы заменить красный цвет на уникальный символ или комбинацию символов перед текстом. Комбинация ^& означает "искомый текст", т.е. приведённый ниже пример вставляет знак процента перед текстом, написанным красным цветом.
    659eee532a796683149306.png
    А потом уже вставить в веб-страницу текст без форматирования и разбирать его.
    Ответ написан
    Комментировать
  • Ошибка при импорте, как исправить?

    Vindicar
    @Vindicar
    RTFM!
    Проблема в том, что ты пытаешься сделать бота и скриптом, и модулем. А импорт работает только одним способом.
    Я бы сказал, просто вынеси main в папку выше (ну или остального бота в подпапку).
    Тогда бот будет модулем, а главный скрипт - скриптом.

    Не, ты, конечно, можешь в __init__ написать изврат в духе
    try:
        from .wtf import stuff
    except ImportError:
        from wtf import stuff

    И сделать в main from __init__ import *
    Но не надо так делать. Пожалеешь потом.
    Ответ написан
    1 комментарий
  • Как реализовать базовый класс для динамического создания подклассов без вызова рекурсии в Python?

    Vindicar
    @Vindicar
    RTFM!
    Пусть родительский класс имеет классовую переменную, храняющую список известных классов потомков и ассоциированные с ними данные.
    Потомки должны будут регистрировать себя в этой коллекции - либо явно (через метод или декоратор), либо неявно, через метакласс. Тогда родительский класс будет перебирать эту коллекцию в поисках "подходящего" класса-потомка и создавать его.

    Пример:
    class Parent:
        known_children = []  # список классов-потомков Parent
        @staticmethod
        def register(klass):  # декоратор для регистрации классов-потомков
            Parent.known_children.append(klass)
            return klass
        @classmethod
        def can_handle(cls, data):  # "эй, потомок, ты можешь себя создать из этих данных?"
            raise NotImplementedError()
        @staticmethod
        def make(data):  # создаёт экземпляр одного из потомков
            for child in Parent.known_children:
                if child.can_handle(data):  # потомок согласился обработать данные?
                    return child(data)  # вызываем конструктор класса-потомка 
            else:  # относится к for ... in !
                raise TypeError(f'Никто не знает, что делать с этим:\n{data!r}')
        # тут остальная начинка класса
        ...
    
    # а это пример потомка
    @Parent.register  # явная регистрация потомка через декоратор
    # так удобнее, потому что так можно создавать промежуточных потомков,
    # которые не будут реально использоваться - только в наследовании
    class SomeChild(Parent):
        @classmethod
        def can_handle(cls, data):
            return data.get('name', None) == 'SomeChild'  # критерий для определения - наш случай или нет?
        
        def __init__(self, data):
            self.x = data['x']
            self.y = data['y']
    
    c = Parent.make({'name': 'SomeChild', 'x': 42, 'y': 69})  # создаст экземпляр SomeChild
    print(c)
    try:
        Parent.make({'name': 'invalid'})  # потерпит неудачу
    except TypeError as err:
        print(err)
    Ответ написан
    Комментировать
  • Как запустить старый проект, где найти информацию и задавать вопросы далее?

    Vindicar
    @Vindicar
    RTFM!
    Переведи ошибку и прочитай внимательно. Тебе в ней перечислили возможные URL, на которые можно зайти. Их синтаксис вполне узнаваемый, это регулярные выражения в стиле питона.
    В частности, интерес вызывает первый пункт в списке - URL с говорящим названием admin/. Скорее всего, он ведёт на какую-то админ-панель. Стоит взглянуть.

    Кроме того, обрати внимание на папку test_project - там почти наверняка простенький пример использования.

    А ещё подскажу, что в профиле владельца репозитория указан его e-mail. Только, боюсь, вопросы придётся задавать
    1. Конкретно
    2. На английском
    Так что это вариант на крайний случай.
    Ответ написан
    Комментировать
  • Как в пайтон вводя имя переменной вызвать её?

    Vindicar
    @Vindicar
    RTFM!
    Замечу, что в таких случаях лучше использовать словарь.
    Ответ написан
    3 комментария
  • Как отфильтровать текст Python?

    Vindicar
    @Vindicar
    RTFM!
    Регулярные выражения в Питоне
    import re
    
    pattern = re.compile(r'^\d+\.\s+')  # начало строки, 1+ цифра, точка, 1+ пробел
    # re.compile() можно сделать один раз, а потом переиспользовать полученный pattern
    text = '1. Салат 1'
    result = pattern.sub('', text)  # заменяем пустой строкой подходящие под шаблон части text
    print(result)
    Ответ написан
    2 комментария
  • Не отображаются виджеты, приложение виснет, где ошибка?

    Vindicar
    @Vindicar
    RTFM!
    def setting(self):
        self.server.check_currency()
        ...
        self.setting()

    Что это за *непроизносимое марсианское ругательство*?
    Во-первых, ты реализуешь (вечный?) цикл через рекурсию, что само по себе глупо. Рекурсия куда более ограниченная штука, хотя бы из-за глубины стека.
    Во-вторых, у тебя именно что бесконечный цикл - ты не даёшь программе передышки, чтобы она могла отрисовать окно. По сути, у тебя выполнение зациклится намертво на строке window1 = Kurz(), так как приложение уйдёт в рекурсивный вызов self.setting() и из него уже не вернётся.

    Первая же ссылка в гугле по запросу "pyqt timer" даёт пример, как периодически обновлять содержимое окна, используя QTimer.

    Сначала читай, потом думай, потом уже бросайся делать.
    Ответ написан
    Комментировать
  • Как можно в Tesseract улучшить распознавание желтого шрифта на красном фоне?

    Vindicar
    @Vindicar
    RTFM!
    Перевести желтое-на-красном в чёрное-на-белом. Если цвета всегда одинаковые, то можно просто найти разность между цветом каждого пикселя и эталоном, и по ней судить о яркости: похоже = чёрное, непохоже = белое, или наоборот.
    Ответ написан
    Комментировать
  • Как пофикисить ошибку запуска телеграм бота на VDS на постоянной основе?

    Vindicar
    @Vindicar
    RTFM!
    Ну у тебя явно два разных окружения.
    Просто python3 bot.py, без activate и прочего, использует системное окружение.
    А в приведённом тобой systemd-модуле явно видно виртуальное окружение.
    Так куда ты поставил openai - в системное или в виртуальное?
    Для установки в виртуальное используй pip из этого окружения.
    Ответ написан
    1 комментарий
  • Как найти чат, возвращающий ошибку "ApiError: 917" с помощью логирования?

    Vindicar
    @Vindicar
    RTFM!
    Заверни вызов метода в try - except, в ветке except пиши в журнал (хоть стандартынй модуль logging, хоть просто в файл) id чата, с которым пытался работать.
    Ответ написан
    Комментировать
  • Почему не работает cron?

    Vindicar
    @Vindicar
    RTFM!
    Попробуй просто
    cd /root/my_venv && /root/my_venv/bin/python /root/my_venv/app.py

    activate-скрипт не так уж много полезного делает.
    Ответ написан
    1 комментарий
  • Почему бот не реагирует на inline клавиатуру?

    Vindicar
    @Vindicar
    RTFM!
    Почему ru_bot() вообще отдекорирован как обработчик события, если ты эту функцию вызываешь вручную?
    И, самое главное, откуда в модуле ru берётся dp, и тот ли это dp, что и в main?

    Я подозреваю, что не тот, а свой собственный. Тогда не удивительно, что регистрация обработчиков в нём не имеет эффекта...

    Если это так

    Переделай импортируемые модули так, чтобы в них можно было передать объект dp из main. Например, так:
    # ru.py
    
    def setup(dp):
        @dp.message_handler()
        async def ru_bot(message: types.Message):  # да, эти функции описаны внутри setup()
            ...
    
        @dp.callback_query_handler()
        async def handle_callback_ru(callback_query: types.CallbackQuery):
            ...
    
    # main.py
    
    dp = ...  # создаёшь диспетчер бота
    import ru
    ru.setup(dp)  # регаешь обработчики из ru
    ...  # запускаешь бота


    Ответ написан
  • Что я сделал не так?

    Vindicar
    @Vindicar
    RTFM!
    В-нулевых, пиши симптомы ошибки.
    Во-первых, simpleDividers(n)[len(simpleDividers(n))+1]
    Тебе что, палец отрезают за каждую использованную локальную переменную?
    Зачем два раза вычислять simpleDividers(n)? Почему не закинуть результат в переменную?
    Во-вторых, ты пытаешься обратиться к элементу списка с номером, превышающим его длину (потому что +1).
    Т.е. если у числа три делителя, ты бы обратился к номеру 4. Как по-твоему, это получится сделать?
    В-третьих, ты не возвращаешь answer через return при возврате из simpleDividers(), а код ниже написна так, словно ты его возвращаешь.
    Ответ написан
    Комментировать
  • Почему python возвращает существующий объект, вместо создания нового?

    Vindicar
    @Vindicar
    RTFM!
    А разгадка проста - сборщик мусора.
    Созданный тобой set() уничтожается сразу же после выхода из функции id(), так как на него не остаётся ссылок.
    Следующий set() создаётся по свежеосвобождённому адресу, а потому имеет тот же самый id().

    Ну и да, set() не является hashable(), а потому не может быть ключом в словаре.

    sets = [set() for _ in range(3)]
    for s in sets:
        print(id(s))

    Этот код выведет разные id(), потому что ссылки на объекты остаются в списке sets(), а значит, новые множества создаются не там же, где старые.
    Ответ написан
    Комментировать
  • Как запустить программу в одной консоле, но чтобы выполнение происходило в другой?

    Vindicar
    @Vindicar
    RTFM!
    Можно обойтись без промежуточного cmd.exe если использовать модуль subprocess, и передать при создании дочернего процесса флаг subprocess.CREATE_NEW_CONSOLE (доступен только на винде).
    Но тебе всё равно придётся вынести часть кода в дочерний процесс, так как один процесс может иметь только одно окно консоли.

    Лучше подумай, не стоит ли использовать GUI. Если очень хочется консольку, то можно создать подобие GUI в ней, используя стандартный модуль curses. Проблема в том, что curses - либа под unix, так что надо искать порт под винду.
    Например, этот пакет добавляет поддержку винды в стандартный модуль curses, делая программу более переносимой.
    Ответ написан
    Комментировать
  • Как создать зависящие от времени функции не останаливая работу программы на pygame?

    Vindicar
    @Vindicar
    RTFM!
    У тебя должна быть фиксированная частота обновления логики игры.
    Тогда ты сможешь любые задержки выражать в числе кадров, которое должно пройти, прежде чем можно выполнить следующее действие. Соответственно у каждого действия есть счётчик кадров, если он не 0 - действие на кулдауне и выполнять его нельзя. Каждый кадр уменьшаешь ненулевые счётчики на 1.

    Чуть более тонкий подход - хранить время, оставшееся до истечения кулдауна. На каждом кадре измерять, сколько времени прошло с прошлого кадра, и вычитать этот интервал. Точность всё равно будет порядка одного кадра, но хотя бы при тормозах кулдауны не будут замедляться.

    Заодно почитай, что такое автомат состояний. Пригодится.
    Ответ написан
    Комментировать
  • Как предотвратить двойные нажатия?

    Vindicar
    @Vindicar
    RTFM!
    Вопрос на засыпку: если объект-кнопка обработал событие, остальные объекты его тоже получат?
    Если да, то именно такое поведение и будет наблюдаться. Надо как-то пометить, что событие уже было обработано элементом интерфейса. Либо изъять событие из очереди, либо как-то дать знать новому экрану, что события обрабатывать не надо.

    Альтернативно, вызывай коллбэк только если элемент зафиксировал и нажатие и отпускание кнопки без выхода курсора за пределы элемента. Тогда "остаточного" события будет недостаточно для активации кнопки.
    Ответ написан
    Комментировать
  • Полезность моделей памяти которые отличный от linear?

    Vindicar
    @Vindicar
    RTFM!
    Ты придумал защищённый режим x86. Селектор+смещение, да, да.
    На практике ОС создавали два селектора размером со всё адресное пространство, а для реального управления использовали только страничный механизм, потому что размер таблицы селекторов был ограничен, да и просто муторная это штука. В итоге в длинном режиме (x64) остался только страничный механизм.
    Ответ написан
    Комментировать