поправь код, Нужно чтобы пиксели ставились в случайном месте, а не по порядку а в разброс. еще нужно чтобы корректно работало указание своих координат для фото (пишет что мы ушли за пределы, хотя в интерфейсе  правильно показывает расположение) и поправь автоматическую проверку после того как закончил рисовать, если пиксель кто-то закрасил, то нужно его перекрасить назад: 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

        # Кеш для fetch_current_pixels
        self.cached_pixels = None
        self.last_fetch_time = 0
        self.fetch_lock = threading.Lock()

        # Флаг для игнорирования черного цвета
        self.ignore_black = False

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

    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)

        # Кнопка установки начальной позиции вручную
        manual_position_frame = ttk.LabelFrame(self.root, text="Начальная позиция")
        manual_position_frame.pack(padx=5, pady=5, fill="x")

        ttk.Button(manual_position_frame, text="Ввести координаты", command=self.set_start_position_manual).pack(side="left", padx=5)
        ttk.Button(manual_position_frame, text="Сбросить позицию", command=self.reset_start_position).pack(side="left", padx=5)

        # Фрейм для управления цветом
        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)

        # Фрейм для дополнительных настроек
        additional_frame = ttk.LabelFrame(self.root, text="Дополнительно")
        additional_frame.pack(padx=5, pady=5, fill="x")

        self.black_var = tk.BooleanVar()
        self.black_var.set(False)
        ttk.Checkbutton(additional_frame, text="Игнорировать черный цвет", variable=self.black_var).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_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()
                # Перестраиваем очередь без удаленного токена
                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_manual(self):
        try:
            x = int(simpledialog.askstring("Начальная позиция", "Введите X координату:"))
            y = int(simpledialog.askstring("Начальная позиция", "Введите Y координату:"))
            self.start_position = (x, y)
            messagebox.showinfo("Позиция установлена", f"Начальная позиция установлена на ({x}, {y})")
            self.update_canvas()
        except (TypeError, ValueError):
            messagebox.showerror("Ошибка", "Неверные координаты.")

    def reset_start_position(self):
        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("Позиция сброшена", "Начальная позиция сброшена.")
        self.update_canvas()

    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):
        with self.fetch_lock:
            current_time = time.time()
            if self.cached_pixels and (current_time - self.last_fetch_time) < 10:
                print("Используем кешированные данные пикселей.")
                return  # Используем кешированные данные
            else:
                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": "cf_clearance=Np7JBD5tprdfoIpkb5YJ52itKt_6Ng_wGupR6dUBr.A-1730235814-1.2.1.1-YCRIOmtELCxNXp4W601ucHEbTWCP8KmyXPKuO26yNacRcpXqS2FjsMEQ1jclpgC_9RRox7I6hl7L1RbzlkBvrKPV4TaViJSp93sIawl.XeeEtDznvitsBhuxP2ShkHNPMdeZCezqmtiUpNFmS5Tu8kcdFUiO0PMUpl8cQwrE8vLOrP.OyfoNqCCYZXx7nUx4EwhvdBQ3ZY1b.CEc3xmY_DyIA6oQzItoa.OLCyAeS6xfZd26c2fijvXqzOiwM15oScEcBw5uSzYoLgfl2bKByyWFNtjB7xXUxNXuDSanMZphOqFuAyFECLp57kXMiJP9dhp6cKBX82MKgEL8sLe7IdsLLzom36t9i4P4f6AJv5TCNx43L5UYM5xZtW5RUBK18JPTNpeql_LWpYbN.OS.krQqDOxo4n2SILXf.IpWYCdpLN.WrRIJZPgl9nN5FLpK",

                    "Priority": "u"
                }

                try:
                    response = requests.get(url, headers=headers)
                    if response.status_code == 200:
                        self.cached_pixels = Image.open(io.BytesIO(response.content))
                        self.last_fetch_time = current_time
                        print("Получены новые данные пикселей с сервера.")
                        # Применение масштаба
                        if self.scale_factor != 1:
                            new_size = (int(self.cached_pixels.width * self.scale_factor), int(self.cached_pixels.height * self.scale_factor))
                            self.cached_pixels = self.cached_pixels.resize(new_size, Image.NEAREST)
                        photo = ImageTk.PhotoImage(self.cached_pixels)
                        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):
        # Убрали обработку кликов, так как позиция вводится вручную
        pass

    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):
        if self.ignore_black and color.lower() == "#000000":
            print(f"Игнорирование рисования черного цвета на ({x},{y}).")
            return
        if self.cached_pixels:
                # Корректируем координаты с учетом масштаба
                scaled_x = int(x / self.scale_factor) if self.scale_factor != 0 else x
                scaled_y = int(y / self.scale_factor) if self.scale_factor != 0 else y
                if 0 <= scaled_x < self.cached_pixels.width and 0 <= scaled_y < self.cached_pixels.height:
                    current_color = self.cached_pixels.getpixel((scaled_x, scaled_y))
                    current_hex = "#%02x%02x%02x" % current_color
                else:
                    print(f"Координаты пикселя ({x},{y}) вне границ изображения.")
                    return
        else:
                print("Нет кешированных данных пикселей.")
                return

        if current_hex.lower() == color.lower():
                print(f"Пиксель ({x},{y}) уже нужного цвета.")
                return
        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": "cf_clearance=Np7JBD5tprdfoIpkb5YJ52itKt_6Ng_wGupR6dUBr.A-1730235814-1.2.1.1-YCRIOmtELCxNXp4W601ucHEbTWCP8KmyXPKuO26yNacRcpXqS2FjsMEQ1jclpgC_9RRox7I6hl7L1RbzlkBvrKPV4TaViJSp93sIawl.XeeEtDznvitsBhuxP2ShkHNPMdeZCezqmtiUpNFmS5Tu8kcdFUiO0PMUpl8cQwrE8vLOrP.OyfoNqCCYZXx7nUx4EwhvdBQ3ZY1b.CEc3xmY_DyIA6oQzItoa.OLCyAeS6xfZd26c2fijvXqzOiwM15oScEcBw5uSzYoLgfl2bKByyWFNtjB7xXUxNXuDSanMZphOqFuAyFECLp57kXMiJP9dhp6cKBX82MKgEL8sLe7IdsLLzom36t9i4P4f6AJv5TCNx43L5UYM5xZtW5RUBK18JPTNpeql_LWpYbN.OS.krQqDOxo4n2SILXf.IpWYCdpLN.WrRIJZPgl9nN5FLpK",
                "Priority": "u"
            }
        try:
            # Получение токена из очереди (ожидание, если очередь пуста)
  


            # Вместо запроса текущего цвета пикселя используем кешированные данные
            
            # Установка нового цвета пикселя
            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((int(x / self.scale_factor), int(y / self.scale_factor)), 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 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

                if self.ignore_black and desired_hex.lower() == "#000000":
                    continue  # Игнорируем черный цвет

                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):
        # Метод больше не используется, так как мы кешируем fetch_current_pixels
        pass

    def run(self):
        # Запускаем основное окно без авто-обновления
        self.root.mainloop()

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

if __name__ == "__main__":
    main()