Advertisement
MegaLoler

Chatango Client v0.4

May 2nd, 2012
868
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.54 KB | None | 0 0
  1. import ch
  2. import curses
  3. import thread
  4. import sys
  5. import base64
  6. import random
  7.  
  8. # Chatango client by MegaLoler
  9. # Chatango library by Lumirayz
  10. # version 0.4
  11.  
  12. #changes:
  13. #default echo to false because it causes some issues
  14. #fixed online list!  although it doesn't show up until someone says something
  15. #fixed a small bug that put the cursor in the wrong place when the user list updated
  16. #added junk to the todo list lol
  17.  
  18. #todo:
  19. #fix /part ing
  20. #make userlist appear immediately
  21. #add notation in the userlist for moderators/etc
  22. #make the lists on the left and right scrollable
  23. #moderator commands and features (like ips)
  24. #better styling (bold, underline, even blinking and stuff!)
  25. #deal with nonexistant rooms
  26. #add command history with up arrow
  27. #disconnection error handling and stuff
  28. #dynamic sizing
  29. #secret messages to individual people instead of everyone as well
  30. #custom skins
  31. #config file or somethin
  32. #cursor movement with arrow keys
  33. #nick name aliases
  34. #special page for pms
  35. #fix bugs
  36. #fix self pages not appearing with echo on
  37. #saving/loading settings
  38. #OUTPUT MACROS!  that would be epic
  39. #automated responses, and preprogrammed/saved messages, etc
  40. #away feature that tells people you are away if you are away and someone says yourname
  41. #log loading
  42. #name highlighting
  43.  
  44. activeRoom = None
  45. generalBuffer = []
  46. contents = ["", "", ""]
  47. focus = 1
  48. raw = False
  49. disguise = "lol"
  50. pages = {}
  51. echo = False
  52.  
  53. screen = curses.initscr()
  54. curses.noecho()
  55. curses.start_color()
  56.  
  57. HELP_MSG = "Press TAB to switch between the three text feilds at the bottom.  Type in room names on the left to connect to rooms.  Once connected, type in messages in the middle.  Type in private messages on the right.  To toggle between open rooms press \"`\".  To toggle between private message recipients press \"~\".  The selected recipient will be highlighted in red.  Type /commands in the middle when connected to a room to get a list of commands that you can use.  Press ESC twice to exit the client."
  58.  
  59. size = screen.getmaxyx()
  60. WIDTH = size[1]
  61. HEIGHT = size[0]
  62. HALF = WIDTH / 2
  63. QUARTER = WIDTH / 4
  64.  
  65. RED = 1
  66. YELLOW = 2
  67. GREEN = 3
  68. CYAN = 4
  69. BLUE = 5
  70. MAGENTA = 6
  71. NORMAL = 7
  72. SECRET = 8
  73.  
  74. curses.init_pair(RED, curses.COLOR_RED, curses.COLOR_BLACK)
  75. curses.init_pair(YELLOW, curses.COLOR_YELLOW, curses.COLOR_BLACK)
  76. curses.init_pair(GREEN, curses.COLOR_GREEN, curses.COLOR_BLACK)
  77. curses.init_pair(CYAN, curses.COLOR_CYAN, curses.COLOR_BLACK)
  78. curses.init_pair(BLUE, curses.COLOR_BLUE, curses.COLOR_BLACK)
  79. curses.init_pair(MAGENTA, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
  80. curses.init_pair(NORMAL, curses.COLOR_WHITE, curses.COLOR_BLACK)
  81. curses.init_pair(SECRET, curses.COLOR_BLACK, curses.COLOR_WHITE)
  82.  
  83. def printLine(string, color=NORMAL):
  84.     generalBuffer.append((string, color))
  85.     while len(generalBuffer) > HEIGHT - 3: del generalBuffer[0]
  86.  
  87. def addMessage(string, color=NORMAL):
  88.     for string in string.split("\n"):
  89.         buf = ""
  90.         for i in string:
  91.             buf += i
  92.             if len(buf) >= HALF:
  93.                 printLine(buf, color)
  94.                 buf = ""
  95.         if buf != "": printLine(buf, color)
  96.     if activeRoom == None:
  97.         drawChat()
  98.         screen.refresh()
  99.  
  100. def hexToDec(h):
  101.     h = h.lower()
  102.     if h == "f":
  103.         return 15
  104.     elif h == "e":
  105.         return 14
  106.     elif h == "d":
  107.         return 13
  108.     elif h == "c":
  109.         return 12
  110.     elif h == "b":
  111.         return 11
  112.     elif h == "a":
  113.         return 10
  114.     else:
  115.         try:
  116.             return int(h)
  117.         except:
  118.             return 0
  119.  
  120. def hexToAnsi(col):
  121.     if col[0] == "#": col = col[1:]
  122.     if len(col) == 3:
  123.         r = hexToDec(col[0])
  124.         g = hexToDec(col[1])
  125.         b = hexToDec(col[2])
  126.     elif len(col) == 6:
  127.         r = hexToDec(col[0])
  128.         g = hexToDec(col[2])
  129.         b = hexToDec(col[4])
  130.     else:
  131.         return NORMAL
  132.  
  133.     r = r > 7
  134.     g = g > 7
  135.     b = b > 7
  136.  
  137.     if r and g and b:
  138.         return NORMAL
  139.     if r and g:
  140.         return YELLOW
  141.     if g and b:
  142.         return CYAN
  143.     if r and b:
  144.         return MAGENTA
  145.     if r:
  146.         return RED
  147.     if g:
  148.         return GREEN
  149.     if b:
  150.         return BLUE
  151.     else:
  152.         return NORMAL
  153.        
  154. def printLineR(r, string, color=NORMAL):
  155.     r.chatBuffer.append((string, color))
  156.     while len(r.chatBuffer) > HEIGHT - 3: del r.chatBuffer[0]
  157.  
  158. def addMessageR(r, string, color=NORMAL):
  159.     for string in string.split("\n"):
  160.         buf = ""
  161.         for i in string:
  162.             buf += i
  163.             if len(buf) >= HALF:
  164.                 printLineR(r, buf, color)
  165.                 buf = ""
  166.         if buf != "": printLineR(r, buf, color)
  167.     if activeRoom == r:
  168.         drawChat()
  169.         screen.refresh()
  170.     else:
  171.         r.new = True
  172.         drawRooms()
  173.         screen.refresh()
  174.  
  175. def page(user, message):
  176.     if user.lower() in pages.keys():
  177.         pages[user.lower()].append(message)
  178.     else:
  179.         pages[user.lower()] = [message]
  180.  
  181. def logMessage(message):
  182.     f = open("log.raw", "a")
  183.     f.write(message.user.name + ": " + message.raw + "\n")
  184.     f.close()
  185.  
  186. def secretEncrypt(message, k):
  187.     message = k + message
  188.     message = base64.b64encode(message)
  189.     insert = base64.b64encode(k*len(k))[:-2]
  190.     location = random.randint(1, len(message))
  191.     first = message[:location]
  192.     if location == len(message):
  193.         last = ""
  194.     else:
  195.         last = message[location:]
  196.     message = first + insert + last
  197.     message = base64.b64encode(message + k)
  198.     return message
  199.  
  200. def secretDecrypt(message, k):
  201.     m = message
  202.     try:
  203.         message = base64.b64decode(message)[:-len(k)]
  204.         insert = base64.b64encode(k*len(k))[:-2]
  205.         location = message.find(insert)
  206.         first = message[:location]
  207.         if location == len(message):
  208.             last = ""
  209.         else:
  210.             last = message[location + len(insert):]
  211.         message = first + last
  212.         message = base64.b64decode(message)
  213.         if message.startswith(k):
  214.             return message[len(k):]
  215.         else:
  216.             raise Exception
  217.     except:
  218.         return "ERROR DECRYPTING: " + m
  219.  
  220. def checkSecret(room, user, message):
  221.     if message.raw.find("<msg=\"") == -1: return False
  222.     msg = message.raw.split("<msg=\"")[1]
  223.     msg2 = ""
  224.     p = None
  225.     for char in msg:
  226.         if char == "\"" and p != "\\": break
  227.         if char == "\\":
  228.             if p == "\\": msg2 += char
  229.         else:
  230.             msg2 += char
  231.         p = char
  232.     addMessageR(room, user.name + ": " + secretDecrypt(msg2, user.name.lower()), SECRET)
  233.     return True
  234.  
  235. class RoomManager(ch.RoomManager):
  236.     def onHistoryMessage(self, room, user, message):
  237.         checkSecret(room, user, message)
  238.         addMessageR(room, user.name + ": " + message.body, hexToAnsi(message.fontColor))
  239.         drawFields()
  240.         screen.refresh()
  241.        
  242.     def onMessage(self, room, user, message):
  243.         logMessage(message)
  244.         if checkSecret(room, user, message) or not echo or user.name.lower() != NICK.lower(): addMessageR(room, user.name + ": " + message.body, hexToAnsi(message.fontColor))
  245.         if user.name.lower() in pages.keys():
  246.             for i in pages[user.name.lower()]: room.message("(PAGE) @" + user.name + ": " + i)
  247.             del pages[user.name.lower()]
  248.         drawFields()
  249.         screen.refresh()
  250.    
  251.     def onPMMessage(self, pm, user, body):
  252.         addMessageR(room, "@" + NICK + ": " + user.name + ": " + body, NORMAL)
  253.         drawFields()
  254.         screen.refresh()
  255.    
  256.     def onLeave(self, room, user):
  257.         addMessageR(room, user.name + " left the room.")
  258.         drawOnline()
  259.         drawFields()
  260.         screen.refresh()
  261.    
  262.     def onJoin(self, room, user):
  263.         addMessageR(room, user.name + " entered the room.")
  264.         drawOnline()
  265.         drawFields()
  266.         screen.refresh()
  267.    
  268.     def onConnect(self, room):
  269.         addMessageR(room, "You have connected to " + room.name + ".")
  270.         drawFields()
  271.         screen.refresh()
  272.    
  273.     def onUserCountChange(self, room):
  274.         drawOnline()
  275.         drawFields()
  276.         screen.refresh()
  277.  
  278. def centerString(string, length):
  279.     off = length - len(string)
  280.     if off > 0:
  281.         l = off / 2
  282.         if off % 2:
  283.             r = l + 1
  284.         else:
  285.             r = l
  286.         return " " * l + string + " " * r
  287.     elif off < 0:
  288.         return string[:length]
  289.  
  290. def leftString(string, length):
  291.     if len(string) > length:
  292.         return string[:length]
  293.     else:
  294.         off = length - len(string)
  295.         return string  + " " * off
  296.  
  297. def rightString(string, length):
  298.     if len(string) > length:
  299.         return string[:length]
  300.     else:
  301.         off = length - len(string)
  302.         return string + off * " "
  303.  
  304. def fieldString(string, length):
  305.     if len(string) > length:
  306.         return string[-length:]
  307.     else:
  308.         off = length - len(string)
  309.         return string + off * " "
  310.  
  311. def fieldLength(string, length):
  312.     if len(string) > length:
  313.         return length
  314.     else:
  315.         return len(string)
  316.  
  317. def drawRooms():
  318.     y = 0
  319.     for y in range(len(chatango.roomnames)):
  320.         if y >= HEIGHT - 2: break
  321.         room = list(chatango.roomnames)[y]
  322.         if chatango.getRoom(room).new:
  323.             attr = curses.color_pair(YELLOW) | curses.A_BOLD
  324.         else:
  325.             attr = curses.color_pair(NORMAL)
  326.         if room == activeRoom.name: attr |= curses.A_REVERSE
  327.         screen.addstr(y + 1, 0, leftString(room, QUARTER), attr)
  328.     if len(chatango.roomnames) > 0: y += 1
  329.     while y < HEIGHT - 2:
  330.         screen.addstr(y + 1, 0, leftString("", QUARTER), curses.color_pair(NORMAL))
  331.         y += 1
  332.  
  333. def drawOnline():
  334.     if not activeRoom:
  335.         users = []
  336.     else:
  337.         users = sorted(activeRoom.usernames)
  338.     y = 0
  339.     for y in range(len(users)):
  340.         if y >= HEIGHT - 2: break
  341.         nick = users[y]
  342.         if activeRoom and activeRoom.activeUser == users[y]:
  343.             attr = curses.color_pair(RED) | curses.A_REVERSE
  344.         else:
  345.             attr = curses.color_pair(NORMAL)
  346.         if nick == activeRoom.name: attr |= curses.A_REVERSE
  347.         screen.addstr(y + 1, HALF + QUARTER, rightString(nick, QUARTER), attr)
  348.     if len(users) > 0: y += 1
  349.     while y < HEIGHT - 2:
  350.         screen.addstr(y + 1, HALF + QUARTER, rightString("", QUARTER), curses.color_pair(NORMAL))
  351.         y += 1
  352.  
  353. def drawChat():
  354.     if not activeRoom:
  355.         chatBuffer = generalBuffer
  356.     else:
  357.         chatBuffer = activeRoom.chatBuffer
  358.     y = 0
  359.     for y in range(len(chatBuffer)):
  360.         if y >= HEIGHT - 3: break
  361.         message = chatBuffer[y]
  362.         screen.addstr(y + 1, QUARTER, leftString(message[0], HALF), curses.color_pair(message[1]))
  363.     if len(chatBuffer) > 0: y += 1
  364.     while y < HEIGHT - 3:
  365.         screen.addstr(y + 1, QUARTER, leftString("", HALF), curses.color_pair(NORMAL))
  366.         y += 1
  367.  
  368. def drawField(y, x, content, width, selected, color=NORMAL):
  369.     attr = curses.color_pair(color)
  370.     if selected: attr |= curses.A_REVERSE
  371.     screen.addstr(y, x, fieldString(content, width), attr)
  372.     if selected:
  373.         return (y, x + fieldLength(content, width))
  374.     else:
  375.         return None
  376.  
  377. def drawFields():
  378.     if activeRoom:
  379.         color = hexToAnsi(chatango.user.fontColor)
  380.     else:
  381.         color = NORMAL
  382.     f1 = drawField(HEIGHT - 1, 0, contents[0], QUARTER - 1, focus == 0)
  383.     f2 = drawField(HEIGHT - 1, QUARTER, contents[1], HALF - 1, focus == 1, color)
  384.     f3 = drawField(HEIGHT - 1, HALF + QUARTER, contents[2], QUARTER - 1, focus == 2, color)
  385.     if f1:
  386.         screen.move(f1[0], f1[1])
  387.     elif f2:
  388.         screen.move(f2[0], f2[1])
  389.     elif f3:
  390.         screen.move(f3[0], f3[1])
  391.        
  392. def drawHeaders():
  393.     screen.addstr(0, 0, centerString("ROOMS", QUARTER), curses.color_pair(NORMAL) | curses.A_REVERSE)
  394.     screen.addstr(0, QUARTER, centerString("CHATANGO", HALF), curses.color_pair(NORMAL) | curses.A_REVERSE)
  395.     screen.addstr(0, HALF + QUARTER, centerString("ONLINE", QUARTER), curses.color_pair(NORMAL) | curses.A_REVERSE)
  396.  
  397. def drawContent():
  398.     drawRooms()
  399.     drawOnline()
  400.     drawChat()
  401.  
  402. def drawInterface():
  403.     drawHeaders()
  404.     drawContent()
  405.     drawFields()
  406.     screen.refresh()
  407.  
  408. def alreadyConnected(room):
  409.     chatango.getRoom(room)
  410.    
  411. def switchRoom(room):
  412.     global activeRoom
  413.     activeRoom = room
  414.     room.new = False
  415.     drawContent()
  416.     drawHeaders()
  417.    
  418. def hide(message, hidden):
  419.     if hidden:
  420.         return "*" * len(message)
  421.     else:
  422.         return message
  423.  
  424. def dialogue(prompt, hidden=False):
  425.     screen.border(0)
  426.     screen.addstr(HEIGHT / 2, HALF - len(prompt), prompt)
  427.     screen.refresh()
  428.    
  429.     RESULT = ""
  430.     while True:
  431.         c = screen.getch()
  432.         if c == 10 and RESULT != "":
  433.             RESULT = RESULT.strip()
  434.             break
  435.         elif c == 127:
  436.             if RESULT != "": RESULT = RESULT[:-1]
  437.             screen.addstr(HEIGHT / 2, HALF, leftString(hide(RESULT, hidden), HALF))
  438.             screen.border(0)
  439.             screen.move(HEIGHT / 2, HALF + len(RESULT))
  440.             screen.refresh()
  441.         elif c == 27:
  442.             curses.endwin()
  443.             sys.exit()
  444.         else:
  445.             if len(RESULT) < HALF - 1:
  446.                 RESULT += chr(c)
  447.                 if hidden:
  448.                     display = "*" * len(RESULT)
  449.                 else:
  450.                     display = RESULT
  451.                 screen.addstr(HEIGHT / 2, HALF, leftString(hide(RESULT, hidden), HALF))
  452.                 screen.border(0)
  453.                 screen.move(HEIGHT / 2, HALF + len(RESULT))
  454.                 screen.refresh()
  455.    
  456.     screen.erase()
  457.     return RESULT
  458.  
  459. NICK = dialogue("Enter your username: ")
  460. PASS = dialogue("Enter your password: ", True)
  461.  
  462. chatango = RoomManager(NICK, PASS)
  463. chatango._userlistMode = ch.Userlist_All
  464. thread.start_new_thread(chatango.main, ())
  465.  
  466. drawInterface()
  467.  
  468. addMessage("Welcome to MegaLoler's Chatango client!\nJoin a room to get started.")
  469.  
  470. esc = False
  471. while True:
  472.     drawFields() #perhaps make more effient later
  473.     c = screen.getch()
  474.     if c == 10: # Enter
  475.         msg = contents[focus].strip()
  476.         if msg != "":
  477.             contents[focus] = ""
  478.             if focus == 0:
  479.                 r = alreadyConnected(msg)
  480.                 if r == None:
  481.                     chatango.joinRoom(msg)
  482.                     room = chatango.getRoom(msg)
  483.                     room.chatBuffer = []
  484.                     room.activeUser = None
  485.                     room.new = False
  486.                     switchRoom(room)
  487.                 else:
  488.                     switchRoom(r)
  489.             elif focus == 1:
  490.                 if activeRoom:
  491.                     if msg[0] == "/":
  492.                         msg = msg[1:]
  493.                         if msg != "":
  494.                             parts = msg.split(" ")
  495.                             cmd = parts[0].lower()
  496.                             if len(parts) == 1:
  497.                                 pars = []
  498.                             else:
  499.                                 pars = parts[1:]
  500.                             par = " ".join(pars)
  501.                             if cmd == "color":
  502.                                 chatango.setFontColor(par)
  503.                                 drawChat()
  504.                             elif cmd == "namecolor":
  505.                                 chatango.setNameColor(par)
  506.                                 drawOnline()
  507.                             elif cmd == "msg":
  508.                                 activeRoom.message("<msg=\"" + secretEncrypt(par, NICK.lower()) + "\"></msg>" + disguise, True)
  509.                             elif cmd == "help":
  510.                                 addMessageR(activeRoom, HELP_MSG)
  511.                             elif cmd == "page":
  512.                                 if len(pars) > 1:
  513.                                     page(pars[0], " ".join(pars[1:]))
  514.                                     addMessageR(activeRoom, "The message will be sent when " + pars[0] + " is next seen.")
  515.                                 else:
  516.                                     addMessageR(activeRoom, "The syntax is /page [user] [message].")
  517.                             elif cmd == "pages":
  518.                                 if par == "":
  519.                                     if len(pages.keys()):
  520.                                         addMessageR(activeRoom, "Pages are queued for the following users: " + ", ".join(pages.keys()) + ".")
  521.                                     else:
  522.                                         addMessageR(activeRoom, "No pages are queued.")
  523.                                 else:
  524.                                     if par.lower() in pages.keys():
  525.                                         addMessageR(activeRoom, "The user " + par + " has the following pages queued:\n" + "\n".join(pages[par.lower()]))
  526.                                     else:
  527.                                         addMessageR(activeRoom, "The user " + par + " does not have any pages queued.")
  528.                             elif cmd == "disguise":
  529.                                 disguise = par
  530.                                 addMessageR(activeRoom, "Disguise set to \"" + disguise + "\".")
  531.                             elif cmd == "clear":
  532.                                 activeRoom.chatBuffer = []
  533.                                 addMessageR(activeRoom, "Cleared the screen.")
  534.                             elif cmd == "raw":
  535.                                 raw = not raw
  536.                                 if raw:
  537.                                     addMessageR(activeRoom, "Raw mode is now ON.")
  538.                                 else:
  539.                                     addMessageR(activeRoom, "Raw mode is now OFF.")
  540.                             elif cmd == "echo":
  541.                                 echo = not echo
  542.                                 if echo:
  543.                                     addMessageR(activeRoom, "Echo mode is now ON.")
  544.                                 else:
  545.                                     addMessageR(activeRoom, "Echo mode is now OFF.")
  546.                             elif cmd == "part":
  547.                                 for i in range(len(chatango._rooms)):
  548.                                     if list(chatango.roomnames)[i] == activeRoom.name: break
  549.                                 chatango.leaveRoom(activeRoom.name)
  550.                                 if len(chatango.roomnames) > 0:
  551.                                     i %= len(chatango.roomnames)
  552.                                     switchRoom(list(chatango.rooms)[i])
  553.                                 else:
  554.                                     activeRoom = None
  555.                                     drawContent()
  556.                                
  557.                             else:
  558.                                 addMessageR(activeRoom, "Commands: /color [color] (sets your font color), /namecolor [color] (sets your name color), /part (disconnect from the current room), /help (get help on how to use this client), /raw (toggles raw mode which allows you to type with html formatting), /clear (clears the screen), /msg [message] (sends a secret message that only special people can see, and the message will appear to others as defined by the /disguise command to obfuscate the fact that you are sending secret messages), /disguise [message] (sets the disguise message for use with the /msg command), /page [user] [message] (queue a message that will be sent when a certain user is seen), /pages (user) (lists any currently queued pages), /echo (toggles echo mode which echos user input immediately rather than waiting for it to echo from the server)")
  559.                     else:
  560.                         activeRoom.message(msg, raw)
  561.                         if echo: addMessageR(activeRoom, NICK + ": " + msg, hexToAnsi(chatango.user.fontColor))
  562.                 else:
  563.                     if msg == "/help":
  564.                         addMessage(HELP_MSG)
  565.                     else:
  566.                         addMessage("You are not connected to any rooms!  Type /help for help.")
  567.             elif focus == 2:
  568.                 if activeRoom and activeRoom.activeUser:
  569.                     chatango.pm.message(ch.User(activeRoom.activeUser), msg)
  570.                     addMessageR(activeRoom, "@" + activeRoom.activeUser + ": " + NICK + ": " + msg, hexToAnsi(chatango.user.fontColor))
  571.             drawFields()
  572.             screen.refresh()
  573.     elif c == 9: # Tab
  574.         focus = (focus + 1) % 3
  575.         drawFields()
  576.         screen.refresh()
  577.     elif c == 96: # `
  578.         if len(chatango.roomnames) > 0:
  579.             for i in range(len(chatango.roomnames)):
  580.                 if list(chatango.roomnames)[i] == activeRoom.name: break
  581.             i = (i + 1) % len(chatango.roomnames)
  582.             switchRoom(list(chatango.rooms)[i])
  583.     elif c == 126: # ~
  584.         if activeRoom:
  585.             if len(activeRoom.usernames) > 0:
  586.                 users = sorted(activeRoom.usernames)
  587.                 for i in range(len(users)):
  588.                     if users[i] == activeRoom.activeUser: break
  589.                 i = (i + 1) % (len(activeRoom.usernames))
  590.                 activeRoom.activeUser = users[i]
  591.                 drawOnline()
  592.     elif c == 127: # Backspace
  593.         if contents[focus] != "": contents[focus] = contents[focus][:-1]
  594.         drawFields()
  595.         screen.refresh()
  596.     elif c == 27: # Esc
  597.         if esc:
  598.             break
  599.         else:
  600.             esc = True
  601.             continue
  602.     else:
  603.         contents[focus] += chr(c)
  604.         drawFields()
  605.         screen.refresh()
  606.     esc = False
  607.  
  608. chatango.stop()
  609. curses.endwin()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement