Advertisement
MegaLoler

Chatango Client v0.2

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