


########
# ANSI
########
def ANSIRELMOVEUP(n):
    return f"\033[{n}A"

def ANSIRELMOVEDOWN(n):
    return f"\033[{n}B"

def ANSIRELMOVELEFT(n):
    return f"\033[{n}D"

def ANSIRELMOVERIGHT(n):
    return f"\033[{n}C"

def ANSIABSMOVE(x,y):
    return f"\033[{y};{x}H"

def ANSIBEGINLINE():
    return f"\r"

def ANSITOPLEFT():
    return f"\033[0;0H"

def ANSIRESETFORMAT():
    return f"\033[0m"

def ANSISETFGCOLOR(colorcode):
    return f"\033[38;5;{colorcode}m"

def ANSISETBGCOLOR(colorcode):
    return f"\033[48;5;{colorcode}m"

# 0 normal 1 bold 2 dim 3 italic 4 underline 5 blink 7 inverse 8 hidden
def ANSISETFORMAT(format):
    return f"\033[{format}m"

###########
# demuxer
###########

import queue
import threading

xmuxer = threading.Event()
qmuxer = queue.Queue()
# Define a function that the demuxer thread will run
def demuxer_thread(q):
    while not xmuxer.is_set():
        # Wait for a message from any of the input queues
        try:
            qq, message = q.get_nowait()
            print("demuxed got",message)
            qq.put(('demuxed', message))
        except queue.Empty:
            # Handle empty queue condition
            continue
    print('demuxer gtfo')


#################
# TELNET SERVER
#################
import asyncio
HOST = '0.0.0.0'
PORT = 23

print("[ctrl]+] then c to close")
print("[ctrl]+] mode character for live keystroke")

async def handle_client(reader, writer):
    client_address = writer.get_extra_info('peername')
    print(f'Client connected from {client_address[0]}:{client_address[1]}')
    # Send the Telnet negotiation sequence
    writer.write(b'\xff\xfd\x18\xff\xfd\x1f\xff\xfd\x20')
    await writer.drain()

    # enable mouse
    writer.write(b'\x1b[?1000h')

    # hello colored
    writer.write(b'\033[31;1;4mHello\033[0m')

    await writer.drain()
    # Receive and process data from the client
    q = queue.Queue()
    while True:
        data = await reader.read(1024)
        # If the client closed the connection, break out of the loop
        if not data:
            break
        if data.startswith(b'\xff'):
            # print garbage on server console
            continue

        if b'\x1b[M' in data:
            # Extract the mouse event information
            mouse_data = data[data.index(b'\x1b[M')+3:]
            x = mouse_data[0] - 32
            y = mouse_data[1] - 32
            button = mouse_data[2] - 32

            # Print the mouse event information
            print(f"{client_address[0]}:{client_address[1]}:Received mouse event: x={x}, y={y}, button={button}")

        #qmuxer.put((q,data))
        #print(q.get())
        #q.queue.clear()

        print(f"{client_address[0]}:{client_address[1]}:{data}")
        # Process the received data
        # In this example, we simply echo back the received data
        writer.write(data)
        await writer.drain()

    q.queue.clear()
    # Disable mouse mode
    writer.write(b'\x1b[?1000l')
    # Close the client socket
    writer.close()
    print(f'Client disconnected from {client_address[0]}:{client_address[1]}')

async def main():
    # Start the server
    server = await asyncio.start_server(handle_client, HOST, PORT)

    # Print a message to indicate that the server is running
    print(f'Telnet server started on {HOST}:{PORT}')

    # Wait for the server to stop
    async with server:
        await server.serve_forever()

    print("exit")
    qmuxer.queue.clear()

########
# main
########

if __name__ == '__main__':
    demuxer = threading.Thread(target=demuxer_thread, args=(qmuxer,))
    try:
        demuxer.start()
        asyncio.run(main())
    except KeyboardInterrupt:
        print("keyboard interrupt")
        xmuxer.set()

    print("after asyncio.run(main())")
