Аналог проекта Place Reddit в виде Telegram-бота

Многие помнят проект place, запущенный однажды на Reddit. Я решил попробовать создать бота в Телеграме с точно такой же функцией.

299 открытий2К показов
Аналог проекта Place Reddit в виде Telegram-бота

Многие помнят проект place, запущенный однажды на Reddit и собравший вокруг себя немало поклонников. Суть предельно проста: раз в пять минут можно закрасить один пиксель на поле 1000×1000 пикселей в свой цвет. Я решил попробовать создать бота в телеграмм с точно такой же функцией, а также что-нибудь порисовать…

Вступление

Статья носит познавательно-обучающий характер, и вот то, что можно из неё извлечь:

  • Библиотека telebot даёт возможность создания телеграм бота а также отслеживания команд с использованием @bot.message_handler(commands=[]) и текста с использованием @bot.message_handler(content_types=[‘text’]).
  • Библиотека telebot даёт возможность отправки сообщений с использованием bot.send_message(message.chat.id, text = «») и фото с помощью bot.send_photo(message.chat.id, img).
  • Библиотека pickle позволяет просто сохранять данные в файле с помощью pickle.dump() и извлекать оттуда используя pickle.load().
  • С помощью pillow можно создавать изображения используя Image.new() и изменять цвет отдельных пикселей используя img.putpixel().
  • Как писать текст с помощью PyAutoGUI.
  • Статья также позволяет изучить работу time.time() и создание простого таймера с её использованием.

Код бота

Для бота я буду использовать библиотеки (некоторые из них необходимо установить через pip install)(telebot, pillow):

			import telebot           # для бота
import pickle            # для открытия файла с цветами поля
from pathlib import Path # для удаления неугодного файла
from PIL import Image    # для отправки холста пользователю
import time              # для высчитывания времени межу изменениями цвета
		

Зададим значение переменных:

			st = time.time() - 100 # переменная измерения времени и его ограничения
l = 100                # ширина и высота поля
h = 7                  # размер пикселя на изображении
		

Запишем в data данные из файла, который мы потом создадим (содержит цвета пикселей):

			data = []
try:
    with open("data.pickle", "rb") as f:
        data = pickle.load(f)
except Exception as ex:
    print("Error during unpickling object (Possibly unsupported):", ex)
		

Запишем токен бота сюда:

			bot = telebot.TeleBot('токен')
		

Как его получить.

Теперь сделаем объяснение работы бота и отправку текущего изображения поля по команде start:

			@bot.message_handler(commands=['start']) # отслеживание команды
def start(message):
    # пояснения:
    bot.send_message(message.chat.id, text="Вы находитесь в боте для группового рисования картины. В этом боте вы можете внести свой вклад в сетевой холст, на котором рисуют и другие люди. Вы можете изменить цвет одного из пикселей раз в 10 секунд. Так вы можете рисовать свои шедеврыы, но учтите, что их могут изменять другие люди.")
    bot.send_message(message.chat.id, text = "Чтобы провести все вышеописанные операции, необходимо отправить мне цвет пикселя в формате: x, y, красный, зелёный, синий (через запятую, без пробелов). Причём ширина поля 0-99, длина 0-99, максимальное значение каждого из цветов 255, минимальное 0. Например:")
    bot.send_message(message.chat.id, text = "0,0,255,255,255")
    bot.send_message(message.chat.id, text = "Отправь 'к' чтобы увидеть полученное изображение.")

    # берём данные из файла
    try:
        with open("data.pickle", "rb") as f:
            data = pickle.load(f)
    except Exception as ex:
        print("Error during unpickling object (Possibly unsupported):", ex)

    # создаём изображение с pillow
    img = Image.new("RGB", (l*h, l*h), 'black')
    for i in range(l):      # для каждого пикселя поля
        for j in range(l):
            # если data[i*l + j] является не нулём
            try:
                # рисуем квадрат hхh
                for i1 in range(h):
                    for j1 in range(h):
                        img.putpixel((i*h +i1, j*h +j1), tuple(data[i*l + j]))

            # иначе ничего не делаем (имитация бурной деятельности):
            except TypeError:
                if j // 2000 == 3:
                    print("0")

    bot.send_photo(message.chat.id, img) # отправляем полученное изображение
		

Далее отслеживаем любой текст от пользователя и в случае необходимости отправляем картинку:

			@bot.message_handler(content_types=['text']) # отслеживаем текст
def func(message):
    # если пользователь просит изображение поля
    if message.text == "к" or message.text == "k" or message.text == "К":
        # берём данные из файла (обновляем)
        try:
            with open("data.pickle", "rb") as f:
                data = pickle.load(f)
        except Exception as ex:
            print("Error during unpickling object (Possibly unsupported):", ex)
    
        # создаём изображение с pillow
        img = Image.new("RGB", (l*h, l*h), 'black')
        for i in range(l):      # для каждого пикселя поля
            for j in range(l):
                # если data[i*l + j] является не нулём (изменялся хоть раз)
                try:
                    # рисуем квадрат hхh
                    for i1 in range(h):
                        for j1 in range(h):
                            img.putpixel((i*h +i1, j*h +j1), tuple(data[i*l + j]))
    
                # иначе ничего не делаем (имитация бурной деятельности):
                except TypeError:
                    if j // 2000 == 3:
                        print("0")
    
        bot.send_photo(message.chat.id, img) # отправляем полученное изображение
		

Если пользователь отправил сообщение не меньше минимальной длины запроса на рисование (минимальный — 0,0,0,0,0 — длина 9), проверяем на ошибки:

			else:
        # если длиннее минимального
        if len(message.text) >= 9:
            # переменная таймера - глобальная
            global st

            # Если с прошлого изменения пикселя прошло не меньше 20 секунд
            if time.time() - st < 20:
                bot.send_message(message.chat.id, text = "слишком часто. Промежуток между сообщениями - 20 секунд")
            else:
                # преобразуем текст сообщения в массив с координатами и цветом
                t = message.text
                x = []
                n = ""
                
                # с учётом разделителей
                for i in t:
                    if i != ",":
                        n += i
                    else:
                        x.append(n)
                        n = ""

                x.append(n)
                n = ""

                # просматриваем корректность цветов и координат: 
                # не вышли ли за край поля, и т.п.
                a = 0
                n = 0
                # в случае нарушений n = 1 (флаг)
                while a <= len(x)-1:
                    try:
                        x[a] = int(x[a])
                        if a < 2:
                            if x[a] > l:
                                n = 1
                        else:
                            if x[a] > 255 or x[a] < 0:
                                n = 1

                    except Exception:
                        n = 1
                        
                    a += 1
		

Если не нашли ошибку — отслеживаем координаты рисования и красим пиксель в необходимый цвет, оповещаем пользователя, обновляем таймер:

			import pickle

# создаём массив
data = []
data = [0] * (100 * 100)

# сохраняем
try:
    with open("data.pickle", "wb") as f:
        pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
except Exception as ex:
    print("Error during pickling object (Possibly unsupported):", ex)
		

Не забудим запустить работу бота:

			bot.polling(none_stop=True)
		

Вот и весь код!

Не забудем создать «data.pickle» с помощью другого кода. Он нужен единожды и запускается первым. Указываем размер поля и сохраняем (после тот же размер нужно указать в программе бота):

			import pickle

# создаём массив
data = []
data = [0] * (100 * 100)

# сохраняем
try:
    with open("data.pickle", "wb") as f:
        pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
except Exception as ex:
    print("Error during pickling object (Possibly unsupported):", ex)
		

Все файлы вы можете найти на GitHub.

Далее запускаем хостинг бота на pythonAnywhere (инструкция). Не забудьте установить необходимые библиотеки. Это делается с помощью обычного pip install в консоли на сайте (в инструкции описана установка библиотеки telebot, вам нужна ещё pillow (pip install pillow))

Бот успешно работает, вы можете опробовать его в телеграмме по ссылке.

Автоматизация

После написания основного кода я решил автоматизировать скорее не запросы, а преобразование картинки в запросы.

Начнём с импорта библиотеки pillow и создания текстового файла с координатами:

			from PIL import Image
f = open("result.txt", "w+")
		

Далее откроем изображение (лучше пиксельарт) и спросим у пользователя координаты размещения:

			img = Image.open('img.jpg')
print("X:")
x = int(input())
print("Y:")
y = int(input())
		

Для каждого пикселя изображения нужно узнать его будущее местоположение и цвет:

			data = []
for i in range(img.size[0]):
    for j in range(img.size[1]):
        col = img.getpixel((i, j))
        data.append(str(i+x) + "," + str(j+y) + "," + str(col[0]) + "," + str(col[1]) + "," + str(col[2]))
		

Записываем полученные координаты в файл:

			for i in data:
    f.write(i + "\n")

f.close()
		

Всё! После запуска получаем то, что необходимо отправлять боту.

Автоматизация отправки

Так как цель тут — скорее изучение Python, то почему бы не использовать библиотеку pyautoGUI (pip install PyAutoGUI)?

Благо, нам необходимо только вписать текст и нажать Enter:

			import pyautogui
from time import sleep

# открываем файл с координатами
f = open("result.txt")

# читаем его
data = f.readlines()

# убираем переходы на следущую строку
diction = []
for i in data:
    a = str(i)
    diction.append(a[0:len(i)-1])

# вписываем запрос в телеграм и отправляем боту
for i in diction:
    sleep(22)
    pyautogui.typewrite(i, interval=0.0)
    pyautogui.press('enter')
		

После запуска вышеописанного кода в течение 22 секунд необходимо перейти на вкладку telegram с открытым чатом бота и кликнуть в поле ввода сообщения.

Таким образом мы можем почти автоматически рисовать различные изображения на всем доступном холсте!

Я запустил своего бота с полем 300*300, можно попробовать его.

Все программы находятся на github.

Удачных компиляций!

Следите за новыми постами
Следите за новыми постами по любимым темам
299 открытий2К показов