|
| 1 | +#!/usr/bin/python3 |
| 2 | +import socket, argparse, threading, curses |
| 3 | +from string import printable |
| 4 | + |
| 5 | +SRV_IP = "" # Enter server here |
| 6 | +SRV_PORT = 6667 |
| 7 | +SRV_ADDR = (SRV_IP, SRV_PORT) |
| 8 | +SRV_NAME = "" # Enter server name here |
| 9 | +NICK = "" # Enter nick |
| 10 | +USERNAME = "" # Enter username |
| 11 | +REALNAME = "" # Enter real name |
| 12 | +PRINTABLE = list(map(ord, printable)) |
| 13 | +UTF = "utf-8" |
| 14 | + |
| 15 | +sockets = [] |
| 16 | +lines = [] |
| 17 | + |
| 18 | +def output(stdscr, msg): |
| 19 | + Y, X = stdscr.getyx() |
| 20 | + max_lines = stdscr.getmaxyx()[0] - 3 |
| 21 | + |
| 22 | + # Scrolling (if necessary) |
| 23 | + if len(lines) > max_lines: |
| 24 | + del lines[0] |
| 25 | + stdscr.clear() |
| 26 | + for i, line in enumerate(lines): |
| 27 | + stdscr.addstr(i, 0, line) |
| 28 | + |
| 29 | + stdscr.addstr(len(lines), 0, msg) |
| 30 | + lines.append(msg) |
| 31 | + stdscr.move(Y, X) # Move the cursor back to the start position |
| 32 | + stdscr.refresh() |
| 33 | + |
| 34 | +def listen(stdscr): |
| 35 | + while True: |
| 36 | + messages = sockets[0].recv(4096).decode(UTF) |
| 37 | + if messages.split(" ")[0] == "PING": |
| 38 | + pong = messages[0] + "O" + messages[2:] |
| 39 | + sockets[0].sendall(pong.encode(UTF)) |
| 40 | + output(stdscr, pong) |
| 41 | + messages = messages.split("\r\n") |
| 42 | + for message in messages: |
| 43 | + if message != "": |
| 44 | + output(stdscr, message) |
| 45 | + |
| 46 | +def main(stdscr): |
| 47 | + send = True |
| 48 | + inchannel = False |
| 49 | + channel = "" |
| 50 | + try: |
| 51 | + sock = socket.create_connection(SRV_ADDR) |
| 52 | + except socket.error in exc: |
| 53 | + print("Caught exception socket.error : {}".format(exc)) |
| 54 | + sockets.append(sock) |
| 55 | + sockets[0].sendall("NICK {}\r\n".format(NICK).encode(UTF)) |
| 56 | + sockets[0].sendall("USER {} 0 * :{}\r\n".format(USERNAME, REALNAME).encode(UTF)) |
| 57 | + t = threading.Thread(target=listen,args=(stdscr,)) |
| 58 | + t.daemon = True |
| 59 | + t.start() |
| 60 | + Ymax, Xmax = stdscr.getmaxyx() |
| 61 | + |
| 62 | + while True: |
| 63 | + stdscr.move(Ymax-1, 0) |
| 64 | + stdscr.clrtoeol() |
| 65 | + stdscr.addstr("> ") |
| 66 | + Y, X = stdscr.getyx() |
| 67 | + eol = X |
| 68 | + txt = [] |
| 69 | + |
| 70 | + while True: |
| 71 | + y, x = stdscr.getyx() |
| 72 | + c = stdscr.getch() |
| 73 | + |
| 74 | + if c in (13, 10): # \r or \n |
| 75 | + break |
| 76 | + elif c == curses.KEY_BACKSPACE: |
| 77 | + if x > X: |
| 78 | + eol -= 1 |
| 79 | + del txt[x-X-1] |
| 80 | + stdscr.move(y, x-1) |
| 81 | + stdscr.clrtoeol() |
| 82 | + stdscr.insstr("".join(txt[x-X-1:])) |
| 83 | + elif c == curses.KEY_LEFT: |
| 84 | + if x > X: |
| 85 | + stdscr.move(y, x-1) |
| 86 | + elif c == curses.KEY_RIGHT: |
| 87 | + if x < eol: |
| 88 | + stdscr.move(y, x+1) |
| 89 | + elif c == curses.KEY_END: |
| 90 | + stdscr.move(y, eol) |
| 91 | + elif c == curses.KEY_HOME: |
| 92 | + stdscr.move(y, X) |
| 93 | + elif c == curses.KEY_DC: # Delete key |
| 94 | + if x < eol: |
| 95 | + eol -= 1 |
| 96 | + del txt[x-X] |
| 97 | + stdscr.clrtoeol() |
| 98 | + stdscr.insstr("".join(txt[x-X:])) |
| 99 | + elif c in PRINTABLE: |
| 100 | + eol += 1 |
| 101 | + if x < eol: |
| 102 | + txt.insert(x-X, chr(c)) |
| 103 | + stdscr.insch(c) |
| 104 | + else: |
| 105 | + txt.append(chr(c)) |
| 106 | + stdscr.addch(c) |
| 107 | + stdscr.move(y, (x+1)) |
| 108 | + |
| 109 | + msg = "".join(txt) |
| 110 | + |
| 111 | + output(stdscr, "{} > {}".format(NICK, msg)) |
| 112 | + |
| 113 | + if msg and msg[0] == "/" and len(msg) > 1: |
| 114 | + msg = msg[1:] |
| 115 | + params = len(msg.split(" ")) - 1 |
| 116 | + command = msg.split(" ")[0].upper() |
| 117 | + if command == "JOIN" and params > 0: |
| 118 | + if msg.split(" ")[1][0] == "#": |
| 119 | + channel = msg.split(" ")[1] |
| 120 | + msg = "JOIN {}".format(channel) |
| 121 | + inchannel = True |
| 122 | + else: |
| 123 | + send = False |
| 124 | + elif command == "NICK" and params > 0: |
| 125 | + msg = "NICK {}".format(msg.split(" ")[1]) |
| 126 | + elif command == "PART" and inchannel == True: |
| 127 | + msg = "PART {}".format(channel) |
| 128 | + inchannel = False |
| 129 | + elif command == "QUIT": |
| 130 | + sockets[0].sendall("QUIT\r\n".encode(UTF)) |
| 131 | + break |
| 132 | + elif msg and msg[0] == "/" and len(msg) <= 1: |
| 133 | + send = False |
| 134 | + elif msg: |
| 135 | + if inchannel == True: |
| 136 | + msg = "PRIVMSG {} :{}\r\n".format(channel, msg) |
| 137 | + |
| 138 | + if send == True and msg: |
| 139 | + msg += "\r\n" |
| 140 | + sockets[0].sendall(msg.encode(UTF)) |
| 141 | + elif send == False and msg: |
| 142 | + output(stdscr, "Invalid command or parameter...") |
| 143 | + send = True # Reset the send flag |
| 144 | + |
| 145 | + sockets[0].shutdown(socket.SHUT_RDWR) |
| 146 | + sockets[0].close() |
| 147 | + stdscr.erase() |
| 148 | + #stdscr.refresh() |
| 149 | + del stdscr |
| 150 | + |
| 151 | +if __name__ == "__main__": |
| 152 | + curses.wrapper(main) |
0 commit comments