Advertisement
MegaLoler

Chatango Client v0.3

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