Задать вопрос
  • Как распарсить GotoIfTime из астериска?

    Vindicar
    @Vindicar
    RTFM!
    Регулярное выражение вида
    GotoIfTime\((?P<from>\d+:\d+)-(?P<to>\d+:\d+),(?P<days>[a-z-]+),\*,\*\?open,s,1\)
    извлечёт из строки нужные части. Их можно будет вытащить так:
    regexp = re.compile(r'GotoIfTime\((?P<from>\d+:\d+)-(?P<to>\d+:\d+),(?P<days>[a-z-]+),\*,\*\?open,s,1\)', re.I)
    m = regexp.match('GotoIfTime(09:00-17:59,mon-fri,*,*?open,s,1)')
    if m:
        print(m.group('days'), m.group('from'), m.group('to'))

    Для работы с датами datetime ну и просто работа со строками. C днём недели можно будет справиться как-то так:
    now = datetime.datetime.now()
    weekday = now.weekday()
    days = m.group('days').lower()
    days_of_week = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']  # datetime.datetime.weekday()
    if '-' in days:  # диапазон
        dfrom, _, dto = days.partition('-')
        dfrom, dto = days_of_week.index(dfrom), days_of_week.index(dto)
        if dfrom <= dto:  # обычный кейс, вроде mon-fri
            day_is_good = dfrom <= weekday <= dto
        else:  # на случае если нужно зацикливание вида fri-tue
            day_is_good = (weekday >= dfrom) or (weekday <= dto)
    else:  # один день, не диапазон
        dow = days_of_week.index(days)
        day_is_good = weekday == dow
    print(day_is_good)  # True или False


    С временем тоже несложно, разбиваешь через partition() по двоеточию, преобразуешь в целое, потом делаешь datetime.datetime.now().replace(hour=h, minute=m) и получаешь указанное время в текущий день.

    Таким образом у тебя будет два момента времени, один на базе from другой на базе to. Их можно просто сравнить с текущим: if from <= now <= to:
    Ответ написан
  • Как добавить кнопки в бота, через телеграм в Aiogram?

    Vindicar
    @Vindicar
    RTFM!
    Гипотетически можно, но практически тяжело. Объясню почему.

    Если ты хочешь добавить в бота новую функциональность, то это возможно при выполнении одного из двух условий:
    1. Новая функциональность по сути совпадает со старой, но работает с другими параметрами. Например, раньше парсили один сайт, теперь два. Пробелма в том, что для этого нужно заранее проектировать старую функциональность так, чтобы она была гибкой. Иными словами, если у тебя в коде намертво прописан путь, по которому нужно вытаскивать с сайта информацию, то без правки кода не получится ничего изменить. И даже если ты это предусмотришь, всё равно будут пределы этой гибкости.

    2. Ты можешь загрузить в бота код, реализующий новую функциональность. Для этого нужно будет:
    - разбить бота на изолированные компоненты, по типу Cogs из библиотеки discord.py.
    - предусмотреть механизм подгрузки файла с компонентом в бота "на ходу".
    - предусмотреть механизм выгрузки компонента из бота "на ходу", что намного сложнее. Это потребуется, если ты захочешь заменить компонент обновлённой версией, не перезапуская бота.
    - предусмотреть механизм, позволяющий тебе отдать боту файл с кодом, чтобы тот поместил этот файл к остальным компонентам.
    Это всё нетривиальные задачи, которые требуют очень хорошего понимания как Питона, так и устройства библиотеки aiogram. Собственно, беглый взгляд на доки показывает, что aiogram вообще не предусматривает удаления обработчиков событий. А это значит что единственный способ выгрузить функциональность из бота - это его перезапуск.
    Ответ написан
    Комментировать
  • Как сделать вывод определённого количества столбцов?

    Vindicar
    @Vindicar
    RTFM!
    Читай про операторы LIMIT и OFFSET у SQL-запроса.
    Если размер страницы N записей, то для выдачи страницы i тебе нужно будет задать OFFSET (i - 1) * N LIMIT N.
    Разумеется, нужно также задать порядок сортировки записей через ORDER BY.
    Ответ написан
    Комментировать
  • Как сделать чтобы программа ждала завершения bat-файла?

    Vindicar
    @Vindicar
    RTFM!
    Если дело в батнике, то можно запускать программу через start /wait program.exe
    Ответ написан
    Комментировать
  • Как создать клавиши с названиями из списка?

    Vindicar
    @Vindicar
    RTFM!
    async def choice_cat(message):
        CATs = cur.execute(f'SELECT name FROM cathlete WHERE id = "{message.from_user.id}"').fetchall()
        for fds in range(len(CATs)):
            astt = ReplyKeyboardMarkup(resize_keyboard=True).add(KeyboardButton(f"CATs[{fds}]"))


    Код, конечно, жесть. По пунктам:
    1. НИКОГДА не генерируй SQL запрос через форматирование строк. Легко словить ошибку, а если очень не повезёт - SQL-инъекцию. Используй подстановку параметров (ссылка для sqlite, если у тебя другая БД - ищи доки на неё).
    2. Как насчёт проверить, а вернула ли БД хоть что-то? Это не гарантируется. Всегда ожидай подставы.
    3. Зачем итерироваться по индексу списка, если ты индекс не используешь? Почему не итерироваться по самому списку? И да, это абсолютные основы питона, которые НАДО знать задолго до того, как браться за ботов.
    4. Зачем ты создаёшь новую клавиатуру на каждой итерации цикла? Тут уже даже не знания языка, тут простая логика. Создал клавиатуру, добавил кнопку - на следующей итерации выбросил старую клавиатуру, создал новую добавил другую кнопку. Ну бессмыслица же! Создай клавиатуру один раз, до цикла, и добавляй кнопки к ней.
    5. Ну тот факт, что ты не посылаешь сообщение, к которому будет прикреплена клавиатура (а сама по себе она не бывает!), это уже мелочи. Видимо, не дошёл ещё до этого этапа.

    kbd = ReplyKeyboardMarkup(resize_keyboard=True)
    btns = cur.execute(f'SELECT name FROM cathlete WHERE id = ?', (message.from_user.id,)).fetchall()
    # btns - список списков. 
    if not btns:
        # кнопки не предусмотрены, как-то даём пользователю знать об этом
        return  # если это фатально, останавливаемся тут
        # иначе создаём какое-то значение btns по умолчанию и  продолжаем
        # тут уж сам решай
    # делаем клавиатуру
    for btn in btns:
        kbd.add(KeyboardButton(btn[0]))
    # ну и отправляем её вместе с сообщением
    Ответ написан
    7 комментариев
  • Как сделать ограничения в бд?

    Vindicar
    @Vindicar
    RTFM!
    Если БД позволяет, можно сделать триггер на вставку записи, который не даёт вставить более 5.
    Но в твоём случае решение кодом будет понятнее, а потому предпочтительнее.
    Ответ написан
    Комментировать
  • Как сплитовать с aiohttp?

    Vindicar
    @Vindicar
    RTFM!
    1. Прочитать ответ из объекта ClientResponse, а не пытаться использовать его как есть.
    2. Для разбора URL на кусочки есть стандартный модуль urllib.parse
    Ответ написан
    Комментировать
  • Нужно спарсить номера телефонов, но они появляются полсе нажатии на кнопку, как их спарсить?

    Vindicar
    @Vindicar
    RTFM!
    а) разобраться, как сайт получает номер телефона, и повторить. Если номер подгружается отдельным запросом, выяснить каким, попробовать сделать запрос самому. Если номер спрятан в теле страницы, выяснить где, как он зашифрован, извлечь. расшифровать. Муторно, но требует меньше ресурсов.
    б) вместо requests использовать безголовый браузер (selenium), имитировать клик на кнопку, пусть сайт сам всё за нас сделает. Легче, но такие вещи прожорливы по памяти и CPU.
    Ответ написан
    1 комментарий
  • Как получить значение из словаря, не зная имени ключа?

    Vindicar
    @Vindicar
    RTFM!
    1. Сформулируй, по какому критерию определять нужное значение
    2. Перебирай значения в словаре, пока не найдешь соответствующее критерию
    Ответ написан
  • Как упростить мой код?

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

    Vindicar
    @Vindicar
    RTFM!
    а) написать systemd unit
    б) написать init.d скрипт
    в) вроде крон умеет делать запуск с условием @reboot, но тут есть баги
    г) если включение = логин пользователя, то профиль-скрипт этого пользователя тоже подойдёт
    Ответ написан
    Комментировать
  • Как можно решить ошибку?

    Vindicar
    @Vindicar
    RTFM!
    Я же тебе уже говорил: если нет ответа на сообщение, то message.reply_to_message будет иметь значение None, и взять у него атрибут from_user не получится (о чём и говорит ошибка).
    Проверяй, что message.reply_to_message не None, если это не так, то думай - или подставляй какое-то другое значение, или просто сообщай об ошибке.
    Ответ написан
  • Как вставить видео в картинку?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала уточни, что ты хочешь получить в результате. Видео с рамкой? А тебе точно для этого нужна своя программа? Утилита ffmpeg может с этим справиться, хотя и муторно будет.
    Если очень хочешь сам, то пакет opencv-python в помощь, смотри классы cv2.VideoCapture() и cv2.VideoWriter(), ну и базовые операции преобразования изображения заодно.
    Потом может потребоваться перекодировать полученное видео тем же ffmpeg'ом.
    Ответ написан
  • Как создать сообщение об ошибке в написании команды?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала, неплохо бы писать в вопросе сообщение об ошибке.
    Я, конечно, догадываюсь, что дело в обращении к message.reply_to_message.username, и догадываюсь, что message.reply_to_message будет иметь значение None, если сообщение не является ответом... но я могу и ошибаться.

    А если я прав, то проверяй, является ли обрабатываемое сообщение ответом, а потом уже пытайся читать сведения об "отвеченном" сообщении.
    Ответ написан
    Комментировать
  • Возможно ли реализовать запрет скачивания сайта через Flask?

    Vindicar
    @Vindicar
    RTFM!
    Отключить сайт.

    Ты можешь прибегать к разным хитростям, конечно.
    - проверка User-Agent и Referer.
    - проверка на исполнение скриптов
    - browser fingerprinting, т.е. попытка выяснить, что за браузер у клиента по его особенностям реализации

    Но в конечном итоге это будет гонка "кто лучше знает поведение реального браузера". Гонка скорее всего закончится, когда потенциальный скрапер будет дёргать твой сайт селениумом.

    Можешь попробовать зайти с другой стороны, конечно, и требовать двухфакторную авторизацию для входа. Но и это автоматизируется, а вот пользователей отпугнёшь.
    Ответ написан
    Комментировать
  • Как преобразовать значение при использовании dataclass?

    Vindicar
    @Vindicar
    RTFM!
    Я в таких случаях создаю фабричный метод.
    @dataclass
    class Person:
        first_name: str
        last_name: str
        bdate: date
    
        @classmethod
        def make(cls, first_name: str, last_name: str, bdate: str) -> 'Person':
            _bdate = datetime.strptime(bdate, '%Y%m%d').date()
            return cls(first_name=first_name, last_name=last_name, bdate=_bdate)
    
    data = {
        'first_name': 'Adam', 
        'last_name': 'Smith', 
        'bdate': '20220617'
    }
    person = Person.make(**data)

    Просто, коротко, позволяет реализовать любую логику приведения типов и вычисления значений по умолчанию, использует базовые механизмы Питона, при необходимости можно проигнорировать и использовать обычный конструктор (который мы не ломаем).

    Но вообще это неправильное распределение обязанностей. Обязанность датакласса - хранить данные, а не менять их представление. За смену представления пусть отвечает тот код, который получает значение строки.
    Ответ написан
    Комментировать
  • Ошибка - name 'self' is not defined. Как исправить?

    Vindicar
    @Vindicar
    RTFM!
    Декоратор отрабатывает при создании собственно класса. На этот момент никаких экземпляров ещё нет.
    self имеет смысл только в контексте тела метода класса, но не "снаружи" методов.

    Так что стоит задуматься, а что именно ты пытаешься сделать. Есть ли смысл в твоём классе-обёртке?

    А если прямо очень надо, можно вспомнить, как работает декоратор, и что его можно вызывать вручную, например, в конструкторе класса.
    @some_decorator_with_params(params)
    def some_func(...):
        ...
    # это всего лишь синтаксический сахар для
    def some_func(...):
        ...
    
    decorator = some_decorator_with_params(params)
    some_func = decorator(some_func)  # вызываем декоратор вручную
    Ответ написан
    Комментировать
  • Почему Telegram бот не хочет отправлять сообщение в котором содержатся кнопки?

    Vindicar
    @Vindicar
    RTFM!
    Дай угадаю, реагирует на /start, и всё.
    Сравни с официальным примером и подумай.
    Подумал?

    - get_mood() не отмечено декоратором @bot.message_handler(), бот про эту функцию ничего не знает. Он знает только про welcome().
    - если не заметил, клавиатура должна отправляться вместе с сообщением, параметром reply_markup. Ты же отправляешь сообщение, потом создаёшь клавиатуру, и далее ничего с ней не делаешь.
    Ответ написан
    Комментировать
  • Ошибка Disnake.py, кто поможет?

    Vindicar
    @Vindicar
    RTFM!
    Ну тебе же английским по белому написали: у объекта класса ApplicationCommandInteraction нет атрибута message. Если залезть в доки, его и впрямь нет. А ты зачем-то дергаешь inter.message.author. Если тебе нужен отправитель команды, то у объекта inter есть такое поле author. Почитай по ссылке.
    Ответ написан
    4 комментария
  • Почему regex работает некорректно?

    Vindicar
    @Vindicar
    RTFM!
    Упрощённый пример: что сматчит "\d+" в строке "123456"? Очевидно, всё - потому что умолчанию квантификаторы + и * жадные, т.е. пытаются сматчить как можно больше. У тебя .* - т.е. "матчи всё подряд, сколько получится", а ограничивается только тем, что "дальше должна идти запятая и пробел". Обрати внимание на ещё одну запятую с пробелом в конце твоей строки data.
    Что делать?
    а) сделать квантификатор не жадным: type=(.*?), Тогда он будет пытаться сматчить как можно меньше.
    б) уточнить, какие именно символы могут захватываться группой type=([a-zA-Z0-9_]+),
    Ответ написан
    Комментировать