예제 #1
0
class ChatDialog(Dialog):
    """ dialog subclass that implements a chat screen with a list of IM buddies, transcript, response field,
        and send button and a cancel button
        """
        
    chat_thread = None
    
    def __init__(self,scr,title = "Chat"):
        """ takes the curses window to pop up over, title to display, will dynamically size to parent window """
        self.current_buddy = None
        self.current_server = None
        self.chat_connection = None
        self.win = None
        self.buddys = None
        self.messages = None
        self.reply = None
        self.reply_border = None
        self.config_button = None
        self.cancel = None
        self.config = {}
        self.status = {}
        self.children = []
        self.myname = None
        self.event_time = time.clock()
        self.title = title
        # load the config right away
        self.load_config()
        self.setparent(scr)
        self.resize()
        max_y,max_x = self.getparent().getmaxyx()
        min_y,min_x = self.getparent().getbegyx()
        Dialog.__init__(self,scr,"ChatDialog", max_y, max_x, [ Frame(title),
                                                            self.buddys,
                                                            self.messages,
                                                            self.reply_border,
                                                            self.reply,
                                                            self.config_button,
                                                            self.cancel], min_y, min_x)
        self.start_chat_thread()
    

    def __copy__(self):
        """ return a copy of this dialog """
        return ChatDialog(self.getparent(), self.title)
        
    def __del__(self):
        """ clean up the chat thread when we go away """
        self.stop_chat_thread()

    def start_chat_thread( self ):
        """ start the chat thread if we have configuration """
        if self.config:
            if not ChatDialog.chat_thread:
                ChatDialog.chat_thread = ChatThread(self.config["name"],self.config["password"],self.config["server"],int(self.config["port"]))
                ChatDialog.chat_thread.start()
            else:
                self.connect(self.config["name"],self.config["password"],self.config["server"],int(self.config["port"]))
                
    def save_messages( self ):
        """ save the message list to the chat transcript """
        if self.messages.editor:
            self.messages.editor.workfile.setReadOnly(False)
            self.messages.editor.save()
            self.messages.editor.workfile.setReadOnly(True)
            
    def stop_chat_thread( self ):
        """ stop the chat thread if one exists """
        if ChatDialog.chat_thread:
            self.handle_chat_events()
                                                            
    def render( self ):
        """ process events whenever render is called """
        if time.clock() - self.event_time > 0.5:
            self.handle_chat_events()
            self.event_time = time.clock()
        Dialog.render(self)
        
    def resize( self ):
        """ compute the postions of all of the components """
        max_y,max_x = self.getparent().getmaxyx()
        min_y,min_x = self.getparent().getbegyx()
        if self.win:
            del self.win
            self.win = None
        self.win = self.getparent().subwin(max_y,max_x,min_y,min_x)
        self.win.clear()
        self.win.keypad(1)
        pw = (max_x - 4)
        ph = (max_y - 4)
        cx = max_x / 2
        y = 1  
        if not self.buddys:
            self.buddys = ListBox("buddys",1,2,y,ph-3,pw/4,"Buddys", 0)
        else:
            self.buddys.setpos(2,y)
            self.buddys.setsize(ph-3,pw/4)
            
        if not self.messages:
            self.messages = FileEditorComponent("messages",2,(pw/4)+2,y,pw - pw/4,ph-3,"Transcript", DEFAULT_CHAT_FILE, False, True)
        else:
            self.messages.setpos((pw/4)+2,y)
            self.messages.setsize(ph-3,pw-pw/4)
            
        y += (ph-2)
        if not self.reply:
            self.reply = Prompt("reply",3,3,y,"",pw-2)
            self.reply_border = Frame( "", 2, y-1, pw, 3 )
        else:
            self.reply.setpos(3,y)
            self.reply.setsize(1,pw-2)
            self.reply_border.setpos( 2, y-1 )
            self.reply_border.setsize( pw, 3 )
            
        y += 2
        
        if not self.config_button:
            self.config_button = Button("config",4,20,y,"CONFIG",Component.CMP_KEY_NOP)
        else:
            self.config_button.setpos(2,y)
            
        if not self.cancel:
            self.cancel = Button("cancel",5,30,y,"EXIT",Component.CMP_KEY_CANCEL)
        else:
            self.cancel.setpos(2,y)
            
        distribute( [self.config_button,self.cancel], 2, pw )
        
        for c in self.children:
            c.setparent(self.win)

    def load_config(self):
        """ load the config file """                               
        self.conf_dir = os.path.expanduser("~/.pedchat")
        if not os.path.exists(self.conf_dir):
            os.mkdir(self.conf_dir)
        self.conf_path = self.conf_dir+"/config"
        if os.path.exists(self.conf_path):
            # load config 
            self.config = json.load(open(self.conf_path,"r"))
        else:
            self.config = {}
            
    def save_config(self):
        """ save the config file """
        if self.config:
            conf_file = open(self.conf_path,"w")
            # save config 
            json.dump(self.config, conf_file)
            conf_file.close()
            
    def write_message( self, message ):
        """ write a message to the current chat log """
        ed = self.messages.editor
        ed.workfile.setReadOnly(False)
        nLines = ed.workfile.numLines()
        for l in message.split("\n"):
            ed.workfile.insertLine(nLines,l+'\n')
            nLines += 1
        ed.workfile.setReadOnly(True)
        ed.rewrap(True)
        ed.goto(nLines-1,0)
        
        if ed.scr:
            ed.scr.refresh()
         

    def show_item( self, i ):
        """ for debugging, drill down into the event """
        if hasattr(i,"__dict__"):
            self.write_message(pprint.pformat(i.__dict__))
        elif isinstance(i, list) or isinstance(i, tuple):
            self.write_message("[\n")
            for k in i:
                self.show_item(k)
            self.write_message("]\n")
        else:
            self.write_message(pprint.pformat(i))
            
    def log_item( self, i ):
        """ for debugging, drill down into the event """
        if hasattr(i,"__dict__"):
            print >>open("./chat.log","a"),pprint.pformat(i.__dict__)
        elif isinstance(i, list) or isinstance(i, tuple):
            print >>open("./chat.log","a"),"["
            for k in i:
                self.log_item(k)
            print >>open("./chat.log","a"),"]"
        else:
            print >>open("./chat.log","a"),pprint.pformat(i)

    def set_buddy_list( self, buddys ):
        """ rebuild the buddy list """
        choice,selection = self.buddys.getvalue()
        bl = []
        for sl in buddys:
            if isinstance(sl,list) or isinstance(sl,tuple):
                for b in sl:
                    if isinstance(b,oscar.SSIGroup):
                        for u in b.users: 
                            bl.append("%s : %s : %s"%(u.name, self.status.get(u.name,"unknown"), b.name))
        if selection:
            n,s,g = selection[choice].split(":")
            for choice in range(0,len(bl)):
                if bl[choice].startswith(n) and bl[choice].endswith(g):
                    break
            else:
                choice = 0
        self.buddys.setvalue((choice,bl))
        
    def update_buddy( self, user ):
        """ update the status of a buddy """
        self.status[user.name] = user.icqStatus
        choice,selection = self.buddys.getvalue()
        if selection:
            for i in range(0,len(selection)):
                n,s,g = selection[i].split(":")
                if n.startswith(user.name):
                    selection[i] = n+": "+ user.icqStatus +" :"+g
                    break         
            self.buddys.setvalue((choice,selection))

    def offline_buddy( self, user ):
        """ update the status of a buddy to offline """
        self.status[user.name] = "offline"
        choice,selection = self.buddys.getvalue()
        if selection:
            for i in range(0,len(selection)):
                n,s,g = selection[i].split(":")
                if n.startswith(user.name):
                    selection[i] = n+": offline :"+g
                    break
            self.buddys.setvalue((choice,selection))
            
    def receive_message(self, user, multiparts, flags ):
        """ recieve a message and format it so we can output it """
        self.set_current_buddy( user.name )
        for p in multiparts:
            for mp in p:
                self.write_message( "%s: %s"%(user.name,BeautifulSoup(mp).get_text()) )

    def handle_chat_events(self):
        """ process all pending chat events """
        if ChatDialog.chat_thread:
            empty = False
            while not empty:
                try:
                    item = ChatDialog.chat_thread.events.get_nowait()
                    eid = item[0]
                    if eid == "initDone":
                        self.chat_connection = item[1]
                    elif eid == "gotBuddyList":
                        self.set_buddy_list( item[1] )
                    elif eid == "updateBuddy":
                        self.update_buddy( item[1] )
                    elif eid == "offlineBuddy":
                        self.offline_buddy( item[1] )
                    elif eid == "receiveMessage":
                        self.receive_message( item[1], item[2], item[3] )
                    elif eid == "gotSelfInfo":
                        self.myname = item[1].name
                        
#                    self.log_item(item)
                    
                    ChatDialog.chat_thread.events.task_done()
                except Queue.Empty:
                    empty = True
            self.save_messages()
                    
    def start_chat( self, buddy_ref ):
        """ start new chat by switching to a different transcript and current buddy """
        buddy,status,group = buddy_ref.split(":")
        buddy=buddy.strip()
        status=status.strip()
        group=group.strip()
        self.set_current_buddy( buddy )
        
        
    def set_current_buddy( self, buddy):
        """ set a new buddy as the current buddy """
        if buddy != self.current_buddy:
            self.save_messages()
            self.messages.setfilename(os.path.expanduser(os.path.join("~/.pedchat","%s.log"%(buddy))),-1)
            self.messages.render()
            self.current_buddy = buddy
            choice,selection = self.buddys.getvalue()
            if selection:
                for i in range(0,len(selection)):
                    if selection[i].startswith(buddy):
                        break
                else:
                    i = 0
                self.buddys.setvalue((i,selection))
            
    def send_message( self, user, message ):
        """ send a message to the user """
        self.chat_connection.sendMessage(user,message)
        self.write_message("%s: %s"%(self.myname,message))                                             
        
    def connect( self, username, password, host, port ):
        """ connect using different connection settings """
        if ChatDialog.chat_thread:
            if self.chat_connection:
                self.chat_connection.disconnect()
            self.messages.setfilename(DEFAULT_CHAT_FILE,0)
            self.buddys.setvalue((0,[]))
            ChatDialog.chat_thread.connect( username, password, host, port )
            
    def handle(self,ch):
        """ handles found file selection, populating the preview window, new searches, and opening a file """
        self.handle_chat_events()
        focus_index = self.current
        focus_field = self.focus_list[self.current][1]
        if ch in [keytab.KEYTAB_SPACE,keytab.KEYTAB_CR]:
            if focus_field == self.buddys:
                (selection,choices) = self.buddys.getvalue()
                # make the selected buddy current in chat
                self.start_chat(choices[selection])
                self.current = self.reply.getorder()-1
                ch = Component.CMP_KEY_NOP
            elif focus_field == self.config_button:
                conf_dialog = ChatConfigDialog(self,"Chat Config Dialog")
                values = conf_dialog.main()
                if values:
                    self.config = values
                    self.save_config()
                    self.current = self.buddys.getorder()-1
                    ch = Component.CMP_KEY_NOP
                    self.connect( values['name'], values['password'], values['server'], int(values['port']) )
            elif focus_field == self.reply and ch == keytab.KEYTAB_CR:
                # type in the reply
                self.send_message( self.current_buddy, self.reply.getvalue() )
                self.reply.setvalue("")
                self.reply.home()
                ch = Component.CMP_KEY_NOP
        elif ch in [keytab.KEYTAB_F04,keytab.KEYTAB_ALTK,keytab.KEYTAB_ALTE]:
            return ch

        ret_ch = Dialog.handle(self,ch)
        
        if ret_ch in [Component.CMP_KEY_CANCEL]:
            self.stop_chat_thread()
            ret_ch = keytab.KEYTAB_ALTK
               
        return ret_ch