Ответы пользователя по тегу Python
  • Как подключить скрипт для определенного блока кода в пайтоне?

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

    Я думаю, схема будет такая:
    1. При входе на целевую страницу, JS генерирует AJAX запрос с помощью fetch() или XMLHttpRequest, на твой выбор. Форму запроса (GET/POST) и передаваемые данные определяешь ты сам, но их должно быть достаточно для идентификации пользователя. В простейшем случае URL целевой страницы должен быть уникальным для каждого пользователя, и тогда можно будет передать этот уникальный идентификатор.
    2. Есть небольшой HTTP-сервер, который принимает эти запросы, записывает их в локальную базу данных и даёт отклик вида "успех/неудача". На чем он написан - неважно, но для простоты можно использовать Flask. Там будет строк 100 от силы. В качестве базы можно использовать sqlite.
    3. Есть Python бот, использующий одну из библиотек под телегу, который периодически смотрит в эту базу и проверяет наличие необслуженных запросов. Если таковые находятся, бот их перебирает, определяет для каждого запроса соответствующего пользователя, отправляет файл, и помечает запрос как обслуженный (ну или просто удаляет).
    61ceec5390670627570751.png

    Есть соблазн совместить пункты 2 и 3 в одном процессе, но это может оказаться нетривиально - тебе придётся крутить работу веб-сервера в главном потоке, а работу бота - в дополнительном. Тогда вместо базы данных можно будет использовать очередь (threading.Queue) - главный поток (Flask) кладёт задания в очередь, а поток бота - выбирает и обслуживает.
    61ceedcee35a5542427561.png
    Плюсы - один Python-скрипт вместо двух, отсутствие промежуточной БД.
    Минусы - повышенная сложность скрипта, при внезапной остановке скрипта текущие задания в очереди будут потеряны, труднее вести статистику обслуживания ботом пользователей.
    Решай сам, стоит ли оно того.
    Ответ написан
    7 комментариев
  • Сортировка словаря по нескольким значениям с условием по time.time()?

    Vindicar
    @Vindicar
    RTFM!
    Ну во-первых, обычные словари сортировке не особенно поддаются. Порядок перебора ключей в словаре не определён, если я не путаю - нет гарантии, что на разных машинах, или на разных версиях питона, или даже при разных запусках скрипта порядок будет одним и тем же.
    Есть, правда, collections.OrderedDict - вот они запоминают порядок ключей, но это порядок всегда хронологический - самый поздний заданный ключ всегда будет последним при переборе.
    Во-вторых, лучше декомпозируй задачу на две. Сначала отфильтруй нужное, потом уже сортируй. Делать всё за один проход трудно, да и нужно ли?

    Я бы вообще посоветовал временно представить словарь как список кортежей (ключ, значение), и фильтровать/сортировать этот список.
    Ответ написан
  • Подскажете аналог set(), но для словаря?

    Vindicar
    @Vindicar
    RTFM!
    Даже не знаю с чего начать.
    Во-первых, массивы в питоне - это array, и они используются нечасто. Я сильно подозреваю, что ты имел ввиду списки (list).
    Во-вторых, set() к спискам отношения не имеет, это абсолютно самостоятельная структура данных - множество. От того, что есть идиома без_повторов = list(set(с_повторами)) set() не становится методом списка или чем-то подобным. Эта идиома прекрасно работает с любым коллекциями - списками, кортежами (tuple), да с чем угодно.
    В-третьих, повторы чего ты хочешь устранить? Ключей? Значений?
    Если ключей, то в словарях ключи и так не повторяются, как уже написали выше. Повторяющийся ключ просто заменит собой старый.
    Если значений, то тебе сначала придётся решить, какой из ключей с одинаковым значением сохранить.
    Ответ написан
    Комментировать
  • Как узнать индекс динамического виджета tkinter?

    Vindicar
    @Vindicar
    RTFM!
    Есть два подхода. Один основан на лямбдах:
    for i in range(2):
        buttonFrame_update=Button(frame,text="Добавить", command= lambda arg=i: update_main_db(arg))

    Приём с аргументом лямбды необходим, чтобы сохранить текущее значение i - иначе в момент вызова лямбды она прочитает последнее значение i, а оно будет указывать на последнюю строку.

    Второй способ, которя я бы выбрал - написать свой виджет - строку таблицы. Тогда обработчик нажатия на кнопку сможет брать данные из экземплярной переменной.
    class MyTableRow(Frame):
        def __init__(self, master, *args, **kwargs):
            super().__init__(master, *args, **kwargs)
            self.widgets = []
            for i in range(3):
                widget = Entry(frame)
                widget.pack(side="left")
                self.widgets.append(widget)
            self.update_btn = Button(self, text = "Добавить", command = self.update_clicked)
            self.update_btn.pack(side="right")
            self.delete_btn = Button(self, text = "Удалить", command = self.delete_clicked)
            self.delete_btn.pack(side="right")
      
        def update_clicked(self):
            print(self.widgets[0].get())
        
        def delete_clicked(self):
            print("whatever")
    
    
    for i in range(2):
        item = MyTableRow(doth, borderwidth=2, relief="groove")
        frames.append(item)
        item.pack(side="top", fill="x")
    Ответ написан
    5 комментариев
  • Не свосем юзербот на discord.py?

    Vindicar
    @Vindicar
    RTFM!
    Нельзя. И хорошо что нельзя.

    Можно подставить имя пользователя в начало текста, но всё равно будет видно, кто писал на самом деле.
    Ответ написан
    Комментировать
  • Защита софта на Python?

    Vindicar
    @Vindicar
    RTFM!
    Вопрос вообще "как идентифицировать компьютер". И это нетривиально.
    1. Получить MAC адрес сетевой карты и проверить его на вхождение в список. Простейший способ его получить - примерно такой:
    import uuid
    print(hex(uuid.getnode()))

    * неясно, как поведет себя функция, если на компе несколько сетевых адаптеров.
    * смена сетевой карты сломает программу
    * MAC адрес сетевой карты часто можно сменить
    2. Можно попробовать использовать CPUID, например, так.
    Но я без понятия, какие процессоры это поддерживают!
    3. Можно разместить секретное значение в реестре целевых машин, и проверять его наличие.

    Самая большая проблема - эту проверку может отломать любой, более-менее знакомый с языком.

    Можешь использовать пакет типа pyarmor, он вроде предоставляет механизм создания "лицензий" и обфусцирует исходный код, чтобы его труднее было анализировать и изменить. Но и pyarmor тоже отламывается при желании.

    Так что главный вопрос: а нафига?
    Ответ написан
    2 комментария
  • Как вывести все данные из БД?

    Vindicar
    @Vindicar
    RTFM!
    value, create = Choose.objects.get_or_create(voter=request.user)


    Может, поэтому?
    Ответ написан
    Комментировать
  • Хэш и соль пароля в файл?

    Vindicar
    @Vindicar
    RTFM!
    1. Читаешь из файла соль и хэш пароля.
    2. К введенному паролю добавляешь соль (так же, как она была добавлена к сохранённому) и хэшируешь.
    3. Сравниваешь два хэша - полученный и прочитанный. Если они совпали, пароль верен.
    Ответ написан
    2 комментария
  • Как переметить нулевые значения в конец списка?

    Vindicar
    @Vindicar
    RTFM!
    original_list = [0,1,8,3,0,4,5,0,0] #исходный список
    new_list = [x for x in original_list if x != 0] #только ненулевые элементы
    how_many_zeros =  len(original_list) - len(new_list) #сколько нулей потеряли?
    new_list.extend( [0] * how_many_zeros ) # добавляем нужное количество нулей в конец
    Ответ написан
    1 комментарий
  • Как еще чуть ускорить алгоритм?

    Vindicar
    @Vindicar
    RTFM!
    А не проще ли будет сделать в два прохода.
    Прямым проходом делаем так: если 0, то счетчик в ноль, иначе счетчик + 1 и элемент приравниваем счетчику.
    Потом то же самое делаем обратным проходом, но изменяем элемент только если он больше счетчика.
    Отсюда получаем:
    lst=[5, 3, 0, 2, 0, 3, 8, 2, 9, 7, 0, 0, 7, 1, 5, 3]
    L = len(lst)
    # если в начале списка не 0, то мы сможем задать корректные значения только на обратном ходе
    # так что ставим заведомо большее значение счетчика
    counter = len(lst) if lst[0] != 0 else 0
    for i in range(0, L):
      if lst[i] == 0:
        counter = 0
      else:
        counter += 1
        lst[i] = counter
    # обратный ход
    counter = lst[-1] - 1 #чтобы не запороть последние элементы
    for i in range(L-1, -1, -1):
      if lst[i] == 0:
        counter = 0
      else:
        counter += 1
        lst[i] = min(counter, lst[i])
    
    print(lst)
    # [2, 1, 0, 1, 0, 1, 2, 3, 2, 1, 0, 0, 1, 2, 3, 4]

    Правда, это даст некорректный результат, если список не содержит нулей. Но это можно обнаружить в ходе первого прохода.
    Ответ написан
    2 комментария
  • Что то не то в скрипте?

    Vindicar
    @Vindicar
    RTFM!
    Перечитывать файл каждый раз при обработке сообщения - не лучшая идея.

    Вместо этого сделай отдельную команду, которая будет его перечитывать.
    blacklist = []
    def load_stoplist():
        global blacklist #чтобы можно было изменить глобальную переменную
        inputfile = '1.txt'
        # учись как правильно работать с файлами
        with open(inputfile, mode='r', encoding='utf-8') as f:
            blacklist = list(map(str.strip, f.readlines()))
    
    @bot.message_handler(commands=['reload'])
    def reload_blacklist(message):
        #тут имеет смысл вставить проверку, чтобы бот игнорировал эту команду от других юзеров
        load_stoplist()
    
    #ну и так далее. А в конце скрипта
    if __name__ == "__main__":
        load_stoplist()
        bot.infinity_polling()
    Ответ написан
  • Discord.py кортеж и split,в чем проблема?

    Vindicar
    @Vindicar
    RTFM!
    Ты пытаешься сделать рандомный выбор из введённых значений?
    А на кой пельмень ты вообще соединяешь args? Если они разделяются пробелом, то так
    # команда вызывается как !choice вариант1 вариант_2 "вариант 3"
    @commands.command(pass_context=True)
    async def choice(self, ctx, *args:str):
      chosen = random.choice(args)

    А если прямо нужно через запятую, то лучше так:
    # команда вызывается как !choice вариант1, вариант 2, вариант 3
    @commands.command(pass_context=True)
    # discord.py интерпретирует * как "всю остальную строку засунь в следующий параметр" 
    async def choice(self, ctx, *, args:str): 
      choices = [arg.strip() for arg in args.split(',')]
      chosen = random.choice(choices)
    Ответ написан
  • Снижается версия Python почему?

    Vindicar
    @Vindicar
    RTFM!
    Нужно немножко включить голову и почитать про то, как работает SSH и оболочка Unix системы. Гуглинг типа "SSH keep program running" быстро бы вывел тебя на то что нужно.
    1. Когда ты подключаешься по SSH, сервер запускает копию bash или sh (или какая там оболочка используется) с правами твоего пользователя.
    <br>
    sshd<br>
      - bash<br>

    При вводе команды в этой оболочке программа ищется в системном PATH, а в нём находится второй питон.

    2. Потом, когда ты делаешь activate, ты переходишь в виртуальное окружение. Оно отличается тем, что питон там ищется по умолчанию другой, так как там поменян PATH (и ещё кое что). И происходит этот переход за счёт запуска дочерней командной оболочки.
    <br>
    sshd<br>
      - bash<br>
          - activate <br>
              - bash<br>


    3. Ты запускаешь свой скрипт. Он запускается из под оболочки внутри activate.
    <br>
    sshd<br>
      - bash<br>
          - activate<br>
              - bash<br>
                  - python3 your_script.py<br>


    4. Ты закрываешь putty. SSH сервер регистрирует отключение клиента, и посылает дочернему bash сигнал HUP - обычно он интерпретируется как сигнал на завершение. Тот передаёт этот сигнал своему дочернему процессу, и так далее.
    <br>
    sshd "sshd: эй, bash, завершайся"<br>
      - bash "bash: эй, activate, завершайся. А теперь я сам завершусь."<br>
          - activate "activate: эй, bash, завершайся. А теперь я сам завершусь"<br>
              - bash "bash: эй, python3, завершайся. А теперь я сам завершусь"<br>
                  - python3 your_script.py "python3: хорошо, завершаюсь."<br>

    В итоге получаем только работающий ssh сервер
    sshd
    И когда ты переоткрываешь сессию, activate уже перестал существовать, и ты снова попадаешь в обычный bash, где в PATH прописан только второй питон.

    Теперь главное: как же это обойти? Нужно сделать так, чтобы python3 проигнорировал сигнал о завершении. Есть несколько способов.
    Самый простой - использовать такой синтаксис:
    nohup python3 your_script.py &
    Амперсанд в конце означает "запусти программу и вернись в оболочку, не дожидаясь когда программа закончит работать". А команда nohup запускает указанную программу с указанными аргументами, но при этом она проигнорирует сигнал HUP, т.е. "эй, завершайся". А потому когда ты закроешь putty, бот должен остаться работать.
    Минус - после переподключения ты не будешь видеть вывод бота в консоль. Так что пиши логи!
    Чтобы остановить бота, придётся использовать ps чтобы узнать ID его процесса, и kill чтобы этот процесс прибить. Ну или можешь предусмотреть команду выхода в самом боте, которая завершит работу скрипта изнутри. Это удобнее.

    Второй способ - использовать программу screen, если она установлена. Документацию по ней гугли. Если коротко, screen позволяет создать виртуальную рабочую сессию, к которой можно подключатсья и отключаться, не прерывая её. При этом весь вывод на экран сохраняется между переподключениями. Удобно если бот пишет много в консоль, но несколько муторно, и надо учить сочетания клавиш.

    Третий способ - сделать так, чтобы бот запускался при загрузке, через init.d скрипт или systemd модуль. Но так как у тебя минимальные права, скорее всего это не прокатит.
    Ответ написан
    1 комментарий
  • Пишет лишнюю букву?

    Vindicar
    @Vindicar
    RTFM!
    А зачем тебе вообще цикл for i in password:?
    Если тебе нужно в консоль и в файл вывести одно и то же, то выполни F.write(f'{n} {password}\n') и всё.
    Ну и приём с with, который я показал в твоём предыдущем вопрос, тоже стоит применить.
    Ответ написан
  • Текст накладывается сам на себя?

    Vindicar
    @Vindicar
    RTFM!
    Добавлю, что
    F.close
    Не делает ровным счётом ничего, так как вы не вызываете метод close.
    Должно быть
    F.close()
    А ещё лучше заменить вот это:
    F = open("txt","w")
      for i in password:
        F.write(str(n)+ password + '\n')
        F.close

    На вот это
    with open("txt","w") as F:
        for i in password:
          F.write(str(n)+ password + '\n')
    Ответ написан
    Комментировать
  • Как сделать чат для трех и более компьютеров в python socket?

    Vindicar
    @Vindicar
    RTFM!
    Выдели кому-то роль сервера, пусть он слушает соединения и хранит список уже подключенных клиентов.
    По приёму сообщения от подключенного клиента, нужно переслать его всем, кроме отправителя.
    Клиенты просто слушают сообщения от сервера и выводят на экран.
    Альтернатива - использовать широковещательный UDP, но TCP-сервер надёжнее.

    Я бы посоветовал разобраться с asyncio, оно может сильно упростить написание сервера.
    Самое сложное будет написать клиента, так как нужно будет подружить asyncio с пользовательским интерфейсом клиента. Даже элементарный input() будет нетривиально использовать.
    Так что можно сразу использовать qasync, чтобы подружить pyqt и asyncio.
    Ответ написан
    Комментировать
  • Как обработать ошибку в discord.py, при неправильном заполнении типа данных?

    Vindicar
    @Vindicar
    RTFM!
    Используй обработчик ошибок, как показано тут. Будет что-то типа такого
    from discord.ext.commands import CommandError, ConversionError
    
    #обработчик команды
    @bot.command() 
    #черная магия discord.py анализирует type hints чтобы понять, как парсить входное сообщение
    async def plus(ctx, x: int, y: int): #указываем, что параметры команды - это целые числа
        z = x + y
        await ctx.send(f"{x} + {y} = {z}")
    #обработчик ошибки
    @plus.error
    async def plus_error(ctx, error):
        if isinstance(error, ConversionError):
            await ctx.send(f"Ошибка преобразования аргументов plus. Вы ввели не числа?")
        elif isinstance(error, CommandError):
            await ctx.send(f"Ошибка выполнения команды plus")
        else:
            await ctx.send(f"Неизвестная ошибка выполнения команды plus")
    Ответ написан
    Комментировать
  • Как изменить название кнопки с командой shell?

    Vindicar
    @Vindicar
    RTFM!
    smokedevil666, сделай глобальный словарь вида
    commands = { "Нажми": "python3 /foor/bar/baz" }
    И ищи в нём полученное сообщение. Если такого ключа нет, ругаешься на пользователя.
    Ответ написан
    8 комментариев
  • Почему выходит ошибка TypeError: list indices must be integers or slices, not str?

    Vindicar
    @Vindicar
    RTFM!
    А ты уверен, что в s1_json корневой элемент - словарь, а не список?
    Ну и то же самое про s_json.
    Ответ написан
    Комментировать
  • Почему в .exe файле программы (Pyqt5) не исполняются команды к sqlite3?

    Vindicar
    @Vindicar
    RTFM!
    > Таблица это сама есть, базу данных я перебросил в корень с исполняемым файлом.
    Т.е. ты не пытаешься запаковать БД, так?
    А ты уверен, что скрипт её находит? Указываешь полный путь до файла с БД? Или как всегда, относительный, и авось текущая рабочая директория будет правильной?
    Просто некоторые py -> exe упаковщики при запуске экзешника распаковывают скрипт во временный каталог, и работают оттуда... а тогда файл БД окажется не рядом со скриптом. Не помню, делает ли так py2exe, выясни.
    Ответ написан
    7 комментариев