Network/IRC/IrcChannelWidget.py

changeset 2253
7ba2af1ff785
parent 2252
1fc32bd13be3
child 2254
14f923d13971
--- a/Network/IRC/IrcChannelWidget.py	Fri Dec 07 19:48:23 2012 +0100
+++ b/Network/IRC/IrcChannelWidget.py	Sun Dec 09 12:19:58 2012 +0100
@@ -9,7 +9,7 @@
 
 import re
 
-from PyQt4.QtCore import pyqtSlot, pyqtSignal, QDateTime, QPoint, QFileInfo
+from PyQt4.QtCore import pyqtSlot, pyqtSignal, QDateTime, QPoint, QFileInfo, QTimer
 from PyQt4.QtGui import QWidget, QListWidgetItem, QIcon, QPainter, QMenu, QApplication
 
 from E5Gui import E5MessageBox, E5FileDialog
@@ -134,6 +134,30 @@
         painter.drawPixmap(0, 0, pix2)
         painter.end()
         return QIcon(pix1)
+    
+    def parseWhoFlags(self, flags):
+        """
+        Public method to parse the user flags reported by a WHO command.
+        
+        @param flags user flags as reported by WHO (string)
+        """
+        # H The user is not away.
+        # G The user is set away.
+        # * The user is an IRC operator.
+        # @ The user is a channel op in the channel listed in the first field.
+        # + The user is voiced in the channel listed.
+        if flags.endswith("@"):
+            privilege = IrcUserItem.Operator
+        elif flags.endswith("+"):
+            privilege = IrcUserItem.Voice
+        else:
+            privilege = IrcUserItem.Normal
+        if "*" in flags:
+            privilege = IrcUserItem.Admin
+        if flags.startswith("G"):
+            privilege |= IrcUserItem.Away
+        self.__privilege = privilege
+        self.__setIcon()
 
 
 class IrcChannelWidget(QWidget, Ui_IrcChannelWidget):
@@ -155,17 +179,6 @@
     LeaveIndicator = "<--"
     MessageIndicator = "***"
     
-    # TODO: add context menu to users list with these entries:
-    #       Whois
-    #       Private Message
-    # TODO: add "Auto WHO" functionality (WHO <channel> %nf)
-    #       The possible combinations for this field are listed below:
-    #       H The user is not away.
-    #       G The user is set away.
-    #       * The user is an IRC operator.
-    #       @ The user is a channel op in the channel listed in the first field.
-    #       + The user is voiced in the channel listed.
-    # TODO: Check away indication in the user list
     def __init__(self, parent=None):
         """
         Constructor
@@ -194,7 +207,6 @@
             # :foo_!n=foo@foohost.bar.net PRIVMSG #eric-ide :some long message
             (re.compile(r":([^!]+).*\sPRIVMSG\s([^ ]+)\s:(.*)"), self.__message),
             # :foo_!n=foo@foohost.bar.net JOIN :#eric-ide
-            # :detlev_!~detlev@mnch-5d876cfa.pool.mediaWays.net JOIN #eric-ide
             (re.compile(r":([^!]+)!([^ ]+)\sJOIN\s:?([^ ]+)"), self.__userJoin),
             # :foo_!n=foo@foohost.bar.net PART #eric-ide :part message
             (re.compile(r":([^!]+).*\sPART\s([^ ]+)\s:(.*)"), self.__userPart),
@@ -206,11 +218,15 @@
             (re.compile(r":([^!]+).*\sQUIT\s*"), self.__userQuit),
             # :foo_!n=foo@foohost.bar.net NICK :newnick
             (re.compile(r":([^!]+).*\sNICK\s:(.*)"), self.__userNickChange),
-            # :barty!n=foo@foohost.bar.net MODE #eric-ide +o foo_
-            (re.compile(r":([^!]+).*\sMODE\s([^ ]+)\s([^ ]+)\s([^ ]+).*"),
+            # :foo_!n=foo@foohost.bar.net MODE #eric-ide +o foo_
+            (re.compile(r":([^!]+).*\sMODE\s([^ ]+)\s([+-][ovO]+)\s([^ ]+).*"),
                 self.__setUserPrivilege),
+            # :cameron.freenode.net MODE #testeric +ns
+            (re.compile(r":([^ ]+)\sMODE\s([^ ]+)\s(.+)"), self.__updateChannelModes),
             # :sturgeon.freenode.net 301 foo_ bar :Gone away for now
             (re.compile(r":.*\s301\s([^ ]+)\s([^ ]+)\s:(.+)"), self.__userAway),
+            # :sturgeon.freenode.net 315 foo_ #eric-ide :End of /WHO list.
+            (re.compile(r":.*\s315\s[^ ]+\s([^ ]+)\s:(.*)"), self.__whoEnd),
             # :zelazny.freenode.net 324 foo_ #eric-ide +cnt
             (re.compile(r":.*\s324\s.*\s([^ ]+)\s(.+)"), self.__channelModes),
             # :zelazny.freenode.net 328 foo_ #eric-ide :http://www.buggeroff.com/
@@ -221,13 +237,24 @@
             (re.compile(r":.*\s332\s.*\s([^ ]+)\s:(.*)"), self.__setTopic),
             # :zelazny.freenode.net foo_ 333 #eric-ide foo 1353089020
             (re.compile(r":.*\s333\s.*\s([^ ]+)\s([^ ]+)\s(\d+)"), self.__topicCreated), 
+            # :cameron.freenode.net 352 detlev_ #eric-ide ~foo foohost.bar.net cameron.freenode.net foo_ H :0 Foo Bar
+            (re.compile(r":.*\s352\s[^ ]+\s([^ ]+)\s([^ ]+)\s([^ ]+)\s[^ ]+\s([^ ]+)"
+                        r"\s([^ ]+)\s:\d+\s(.*)"), self.__whoEntry),
             # :zelazny.freenode.net 353 foo_ @ #eric-ide :@user1 +user2 user3
             (re.compile(r":.*\s353\s.*\s.\s([^ ]+)\s:(.*)"), self.__userList),
+            # :sturgeon.freenode.net 354 foo_ 42 ChanServ H@
+            (re.compile(r":.*\s354\s[^ ]+\s42\s([^ ]+)\s(.*)"), self.__autoWhoEntry),
             # :zelazny.freenode.net 366 foo_ #eric-ide :End of /NAMES list.
             (re.compile(r":.*\s366\s.*\s([^ ]+)\s:(.*)"), self.__ignore),
             # :sturgeon.freenode.net 704 foo_ index :Help topics available to users:
             (re.compile(r":.*\s70[456]\s[^ ]+\s([^ ]+)\s:(.*)"), self.__help),
         ]
+        
+        self.__autoWhoTemplate = "WHO {0} %tnf,42"
+        self.__autoWhoTimer = QTimer()
+        self.__autoWhoTimer.setSingleShot(True)
+        self.__autoWhoTimer.timeout.connect(self.__sendAutoWhoCommand)
+        self.__autoWhoRequested = False
     
     @pyqtSlot()
     def on_messageEdit_returnPressed(self):
@@ -261,6 +288,7 @@
                 else:
                     self.sendData.emit("PRIVMSG " + self.__name + " :" + msg)
             self.messageEdit.clear()
+            self.unsetMarkerLine()
     
     def requestLeave(self):
         """
@@ -629,6 +657,151 @@
         
         return False
     
+    def __updateChannelModes(self, match):
+        """
+        Private method to handle a message reporting the channel modes.
+        
+        @param match match object that matched the pattern
+        @return flag indicating whether the message was handled (boolean)
+        """
+        # group(1)  user or server
+        # group(2)  channel
+        # group(3)  modes and parameter list
+        if match.group(2).lower() == self.__name:
+            nick = match.group(1)
+            modesParameters = match.group(3).split()
+            modeString = modesParameters.pop(0)
+            isPlus = True
+            message = ""
+            for mode in modeString:
+                if mode == "+":
+                    isPlus = True
+                    continue
+                elif mode == "-":
+                    isPlus = False
+                    continue
+                elif mode == "a":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'anonymous'.").format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} removes the 'anonymous' mode from the channel.").format(
+                            nick)
+                elif mode == "b":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets a ban on {1}.").format(
+                            nick, modesParameters.pop(0))
+                    else:
+                        message = self.trUtf8(
+                            "{0} removes the ban on {1}.").format(
+                            nick, modesParameters.pop(0))
+                elif mode == "c":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'no colors allowed'.").format(
+                            nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'allow color codes'.").format(
+                            nick)
+                elif mode == "e":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets a ban exception on {1}.").format(
+                            nick, modesParameters.pop(0))
+                    else:
+                        message = self.trUtf8(
+                            "{0} removes the ban exception on {1}.").format(
+                            nick, modesParameters.pop(0))
+                elif mode == "i":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'invite only'.").format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} removes the 'invite only' mode from the channel."
+                            ).format(nick)
+                elif mode == "k":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel key to '{1}'.").format(
+                            nick, modesParameters.pop(0))
+                    else:
+                        message = self.trUtf8(
+                            "{0} removes the channel key.").format(nick)
+                elif mode == "l":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel limit to %n nick(s).", "",
+                            int(modesParameters.pop(0))).format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} removes the channel limit.").format(nick)
+                elif mode == "m":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'moderated'.").format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'unmoderated'.").format(nick)
+                elif mode == "n":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'no messages from outside'."
+                            ).format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'allow messages from outside'."
+                            ).format(nick)
+                elif mode == "p":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'private'.").format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'public'.").format(nick)
+                elif mode == "q":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'quiet'.").format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} removes the 'quiet' mode from the channel.").format(
+                            nick)
+                elif mode == "r":
+                    continue
+                elif mode == "s":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'secret'.").format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} sets the channel mode to 'visible'.").format(nick)
+                elif mode == "t":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} switches on 'topic protection'.").format(nick)
+                    else:
+                        message = self.trUtf8(
+                            "{0} switches off 'topic protection'.").format(nick)
+                elif mode == "I":
+                    if isPlus:
+                        message = self.trUtf8(
+                            "{0} sets invitation mask {1}.").format(
+                            nick, modesParameters.pop(0))
+                    else:
+                        message = self.trUtf8(
+                            "{0} removes the invitation mask {1}.").format(
+                            nick, modesParameters.pop(0))
+                
+                self.__addManagementMessage(self.trUtf8("Mode"), message)
+            
+            return True
+        
+        return False
+    
     def __setUserPrivilege(self, match):
         """
         Private method to handle a change of user privileges for the channel.
@@ -640,9 +813,9 @@
             itm = self.__findUser(match.group(4))
             if itm:
                 itm.changePrivilege(match.group(3))
-                self.__addManagementMessage(IrcChannelWidget.MessageIndicator,
-                    self.trUtf8("{0} sets mode for {1}: {2}.").format(
-                        match.group(1), match.group(4), match.group(3)))
+            self.__addManagementMessage(IrcChannelWidget.MessageIndicator,
+                self.trUtf8("{0} sets mode for {1}: {2}.").format(
+                    match.group(1), match.group(4), match.group(3)))
             return True
         
         return False
@@ -970,3 +1143,81 @@
         @param evt reference to the show event (QShowEvent)
         """
         self.__hidden = False
+    
+    def initAutoWho(self):
+        """
+        Public method to initialize the Auto Who system.
+        """
+        if Preferences.getIrc("AutoUserInfoLookup"):
+            self.__autoWhoTimer.setInterval(
+                Preferences.getIrc("AutoUserInfoInterval") * 1000)
+            self.__autoWhoTimer.start()
+    
+    @pyqtSlot()
+    def __sendAutoWhoCommand(self):
+        """
+        Private slot to send the WHO command to update the users list.
+        """
+        if self.usersList.count() <= Preferences.getIrc("AutoUserInfoMax"):
+            self.__autoWhoRequested = True
+            self.sendData.emit(self.__autoWhoTemplate.format(self.__name))
+    
+    def __autoWhoEntry(self, match):
+        """
+        Private method to handle a WHO entry returned by the server as requested
+        automatically.
+        
+        @param match match object that matched the pattern
+        @return flag indicating whether the message was handled (boolean)
+        """
+        # group(1)  nick
+        # group(2)  user flags
+        if self.__autoWhoRequested:
+            itm = self.__findUser(match.group(1))
+            if itm:
+                itm.parseWhoFlags(match.group(2))
+            return True
+        
+        return False
+    
+    def __whoEnd(self, match):
+        """
+        Private method to handle the end of the WHO list.
+        
+        @param match match object that matched the pattern
+        @return flag indicating whether the message was handled (boolean)
+        """
+        if match.group(1).lower() == self.__name:
+            if self.__autoWhoRequested:
+                self.__autoWhoRequested = False
+                self.initAutoWho()
+            else:
+                self.__addManagementMessage(self.trUtf8("Who"),
+                    self.trUtf8("End of /WHO list for {0}.").format(match.group(1)))
+            return True
+        
+        return False
+    
+    def __whoEntry(self, match):
+        """
+        Private method to handle a WHO entry returned by the server as requested
+        manually.
+        
+        @param match match object that matched the pattern
+        @return flag indicating whether the message was handled (boolean)
+        """
+        # group(1)  channel
+        # group(2)  user
+        # group(3)  host
+        # group(4)  nick
+        # group(5)  user flags
+        # group(6)  real name
+        if match.group(2).lower() == self.__name:
+            away = self.trUtf8(" (Away)") if match.group(5).startswith("G") else ""
+            self.__addManagementMessage(self.trUtf8("Who"),
+                self.trUtf8("{0} is {1}@{2} ({3}){4}").format(
+                    match.group(4), match.group(2), match.group(3), match.group(6), away))
+            return True
+        
+        return False
+

eric ide

mercurial