Начинает работу без команды. когда загружаешь фото, запрос на получение пикселей fetch_current_pixels должен отправлять на сервер не более чем один раз в 10 секунд, кешируй в общем. Установить позицию где будет происходить рисование не получается и сделай флаг который позволит игнорировать рисование черного цвета. Кроме того вместо # Получение текущего цвета пикселя отправки запроса сделай чтобы цвета брал из fetch_current_pixels : import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
import requests
from PIL import Image, ImageTk
import threading
import time
import io
import random
import queue

class PixelBattleApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Pixel Battle Manager")

        # Управление токенами с использованием очереди
        self.tokens = ["28862a05a3f84141b50828b6dbd78884.0d8b887f5b744531a4d51b9d3ef07f62.m2rl7z0l","c2b44bf252b4459ea753dbff0dca94a6.9e374c5481af4395b6c95dbced6b979b.m2utx942","0bdc81e3e4c7496da4f0ebf25e0dcdc6.b91387eb9246476f972a8e75fee4d3ab.m2rkw5so","15f33cad6f824c7ca8437ab42c2fb6b0.0a9428d4a2a94afca7ae2b401d1eb79a.m2uuxcz0","b3c635a83a2145c6b298e626e5f1768a.0fc91a85fcb84ef3987aaa1f20da8e34.m2uv21iv","13ef609edbe3404fb113c835e7282ec8.75bdb429646848a4b6feacd703962fda.m2uv2z2a","364a2bfea0584a81828104727ada7fd6.01a948b01c4c475d8fbce8656750bc8b.m2uv3tw3","9f08fd41a7ef48f2b06e11c40ac0ea20.0395268728804e8e81d3939c52597a9c.m2uv4t8q"]
        self.token_lock = threading.Lock()
        self.token_queue = queue.Queue()
        for token in self.tokens:
            self.token_queue.put(token)

        # Масштаб и пикселизация
        self.scale_factor = 1
        self.pixel_size = 1

        # Изображение и позиция для рисования
        self.original_image = None
        self.pixelated_image = None
        self.start_position = (0, 0)
        self.setting_start_position = False

        # Авто-рисование
        self.auto_drawing = False
        self.auto_draw_thread = None

        # Периодическое обновление
        self.update_interval = 10  # секунды
        self.update_thread = None

        # Настройка UI и холста
        self.setup_ui()
        self.setup_canvas()
        self.start_periodic_update()

    def setup_ui(self):
        # Фрейм для управления токенами
        token_frame = ttk.LabelFrame(self.root, text="Управление токенами")
        token_frame.pack(padx=5, pady=5, fill="x")

        ttk.Button(token_frame, text="Добавить токен", command=self.add_token).pack(side="left", padx=5)
        ttk.Button(token_frame, text="Удалить токен", command=self.remove_token).pack(side="left", padx=5)
        self.token_label = ttk.Label(token_frame, text=f"Всего токенов: {len(self.tokens)}")
        self.token_label.pack(side="left", padx=10)

        # Фрейм для управления изображением
        image_frame = ttk.LabelFrame(self.root, text="Управление изображением")
        image_frame.pack(padx=5, pady=5, fill="x")

        ttk.Button(image_frame, text="Загрузить изображение", command=self.load_image).pack(side="left", padx=5)
        ttk.Button(image_frame, text="Показать оригинал", command=self.show_original_image).pack(side="left", padx=5)
        ttk.Button(image_frame, text="Начать авто-рисование", command=self.start_auto_draw).pack(side="left", padx=5)
        ttk.Button(image_frame, text="Остановить авто-рисование", command=self.stop_auto_draw).pack(side="left", padx=5)

        # Фрейм для управления масштабом и пикселизацией
        control_frame = ttk.LabelFrame(self.root, text="Настройки")
        control_frame.pack(padx=5, pady=5, fill="x")

        # Масштаб
        ttk.Label(control_frame, text="Масштаб:").pack(side="left", padx=5)
        self.scale_entry = ttk.Entry(control_frame, width=5)
        self.scale_entry.insert(0, "1")
        self.scale_entry.pack(side="left", padx=5)
        ttk.Button(control_frame, text="Применить", command=self.apply_scale).pack(side="left", padx=5)

        # Размер пикселя
        ttk.Label(control_frame, text="Размер пикселя:").pack(side="left", padx=5)
        self.pixel_size_entry = ttk.Entry(control_frame, width=5)
        self.pixel_size_entry.insert(0, "1")
        self.pixel_size_entry.pack(side="left", padx=5)
        ttk.Button(control_frame, text="Применить", command=self.apply_pixel_size).pack(side="left", padx=5)

        # Кнопка установки начальной позиции
        ttk.Button(self.root, text="Установить начальную позицию", command=self.set_start_position).pack(padx=5, pady=5, fill="x")

        # Фрейм для управления цветом
        color_frame = ttk.LabelFrame(self.root, text="Цвет пикселя")
        color_frame.pack(padx=5, pady=5, fill="x")

        ttk.Label(color_frame, text="Цвет:").pack(side="left", padx=5)
        self.color_entry = ttk.Entry(color_frame)
        self.color_entry.insert(0, "#000000")
        self.color_entry.pack(side="left", padx=5)

    def setup_canvas(self):
        self.canvas = tk.Canvas(self.root, width=800, height=600, bg="white")
        self.canvas.pack(padx=5, pady=5)
        self.canvas.bind("<Button-1>", self.canvas_click)

        # Для установки начальной позиции
        self.canvas.bind("<Button-3>", self.remove_start_position)

        # Хранение объектов на холсте
        self.canvas_current_image = None  # Текущее состояние пикселей с сервера
        self.canvas_loaded_image = None  # Загруженное пикселизированное изображение
        self.canvas_start_position_marker = None  # Маркер начальной позиции

    def add_token(self):
        token = simpledialog.askstring("Добавить токен", "Введите Bearer токен:")
        if token:
            with self.token_lock:
                self.tokens.append(token)
                self.token_queue.put(token)
                self.token_label.config(text=f"Всего токенов: {len(self.tokens)}")

    def remove_token(self):
        with self.token_lock:
            if self.tokens:
                removed = self.tokens.pop()
                # Перестраиваем очередь без удаленного токена
                # Примечание: очередь Queue не поддерживает удаление элементов,
                # поэтому мы создаем новую очередь и повторно добавляем все остальные токены
                remaining_tokens = list(self.token_queue.queue)
                remaining_tokens = [t for t in remaining_tokens if t != removed]
                self.token_queue = queue.Queue()
                for token in remaining_tokens:
                    self.token_queue.put(token)
                self.token_label.config(text=f"Всего токенов: {len(self.tokens)}")
                messagebox.showinfo("Токен удалён", f"Токен {removed} удалён.")
            else:
                messagebox.showwarning("Предупреждение", "Нет токенов для удаления.")

    def apply_scale(self):
        try:
            scale = float(self.scale_entry.get())
            if scale <= 0:
                raise ValueError
            self.scale_factor = scale
            if self.original_image:
                self.pixelize_image()
                self.update_canvas()
        except ValueError:
            messagebox.showerror("Ошибка", "Неверный коэффициент масштаба.")

    def apply_pixel_size(self):
        try:
            size = int(self.pixel_size_entry.get())
            if size <= 0:
                raise ValueError
            self.pixel_size = size
            if self.original_image:
                self.pixelize_image()
                self.update_canvas()
        except ValueError:
            messagebox.showerror("Ошибка", "Неверный размер пикселя.")

    def set_start_position(self):
        messagebox.showinfo("Установка позиции", "Кликните левой кнопкой мыши на холсте для установки начальной позиции.")
        self.setting_start_position = True

    def remove_start_position(self, event):
        self.start_position = None
        if self.canvas_start_position_marker:
            self.canvas.delete(self.canvas_start_position_marker)
            self.canvas_start_position_marker = None
        messagebox.showinfo("Позиция сброшена", "Начальная позиция сброшена.")

    def pixelize_image(self):
        if self.original_image:
            width, height = self.original_image.size
            px_size = self.pixel_size
            # Пикселизация
            self.pixelated_image = self.original_image.resize(
                (width // px_size, height // px_size),
                Image.NEAREST
            ).resize((width, height), Image.NEAREST)
            # Применение масштаба
            if self.scale_factor != 1:
                new_size = (int(self.pixelated_image.width * self.scale_factor), int(self.pixelated_image.height * self.scale_factor))
                self.pixelated_image = self.pixelated_image.resize(new_size, Image.NEAREST)

    def load_image(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("Файлы изображений", "*.png *.jpg *.jpeg *.bmp *.gif")]
        )
        if file_path:
            try:
                self.original_image = Image.open(file_path).convert("RGB")
                # Проверка размера и автоматическое изменение
                max_size = 40  # Максимальная ширина или высота
                if self.original_image.width > max_size or self.original_image.height > max_size:
                    self.original_image.thumbnail((max_size, max_size), Image.LANCZOS)
                    messagebox.showinfo("Изменение размера", f"Изображение было изменено до {self.original_image.size}")
                self.pixelize_image()
                # Создание изображения для отображения на холсте
                self.canvas_loaded_image = ImageTk.PhotoImage(self.pixelated_image)
                self.update_canvas()
            except Exception as e:
                messagebox.showerror("Ошибка", f"Не удалось загрузить изображение: {e}")

    def show_original_image(self):
        if self.original_image:
            original_window = tk.Toplevel(self.root)
            original_window.title("Оригинальное изображение")
            original_photo = ImageTk.PhotoImage(self.original_image)
            label = tk.Label(original_window, image=original_photo)
            label.image = original_photo  # Сохраняем ссылку
            label.pack()
        else:
            messagebox.showwarning("Предупреждение", "Нет загруженного изображения.")

    def update_canvas(self):
        def task():
            if self.pixelated_image:
                try:
                    self.fetch_current_pixels()
                except Exception as e:
                    print(f"Ошибка обновления холста: {e}")

        threading.Thread(target=task, daemon=True).start()

    def fetch_current_pixels(self):
        url = f"https://3years.aeza.net/api/pixels.png?{random.random()}"
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
            "Accept": "*/*",
            "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3",
            "Accept-Encoding": "gzip, deflate, br, zstd",
            "Referer": "https://3years.aeza.net/",
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-origin",
            "Connection": "keep-alive",
            "Cookie": "carrotquest_device_guid=28884379-c402-4760-b2bd-237448f93638; _ym_uid=1699882530587841879; _ym_d=1699882530; cf_clearance=iociDUITz1GvVdkgPr.PJ7mbkxhbdTPGmVJwe_A.vC0-1730230345-1.2.1.1-h.klFKOnF9LAfzKjogGrQcPq8YJHKpA6zJ2hMBIhzxtYxq3T4QXExqN3I3DIslc4iOcWEMaigi3L05CJLkBK_gq2Qz9Xa4KP5ciExfexTnNkEm18KQO3_eSlrQ4HYIZfcrpj0i9XlqKU5HzpBItqZvM0DkiEXzo._EAJ_iMog3SH_mq_6qPzMpmLtzjgeBVB7K6TUA9Zu.vOBrtJjzIHPkCP7s1GhquO7cLG.Ts2uigIy5Tj2Hhpw828PZ_HzVQa7REvlwtnZZYLWjkzOgOZYBUtVI97sfC2Kf8.bVY5D5Hfoao_H38bkBBsIsmRiliYvFQ0jaZZrENMKIdKD.EvhGggUB6ZX4yA.AuBcbB.hrhu46gmcYQZcJxzjOQuo8saFWWQR5YbAzLzz08kwGB6XBjFpLit61kFnqdWo_Tcmq01IiHYtQRRu8.oV0ZjE6FH",
            "Priority": "u"
        }

        try:
            response = requests.get(url, headers=headers)
            if response.status_code == 200:
                image = Image.open(io.BytesIO(response.content))
                # Применение масштаба
                if self.scale_factor != 1:
                    new_size = (int(image.width * self.scale_factor), int(image.height * self.scale_factor))
                    image = image.resize(new_size, Image.NEAREST)
                photo = ImageTk.PhotoImage(image)
                self.canvas_current_image = photo  # Сохраняем ссылку
                self.canvas.delete("all")  # Очистить холст
                self.canvas.create_image(0, 0, anchor="nw", image=self.canvas_current_image)

                # Отображение пикселизированного изображения, если задана начальная позиция
                if self.start_position and self.pixelated_image:
                    self.canvas_loaded_image = ImageTk.PhotoImage(self.pixelated_image)
                    self.canvas.create_image(self.start_position[0], self.start_position[1], anchor="nw", image=self.canvas_loaded_image)

                    # Отображение маркера начальной позиции
                    if not self.canvas_start_position_marker:
                        x, y = self.start_position
                        self.canvas_start_position_marker = self.canvas.create_rectangle(
                            x,
                            y,
                            x + self.pixelated_image.width,
                            y + self.pixelated_image.height,
                            outline="red",
                            width=2
                        )
                else:
                    # Если позиция не установлена, просто отображаем загруженное изображение на холсте
                    if self.pixelated_image:
                        self.canvas_loaded_image = ImageTk.PhotoImage(self.pixelated_image)
                        self.canvas.create_image(0, 0, anchor="nw", image=self.canvas_loaded_image)

            else:
                print(f"Не удалось получить карту пикселей: статус {response.status_code}")
        except Exception as e:
            print(f"Ошибка при получении карты пикселей: {e}")

    def canvas_click(self, event):
        if self.setting_start_position:
            # Установка начальной позиции
            x = event.x
            y = event.y
            self.start_position = (x, y)
            self.setting_start_position = False
            messagebox.showinfo("Позиция установлена", f"Начальная позиция установлена на ({x}, {y})")
            self.update_canvas()
            return

        if self.token_queue.empty():
            messagebox.showerror("Ошибка", "Нет доступных токенов.")
            return

        # Обработка левого клика для установки пикселя
        x = int(event.x / self.scale_factor)
        y = int(event.y / self.scale_factor)
        color = self.color_entry.get()

        # Запуск сетевого запроса в отдельном потоке
        self.place_pixel(x, y, color)

    def place_pixel(self, x, y, color):
        threading.Thread(target=self.use_token_and_place_pixel, args=(x, y, color), daemon=True).start()

    def use_token_and_place_pixel(self, x, y, color):
        try:
            # Получение токена из очереди (ожидание, если очередь пуста)
            token = self.token_queue.get(block=True)

            headers = {
                "Authorization": f"Bearer {token}",
                "Content-Type": "application/json",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
                "Accept": "*/*",
                "Accept-Language": "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3",
                "Accept-Encoding": "gzip, deflate, br, zstd",
                "Referer": "https://3years.aeza.net/",
                "Sec-Fetch-Dest": "empty",
                "Sec-Fetch-Mode": "cors",
                "Sec-Fetch-Site": "same-origin",
                "Connection": "keep-alive",
                "Cookie": "carrotquest_device_guid=28884379-c402-4760-b2bd-237448f93638; _ym_uid=1699882530587841879; _ym_d=1699882530; cf_clearance=iociDUITz1GvVdkgPr.PJ7mbkxhbdTPGmVJwe_A.vC0-1730230345-1.2.1.1-h.klFKOnF9LAfzKjogGrQcPq8YJHKpA6zJ2hMBIhzxtYxq3T4QXExqN3I3DIslc4iOcWEMaigi3L05CJLkBK_gq2Qz9Xa4KP5ciExfexTnNkEm18KQO3_eSlrQ4HYIZfcrpj0i9XlqKU5HzpBItqZvM0DkiEXzo._EAJ_iMog3SH_mq_6qPzMpmLtzjgeBVB7K6TUA9Zu.vOBrtJjzIHPkCP7s1GhquO7cLG.Ts2uigIy5Tj2Hhpw828PZ_HzVQa7REvlwtnZZYLWjkzOgOZYBUtVI97sfC2Kf8.bVY5D5Hfoao_H38bkBBsIsmRiliYvFQ0jaZZrENMKIdKD.EvhGggUB6ZX4yA.AuBcbB.hrhu46gmcYQZcJxzjOQuo8saFWWQR5YbAzLzz08kwGB6XBjFpLit61kFnqdWo_Tcmq01IiHYtQRRu8.oV0ZjE6FH",
                "Priority": "u"
            }

            # Получение текущего цвета пикселя
            get_response = requests.get(f'https://3years.aeza.net/api/pixels?x={x}&y={y}', headers=headers)
            if get_response.status_code != 200:
                print(f"Не удалось получить пиксель ({x},{y}): статус {get_response.status_code}")
                return
            current_pixel = get_response.json()
            if current_pixel.get("color").lower() == color.lower():
                print(f"Пиксель ({x},{y}) уже нужного цвета.")
                return

            # Установка нового цвета пикселя
            data = {"color": color, "x": x, "y": y}
            put_response = requests.put('https://3years.aeza.net/api/pixels', headers=headers, json=data)
            if put_response.status_code == 200:
                print(f"Пиксель ({x},{y}) установлен на {color}.")
                # Обновление локального изображения
                if self.pixelated_image:
                    try:
                        self.pixelated_image.putpixel((x, y), self.hex_to_rgb(color))
                        self.update_canvas()
                    except Exception as e:
                        print(f"Ошибка при обновлении локального изображения: {e}")
            else:
                print(f"Не удалось установить пиксель ({x},{y}): статус {put_response.status_code}")
        except Exception as e:
            print(f"Ошибка при установке пикселя ({x},{y}): {e}")
        finally:
            # Возврат токена в очередь через 5 секунд
            threading.Timer(5, self.token_queue.put, args=(token,)).start()

    def get_available_token(self):
        # Этот метод теперь не нужен, так как мы используем очередь
        pass

    # Удаляем метод release_token, так как управление токенами осуществляется через очередь
    # def release_token(self, token):
    #     with self.token_lock:
    #         self.token_last_used[token] = 0  # Сброс времени использования токена

    def hex_to_rgb(self, hex_color):
        hex_color = hex_color.lstrip('#')
        if len(hex_color) != 6:
            raise ValueError("Неверный формат цвета.")
        r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
        return (r, g, b)

    def start_auto_draw(self):
        if not self.pixelated_image:
            messagebox.showerror("Ошибка", "Нет загруженного изображения для рисования.")
            return
        if self.auto_drawing:
            messagebox.showinfo("Информация", "Авто-рисование уже запущено.")
            return
        self.auto_drawing = True
        self.auto_draw_thread = threading.Thread(target=self.auto_draw, daemon=True)
        self.auto_draw_thread.start()
        messagebox.showinfo("Информация", "Авто-рисование запущено.")

    def stop_auto_draw(self):
        if self.auto_drawing:
            self.auto_drawing = False
            messagebox.showinfo("Информация", "Авто-рисование остановлено.")
        else:
            messagebox.showinfo("Информация", "Авто-рисование не запущено.")

    def auto_draw(self):
        px_size = self.pixel_size
        width, height = self.pixelated_image.size

        # Если начальная позиция не установлена, не продолжаем
        if not self.start_position:
            print("Начальная позиция не установлена.")
            self.auto_drawing = False
            return

        # Определяем область для рисования
        start_x, start_y = self.start_position
        end_x = min(start_x + width, 255)  # Предполагаемая ширина холста
        end_y = min(start_y + height, 255)  # Предполагаемая высота холста

        for y in range(height):
            for x in range(width):
                if not self.auto_drawing:
                    print("Авто-рисование остановлено пользователем.")
                    return

                color = self.pixelated_image.getpixel((x, y))
                desired_hex = "#%02x%02x%02x" % color

                canvas_x = start_x + x
                canvas_y = start_y + y

                if canvas_x < 0 or canvas_x >= 255 or canvas_y < 0 or canvas_y >= 255:
                    continue  # Пропускаем пиксели вне границ холста

                # Запуск сетевого запроса в отдельном потоке
                self.place_pixel(canvas_x, canvas_y, desired_hex)

                # Регулируем скорость: каждые 10 пикселей делаем паузу
                if (x + y * width) % 10 == 0:
                    time.sleep(0.1)

        print("Авто-рисование завершено.")
        self.auto_drawing = False

    def start_periodic_update(self):
        self.update_thread = threading.Thread(target=self.periodic_update, daemon=True)
        self.update_thread.start()

    def periodic_update(self):
        while True:
            if self.pixelated_image and self.start_position:
                self.check_and_update_pixels()
            time.sleep(self.update_interval)

    def check_and_update_pixels(self):
        px_size = self.pixel_size
        width, height = self.pixelated_image.size

        start_x, start_y = self.start_position
        end_x = min(start_x + width, 255)
        end_y = min(start_y + height, 255)

        for y in range(height):
            for x in range(width):
                try:
                    desired_color = self.pixelated_image.getpixel((x, y))
                    desired_hex = "#%02x%02x%02x" % desired_color

                    canvas_x = start_x + x
                    canvas_y = start_y + y

                    if canvas_x < 0 or canvas_x >= 255 or canvas_y < 0 or canvas_y >= 255:
                        continue  # Пропускаем пиксели вне границ холста

                    # Запуск сетевого запроса в отдельном потоке
                    self.place_pixel(canvas_x, canvas_y, desired_hex)

                except Exception as e:
                    print(f"Ошибка при проверке пикселя ({x},{y}): {e}")

    def run(self):
        self.root.mainloop()

def main():
    root = tk.Tk()
    app = PixelBattleApp(root)
    app.run()

if __name__ == "__main__":
    main()