• Как сделать так, чтобы бот телеграмм пересылал данные, которые ввел пользователь в другой аккаунт?

    Vindicar
    @Vindicar
    RTFM!
    Смотри в сторону Finite State Machine.
    Если коротко, твой бот должен для каждого пользователя помнить, на каком этапе он находится, и обрабатывать события соответственно.
    "Я получил сообщение от Васи. Вася сейчас на этапе ввода имени. Значит, нужно записать текст сообщения как имя, попросить ввести номер, и перевести Васю на этап ввода номера."
    Многие библиотеки для чатботов уже имеют ту или иную реализацию FSM, как раз для реализации таких вот сценариев. Читай документацию на ту библиотеку, которую ты собираешься использовать.
    Если у неё этого нет, или эта реализация тебе почему-то не подходит, то можно попробовать наколхозить её самому. Как я уже говорил, всё, что реально нужно - для каждого пользователя помнить, на каком этапе он находится.
    Ответ написан
    Комментировать
  • Как правильно одновременно запустить двух Telegram ботов одной программой на Python?

    Vindicar
    @Vindicar
    RTFM!
    Ну потому что ты фигню написал.
    th_bot = Thread(target=pyrobot(), args=())
    th_userbot = Thread(target=aiobot(), args=())

    Ты пытаешься запустить в качестве потока значение, возвращаемое функцией pyrobot(). А так как она уходит в цикл и значения не возвращает, то далее ничего не происходит. До вызова конструктора Thread() дело не доходит. С aiobot() аналогично.

    Еще раз:
    pyrobot() - вызов функции
    pyrobot - ссылка на функцию

    EDIT:
    Оба бота - асинхронные на базе asyncio, им для работы нужен цикл-реактор (event loop). Вообще не факт, что хорошая идея запускать их в потоках.
    Тут есть два варианта, сразу даже не скажу, что проще.
    Вариант А, лобовой: каждый бот создаёт своё собственные реактор через asyncio.new_event_loop(), потом задаёт его как текущий для своего потока через asyncio.set_event_loop(loop). Если ботам не требуется взаимодействовать, то это может быть проще. Если требуется... будут проблемы. Два реактора в одной программе - это не хорошо.

    Вариант Б, адекватный:
    И вызов app.run(), и вызов executor.start_polling(dp, skip_updates=True) скорее всего под капотом создают асинхронную функцию (корутину), и запускают её в реакторе. Тогда ты можешь обойтись без потоков, заставив обоих ботов работать на одном реакторе. Нужно будет зарыться в доки, или даже глянуть исходники.

    Например, для пирограмма написано такое:
    When calling this method (app.run()) without any argument it acts as a convenience method that calls start(), idle() and stop() in sequence. It makes running a single client less verbose.

    Т.е. вместо вызова app.run() ты можешь изменить код так:
    async def pyrobot():  # обрати внимание, теперь функция асинхронная!
        print("pyro started")
        @app.on_message(filters.chat("some_chat"))
        async def print_pyrogram():
            print("Pyrogram")
        # это вместо вызова app.run(), как написано в доках.
        await app.start()
        try:
            await app.idle()
        finally:
            await app.end()

    Затем делаешь аналогичный трюк с aiobot(). Нужно посмотреть в доках на аиограм, как именно.

    И потом запускаешь обоих ботов кодом вида...
    asyncio.run(asyncio.gather(pyrobot(), aiobot()))
    Ответ написан
    3 комментария
  • Могу ли я написать дата пак для майнкрафта на Python?

    Vindicar
    @Vindicar
    RTFM!
    Нет. У Майнкрафта свой скриптовый язык.
    Ответ написан
    Комментировать
  • Считывание ключа домофона через NFC?

    Vindicar
    @Vindicar
    RTFM!
    Бесконтактные ключи работают на 125 kHz RFID-метках, а не на NFC.
    Ответ написан
    2 комментария
  • Как составить число n из элементов списка?

    Vindicar
    @Vindicar
    RTFM!
    Я бы решал задачу рекурсивно.
    Найди в списке все числа x, меньшие или равные n, определи их позиции в списке, отсортируй по убыванию числа.
    Если в списке есть x, равное n, ответ найден.
    Иначе перебирай числа по порядку, от больших к меньшим, и для каждого числа x пробуй убрать его из списка, а потом рекурсивно решить задачу для суммы n-x.
    Т.е. что-то типа:
    def compose_sum(numbers: list[int], total: int) -> list[int] | None:
        # ищем индексы потенциальных слагаемых
        indices = [i for i in range(len(numbers)) if numbers[i] <= total] 
        # сортируем по убыванию слагаемых, потом по порядку в списке
        indices.sort(key = lambda i: (numbers[i], i), reverse=True)
        # если нулевой элемент совпадает - мы нашли точную сумму. Прерываем рекурсию.
        if numbers[indices[0]] == total: 
            return [indices[0]]
        for index in indices: # иначе перебираем слагаемые
            numcopy = numbers.copy()
            # копия списка без рассматриваемого слагаемого
            current = numpcopy.pop(index)
            next_indices = compose_sum(numcopy, total - current)
            if next_indices:  # нашли решение, корректируем индексы (так как мы удалили один элемент)
                for i in range(len(next_indices)):
                    if next_indices[i] >= index:
                        next_indices[i] += 1
                return [index] + next_indices # отдаём наше решение "наверх"
            # next_indices пуст/None - решения не нашли, пробуем другой index
        return None # не нашли решения ни для одного index
    Ответ написан
    Комментировать
  • Почему выводит не совсем правильно?

    Vindicar
    @Vindicar
    RTFM!
    Ты забыл последнюю сторону многоугольника.
    p += len_seg(x[-1],y[-1],x[0],y[0])
    Ответ написан
    Комментировать
  • Как реализовать код диаграммы uml с стрелками зависимости и ассоциации?

    Vindicar
    @Vindicar
    RTFM!
    линия и пунктир с треугольной стрелкой самые понятные, они просто наследуют классы

    Неверно. Сплошная стрелка - да, наследует классы. Пунктир - это реализация интерфейса (в терминах Питона это скорее typing.Protocol). Т.е. при реализации один участник (с меткой interface) описывает методы без их реализации, а второй - их реализует.

    линии с ромбиками понятны отчасти, в свойстве класса создают экземпляр другого класса

    Это скорее коллекция и элементы. Разница между агрегацией (пустой ромб) и композицией (закрашенный ромб) простая. В случае композиции элемент не имеет смысла вне содержащей его коллекции. Например, чат и сообщения - если сообщение не может быть вне чата, это композиция. А вот, скажем, контакт и группа контактов - агрегация, так как контакт может рассматриваться и иметь смысл вне группы, в которую он входит.
    И агрегация, и композиция - это частные случаи ассоциации. В случае ассоциации одному или нескольким экземплярам одного класса сопоставляется один или несколько экземпляров другого класса. Например, у сообщений в любом чате есть автор - это ассоциация один-ко-многим.

    Зависимость - это когда один объект использует другой (имеет ссылку на другой), но в то же время их экземпляры не сопоставлены, т.е. связь между ними не имеет особого смысла с точки зрения решаемой задачи.
    Пример: окно, где показывается чат, и сам чат. Окно знает, какой чат оно показывает. В то же время одно окно может использоваться для разных чатов в разное время. Аналогично, один чат может показываться в разных окнах. Т.е. связь окно-чат не имеет какого-то особенного смысла. Сравни это со связью сообщение-автор.
    Ответ написан
  • Как импортировать с файла функцию, но чтобы этот файл не запускался?

    Vindicar
    @Vindicar
    RTFM!
    Заверни тело файла (непосредственный вызов функций) в конструкцию
    if __name__ == '__main__':
    Этот if выполнится, только если файл запущен непосредственно, и не выполнится, если он импортирован.
    Ответ написан
    Комментировать
  • Как получить колонку по ее значение?

    Vindicar
    @Vindicar
    RTFM!
    Короче.
    Основную идею я тебе дам, но всё равно почитай уже любой учебник!

    Аналогия очень простая.
    Строка таблицы = структура данных, типа класса. Таблица = коллекция однотипных структур данных.
    Столбец - поле в структуре данных. Схема таблицы - описание структуры данных, т.е. набора полей.
    Таким образом, изменение схемы таблицы - изменение состава структуры данных - это НЕ обычная операция.
    Её не нужно использовать для добавления новых данных в коллекцию! Если ты добавляешь новые данные в коллекцию - это добавление строки, т.е. INSERT.

    "А что делать, если у меня переменный набор данных? Например, есть чаты, и в них есть юзеры?"
    У тебя несколько сущностей - в таких случаях тебе нужно несколько таблиц. Ключевой момент - сколько сущностей связано с той или иной сущностью.
    Например, у нас есть чаты, и есть пользователи. Пользователь может быть в нескольких чатах, чат может иметь нескольких пользователей. Это называется "связь многие-ко-многим". Если сделать так, как ты делаешь сейчас, то будет таблица Чаты, и при добавлении пользователя в чат будет добавляться столбец в таблицу. Это неправильно!
    Правильно: есть таблица Чаты (каждый чат имеет свой id - "ключ", "primary key"), таблица Пользователи (каждый имеет свой ключ) и вспомогательная таблица (association table) ПользовательВЧате.
    В виде SQL это будет выглядить примерно так:
    CREATE TABLE Chats (id INTEGER PRIMARY KEY, name TEXT, topic TEXT);
    CREATE TABLE Users (id INTEGER PRIMARY KEY, name TEXT);
    CREATE TABLE UserInChat(
        chat_id INTEGER, 
        user_id INTEGER, 
        FOREIGN KEY (chat_id) REFERENCES Chats(id),
        FOREIGN KEY (user_id) REFERENCES Users(id),
        PRIMARY KEY (chat_id, user_id)
    );

    Что это нам даёт?
    - Наличие записи в UserInChat означает, что пользователь находится в чате.
    - Каждая запись в UserInChat ссылает на существующего пользователя и на существующий чат.
    - Сочетание пользователь-чат уникально, т.е. пользователь не может войти в один чат дважды (одновременно).
    - В то же время, могут быть записи с одним и тем же пользователем (один пользователь в нескольких чатах).
    - Могут быть записи с одним и тем же чатом (много пользователей в одном чате).

    Как узнать, какие пользователи находятся в чате? Используем запрос с JOIN.
    SELECT Users.id as userid, Users.name as username
    FROM Users INNER JOIN UserInChat ON Users.id = UserInChat.user_id
    WHERE UserInChat.chat_id = 123456;


    Для добавления пользователя в чат делаем INSERT INTO в таблицу UsersInChat. Для удаление - DELETE FROM. Тут всё как обычно.
    Ответ написан
  • Почему выдает ошибку Cannot choose from an empty sequence?

    Vindicar
    @Vindicar
    RTFM!
    visit = random.choice(urls)
    У тебя в urls пусто на момент вызова функции.

    urls = []  # в urls пусто
    
    # тут ты только определяешь функцию, но не вызываешь её
    def createlist(ids):
        global urls
        for id in ids:
            urls.append("https://scrap.tf/raffles/" + id)
    # так что тут urls всё ещё пуст
    # внутри login() вызывается rufflejoin(). А urls всё ещё пуст.
    login()
    Ответ написан
    Комментировать
  • Как отправлять текст соответствующий картинке в телеграм боте на Python?

    Vindicar
    @Vindicar
    RTFM!
    1. вынеси random.choice(os.listdir('test')) в переменную, например, img
    2. используй os.path.splitext(os.path.basename(img))[0], чтобы получить имя файла картинки без расширения
    3. Не забудь отправить файл через photo = open('test/' + img, 'rb')
    4. PROFIT
    Ответ написан
    2 комментария
  • Как сделать актуализацию данных?

    Vindicar
    @Vindicar
    RTFM!
    Используй on_conflict. Он позволяет как молча проигнорировать втавляемые данные, так и частично обновить существующую строку.
    Причем достаточно конфликта по ключу (id пользователя).

    Собственно, это хорошо работает, если тебе нужно просто хранить в базе всех, кто обращался к твоему боту.
    Если же тебе нужна именно своя регистрация (а тут надо дважды подумать - нужна ли? может, нафиг её?), то тогда сначала проверяй вручную, прошёл ли пользователь регистрацию.
    Ответ написан
    Комментировать
  • Как запускать Process в скриптах которые импорируются?

    Vindicar
    @Vindicar
    RTFM!
    AttributeError: Can't pickle local object 'start.<locals>.f1'

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

    Vindicar
    @Vindicar
    RTFM!
    File -> Close Project. Закроет проект и покажет меню выбора проекта.

    Также есть настройка, Settings > Appearance & Behaviour > System Settings > Reopen projects on startup
    Ответ написан
    5 комментариев
  • Как в Python проверять, в каких скриптах какие ресурсы, импортируемые из других скриптов, используются?

    Vindicar
    @Vindicar
    RTFM!
    Ну вот тут ты столкнулся с проблемой изоляции изменений.
    Обозначим код, который предоставляет какую-то услугу, сервисом, а код, который эту услугу использует - клиентом.
    Тогда есть два типа изменений: изменение реализации сервиса и изменение интерфейса сервиса.

    Первое изменение затрагивает сервис, но необязательно затрагивает клиента. Если клиент обращается к сервису таким же образом, получает результат в таком же виде, получает сведения об ошибках таким же способом, то с точки зрения клиента изменения не было. Пусть даже в реальности сервис переписали с нуля.
    Пример: пусть у нас есть функция сортировки списка "на месте", т.е. изменяя этот список.
    def sort_list(original: list):
    Если раньше она сортировала список пузырьком, а потом мы её переписали на quicksort - с точки зрения кода, который эту функцию использует, ничего не изменится, кроме времени работы функции. Клиент все равно будет использовать функцию так:
    some_list = [ ... ]
    sort_list(some_list)


    Второй тип изменений меняет интерфейс сервиса, т.е. то, как он взаимодействует с клиентом.
    Продолжая пример, если мы решили, что теперь функция будет возвращать отсортированную копию списка, не трогая исходный список:
    def sort_list(original: list) -> list:
    Это изменение сломает клиентский код, так как ему теперь придётся использовать функцию так:
    some_list = [ ... ]
    some_list = sort_list(some_list)

    Т.е. наше изменение сломало обратную совместимость: старый код не может работать с новыми версиями.

    Если быть осторожным, то можно внести изменение в интерфейс, не сломав обратную совместимость. Например, если мы решили научить нашу функцию сортировать не только по возрастанию, но и по убыванию:
    def sort_list(original: list, inverse: bool = False):

    Тогда изначальный способ вызова sort_list(some_list) по прежнему будет работать. Мы расширили старый интерфейс, а не заменили его.

    Так вот, к чему я всё это говорю: умные IDE, вроде PyCharm, могут находить случаи использования того или иного элемента в рамках одного проекта (!). Но эта возможность очень ограничена, так как всегда можно так запутать код, что никакая IDE не разберётся.
    Поэтому лучше заранее продумывать, как клиентский код будет обращаться к услугам твоих сервисов, чтобы не пришлось переписывать интерфейс, а только реализацию.
    Ответ написан
    Комментировать
  • Бот рандомно выбирает вопрос, но проверяет по ответам для 1 вопроса, как исправить?

    Vindicar
    @Vindicar
    RTFM!
    if x == 1:
        @bot.message_handler(content_types="text")
        def send(message):
            ...

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

    1. Должна быть только одна функция, декорированная как @bot.message_handler(content_types="text"). Если их несколько, отработате только одна.

    2. Ты должен хранить сведения:
    - Какой вопрос был последним задан тому или иному пользователю
    - Какие вопросы были уже заданы тому или иному пользователю
    В рамках обучения их можно хранить в словаре, но в реальном боте потребуется постоянное хранилище (например, БД), в котором эти сведения пережили бы перезапуск бота

    3. Внутри обработчика @bot.message_handler ты должен получить ID пользователя, отправившего сообщение, взять из описанного выше хранилища номер последнего заданного вопроса (если есть), и уже на основании этого номера судить о том, правильный ли ответ.
    Ответ написан
  • Как склеить программно 2 изображения?

    Vindicar
    @Vindicar
    RTFM!
    Ну базовый вариант, если используешь OpenCV, ориентируйся на поиск локальных особенностей, а потом findHomography(), чтобы найти такое преобразование, которое переведёт точку на первом кадре в эквивалентную точку на втором. После этого warpPerspective(), чтобы преобразовать первый кадр в систему координат второго. Это должно совместить общие точки.

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

    Vindicar
    @Vindicar
    RTFM!
    Для начала скажи, какую задачу ты пытаешься решить.
    Для случая с одноразовым потоком-воркером, тебе не нужна глобальная переменная. Достаточно класса.
    import threading
    
    class MyWorkerThread(threading.Thread):
        def __init__(self, arg1: float, arg2: float): # передаём потоку входные данные 
            # поток не должен их менять!
            super().__init__()
            self.arg1 = arg1
            self.arg2 = arg2
            self.result: t.Optional[float] = None
        def run(self):
            time.sleep(10) # имитируем длительную работу
            self.result = self.arg1 + self.arg2

    И тогда ты можешь его использовать так:
    worker = MyWorkerThread(42, 69)
    worker.start()
    while True: 
        if worker.is_alive():  # проверяем, жив ли поток
            # делаешь ещё что-то, пока поток работает
            print('Still working...')
            time.sleep(0.5)
        else:
            # поток завершился, даём знать пользователю.
            print(f'Done! Result is {worker.result}!')
            break # выходим из цикла

    Если же тебе нужно просто дождаться конца потока, ничего не делая в процессе, можно сделать просто worker.join()

    Сложности начинаются, когда тебе нужно взаимодействовать с длительным потоком. Усложним пример:
    import threading, queue
    
    class MyWorkerThread(threading.Thread):
        def __init__(self, arg1: float, arg2: float): # передаём потоку входные данные 
            # поток не должен их менять!
            super().__init__()
            self.arg1 = arg1
            self.arg2 = arg2
            self.result: t.Optional[float] = None
            self.progress = queue.Queue()
        def run(self):
            for i in range(10):
                time.sleep(1) # имитируем длительную работу
                self.progress.put(i/10) # сообщаем о прогрессе
            self.result = self.arg1 + self.arg2
            self.progress.put(1.00)

    Тогда код использования изменится следующим образом:
    worker = MyWorkerThread(42, 69)
    worker.start()
    while True: 
        if worker.is_alive():  # проверяем, жив ли поток
            # делаешь ещё что-то, пока поток работает
            try:
                progress = worker.progress.get(block=True, timeout=0.5)
            except queue.Empty: # поток ничего не сообщил
                print('Still working...')
            else:
                print(f'Still working... {progress:.0%}')
                worker.progress.task_done() # один вызов task_done() на один успешный вызов get()!
        else:
            # поток завершился, даём знать пользователю.
            print(f'Done! Result is {worker.result}!')
            break # выходим из цикла

    Если тебе нужно передать потоку новые задания, то можешь использовать ещё одну queue.
    Ответ написан
    Комментировать
  • Как выполнять функцию каждую минуту в определенный интервал времени aioschedule?

    Vindicar
    @Vindicar
    RTFM!
    Вариант А. Выполнять каждые 5 минут, внутри функции проверять текущее время. Ничего не делать, если время не в заданном интервале.

    Варинат Б. Запланировать две функции. Одна, в 10:00, запустит через asyncio.create_task() простую зацикленную функцию, которая будет вызывать твою функцию, а потом делать await asyncio.sleep(300). Ссылку на задачу, возвращённую create_task(), надо будет сохранить в глобальную переменную.
    Другая функция, в 14:00, остановит эту задачу.

    Вариант А проще, и надёжнее в плане ситуации, когда программа запущена в промежуток между 10:00 и 14:00.
    Ответ написан
  • Multiprocessing.Process OSError: [WinError 87] Параметр задан неверно, что делать?

    Vindicar
    @Vindicar
    RTFM!
    Ну так тебе же говорят:
    _pickle.PicklingError: Can't pickle <function <lambda> at 0x00000122F23CEB00>: attribute lookup <lambda> on __main__ failed

    Для передачи данных в соседний процесс они сериализуются через модуль pickle. А сериализовываться так может далеко не всё.
    Если тебе нужно скинуть в соседний процесс код... это проблема. Может, получится скинуть словарь с локальными переменными, строку с кодом, и вычислить эту строку через eval() уже "на месте".
    Альтернативно, просто замени лямбду на нормальную функцию, и попробуй передать либо её, либо имя этой функции, чтобы дочерний процесс сам её взял из globals() или еще откуда.
    Ответ написан
    Комментировать