1import re 2from time import time 3 4from gi.repository import GObject 5 6from pychess.ic import IC_STATUS_OFFLINE, IC_STATUS_ACTIVE, IC_STATUS_PLAYING, IC_STATUS_BUSY, \ 7 GAME_TYPES_BY_FICS_NAME, BLKCMD_FINGER 8from pychess.Utils.const import WHITE, BLACK 9from pychess.System.Log import log 10 11types = "(?:blitz|standard|lightning|wild|bughouse|crazyhouse|suicide|losers|atomic)" 12rated = "(rated|unrated)" 13colors = r"(?:\[(white|black)\]\s?)?" 14ratings = r"([\d\+\-]{1,4})" 15titleslist = r"(?:GM|IM|FM|WGM|WIM|WFM|TM|SR|TD|SR|CA|C|U|D|B|T|\*)" 16titles = r"((?:\(%s\))+)?" % titleslist 17names = r"(\w+)%s" % titles 18mf = r"(?:([mf]{1,2})\s?)?" 19# FIXME: Needs support for day, hour, min, sec 20times = "[, ]*".join(r"(?:(\d+) %s)?" % s 21 for s in ("days", "hrs", "mins", "secs")) 22 23# "73 days, 5 hrs, 55 mins" 24# ('73', '5', '55', None) 25 26 27class FingerObject: 28 def __init__(self, name=""): 29 self.__fingerTime = time() 30 31 self.__name = name 32 self.__status = None 33 self.__upTime = 0 34 self.__idleTime = 0 35 self.__busyMessage = "" 36 self.__lastSeen = 0 37 self.__totalTimeOnline = 0 38 self.__created = 0 # Since field from % of life online 39 self.__email = "" 40 self.__sanctions = "" 41 self.__adminLevel = "" 42 self.__timeseal = False 43 self.__notes = [""] * 10 44 self.__gameno = "" 45 self.__color = WHITE 46 self.__opponent = "" 47 self.__silence = False 48 self.__titles = None 49 50 self.__ratings = {} 51 52 def getName(self): 53 """ Returns the name of the user, without any title sufixes """ 54 return self.__name 55 56 def getStatus(self): 57 """ Returns the current user-status as a STATUS constant """ 58 return self.__status 59 60 def getUpTime(self): 61 """ Returns the when the user logged on 62 Not set when status == STATUS_OFFLINE """ 63 return self.__upTime + time() - self.__fingerTime 64 65 def getIdleTime(self): 66 """ Returns the when the last time the user did something active 67 Not set when status == STATUS_OFFLINE """ 68 return self.__idleTime + time() - self.__fingerTime 69 70 def getBusyMessage(self): 71 """ Returns the userset idle message 72 This is set when status == STATUS_BUSY or sometimes when status == 73 STATUS_PLAYING """ 74 return self.__busyMessage 75 76 def getLastSeen(self): 77 """ Returns when the user logged off 78 This is only set when status == STATUS_OFFLINE 79 This is not set, if the user has never logged on """ 80 return self.__lastSeen 81 82 def getTotalTimeOnline(self): 83 """ Returns how many seconds the user has been on FICS since the account 84 was created. 85 This is not set, if the user has never logged on """ 86 return self.__totalTimeOnline 87 88 def getCreated(self): 89 """ Returns when the account was created """ 90 return self.__created 91 92 def getEmail(self): 93 """ Returns the email adress of the user. 94 This will probably only be set for the logged in user """ 95 return self.__email 96 97 def getSanctions(self): 98 """ Returns any sanctions the user has against them. This is usually 99 an empty string """ 100 return self.__sanctions 101 102 def getAdminLevel(self): 103 """ Returns the admin level as a string 104 Only set for admins. """ 105 return self.__adminLevel 106 107 def getTimeseal(self): 108 """ Returns True if the user is using timeseal for fics connection """ 109 return self.__timeseal 110 111 def getNotes(self): 112 """ Returns a list of the ten finger notes """ 113 return self.__notes 114 115 def getGameno(self): 116 """ Returns the gameno of the game in which user is currently playing 117 This is only set when status == STATUS_PLAYING """ 118 return self.__gameno 119 120 def getColor(self): 121 """ If status == STATUS_PLAYING getColor returns the color witch the 122 player has got in the game. 123 Otherwise always WHITE is returned """ 124 return self.__color 125 126 def getOpponent(self): 127 """ Returns the opponent of the user in his current game 128 This is only set when status == STATUS_PLAYING """ 129 return self.__opponent 130 131 def getSilence(self): 132 """ Return True if the user is playing in silence 133 This is only set when status == STATUS_PLAYING """ 134 return self.__silence 135 136 def getRatings(self): 137 return self.__ratings 138 139 def getRating(self, type=None): 140 return int(self.__ratings[type][0]) 141 142 def getRatingsLen(self): 143 return len(self.__ratings) 144 145 def getTitles(self): 146 return self.__titles 147 148 def setName(self, value): 149 self.__name = value 150 151 def setStatus(self, value): 152 self.__status = value 153 154 def setUpTime(self, value): 155 """ Use relative seconds """ 156 self.__upTime = value 157 158 def setIdleTime(self, value): 159 """ Use relative seconds """ 160 self.__idleTime = value 161 162 def setBusyMessage(self, value): 163 """ Use relative seconds """ 164 self.__busyMessage = value 165 166 def setLastSeen(self, value): 167 """ Use relative seconds """ 168 self.__lastSeen = value 169 170 def setTotalTimeOnline(self, value): 171 """ Use relative seconds """ 172 self.__totalTimeOnline = value 173 174 def setCreated(self, value): 175 """ Use relative seconds """ 176 self.__created = value 177 178 def setEmail(self, value): 179 self.__email = value 180 181 def setSanctions(self, value): 182 self.__sanctions = value 183 184 def setAdminLevel(self, value): 185 self.__adminLevel = value 186 187 def setTimeseal(self, value): 188 self.__timeseal = value 189 190 def setNote(self, index, note): 191 self.__notes[index] = note 192 193 def setGameno(self, value): 194 self.__gameno = value 195 196 def setColor(self, value): 197 self.__color = value 198 199 def setOpponent(self, value): 200 self.__opponent = value 201 202 def setSilence(self, value): 203 self.__silence = value 204 205 def setRating(self, rating_type, rating_line): 206 self.__ratings[rating_type] = rating_line 207 208 def setTitles(self, titles): 209 self.__titles = titles 210 211 212class FingerManager(GObject.GObject): 213 214 __gsignals__ = { 215 'fingeringFinished': (GObject.SignalFlags.RUN_FIRST, None, (object, )), 216 'ratingAdjusted': (GObject.SignalFlags.RUN_FIRST, None, (str, str)), 217 } 218 219 def __init__(self, connection): 220 GObject.GObject.__init__(self) 221 self.connection = connection 222 223 fingerLines = ( 224 r"(?P<never>%s has never connected\.)" % names, 225 "Last disconnected: (?P<last>.+)", 226 "On for: (?P<uptime>.+?) +Idle: (?P<idletime>.+)", 227 r"%s is in (?P<silence>silence) mode\." % names, 228 r"\(playing game (?P<gameno>\d+): (?P<p1>\S+?)%s vs. (?P<p2>\S+?)%s\)" 229 % (titles, titles), r"\(%s (?P<busymessage>.+?)\)" % names, 230 r"%s has not played any rated games\." % names, 231 "rating +RD +win +loss +draw +total +best", 232 "(?P<gametype>%s) +(?P<ratings>.+)" % types, 233 "Email *: (?P<email>.+)", "Sanctions *: (?P<sanctions>.+)", 234 "Total time online: (?P<tto>.+)", 235 r"% of life online: [\d\.]+ \(since (?P<created>.+?)\)", 236 r"Timeseal [ \d] : (?P<timeseal>Off|On)", 237 "Admin Level: (?P<adminlevel>.+)", 238 r"(?P<noteno>\d+): *(?P<note>.*)", "$") 239 240 self.connection.expect_fromplus(self.onFinger, "Finger of %s:" % names, 241 "$|".join(fingerLines)) 242 243 self.connection.client.run_command("iset nowrap 1") 244 245 # We don't use this. Rather we use BoardManagers "gameEnded", after 246 # which we do a refinger. This is to ensure not only rating, but also 247 # wins/looses/draws are updated 248 # self.connection.expect(self.onRatingAdjust, 249 # "%s rating adjustment: (\d+) --> (\d+)" % types 250 # Notice if you uncomment this: The expression has to be compiled with 251 # re.IGNORECASE, or the first letter of 'type' must be capital 252 253 def parseDate(self, date): 254 # Tue Mar 11, 10:56 PDT 2008 255 return date 256 257 def parseShortDate(self, date): 258 # 24-Oct-2007 259 return date 260 261 def parseTime(self, time): 262 # 3 days, 2 hrs, 53 mins 263 return time 264 265 def onFinger(self, matchlist): 266 finger = FingerObject() 267 name = matchlist[0].groups()[0] 268 finger.setName(name) 269 if matchlist[0].groups()[1]: 270 titles = re.findall(titleslist, matchlist[0].groups()[1]) 271 finger.setTitles(titles) 272 for match in matchlist[1:]: 273 if not match.group(): 274 continue 275 groupdict = match.groupdict() 276 if groupdict["never"] is not None: 277 finger.setStatus(IC_STATUS_OFFLINE) 278 elif groupdict["last"] is not None: 279 finger.setStatus(IC_STATUS_OFFLINE) 280 finger.setLastSeen(self.parseDate(groupdict["last"])) 281 elif groupdict["uptime"] is not None: 282 finger.setStatus(IC_STATUS_ACTIVE) 283 finger.setUpTime(self.parseTime(groupdict["uptime"])) 284 finger.setIdleTime(self.parseTime(groupdict["idletime"])) 285 elif groupdict["silence"] is not None: 286 finger.setSilence(True) 287 elif groupdict["gameno"] is not None: 288 finger.setStatus(IC_STATUS_PLAYING) 289 finger.setGameno(groupdict["gameno"]) 290 if groupdict["p1"].lower() == self.connection.getUsername( 291 ).lower(): 292 finger.setColor(WHITE) 293 finger.setOpponent(groupdict["p2"]) 294 else: 295 finger.setColor(BLACK) 296 finger.setOpponent(groupdict["p1"]) 297 elif groupdict["busymessage"] is not None: 298 finger.setStatus(IC_STATUS_BUSY) 299 finger.setBusyMessage(groupdict["busymessage"]) 300 elif groupdict["gametype"] is not None: 301 gametype = GAME_TYPES_BY_FICS_NAME[groupdict["gametype"].lower()] 302 ratings = groupdict["ratings"].split() 303 finger.setRating(gametype.rating_type, ratings) 304 elif groupdict["email"] is not None: 305 finger.setEmail(groupdict["email"]) 306 elif groupdict["sanctions"] is not None: 307 finger.setSanctions(groupdict["sanctions"]) 308 elif groupdict["tto"] is not None: 309 finger.setTotalTimeOnline(self.parseTime(groupdict["tto"])) 310 elif groupdict["created"] is not None: 311 finger.setCreated(self.parseDate(groupdict["created"])) 312 elif groupdict["timeseal"] is not None: 313 finger.setTimeseal(groupdict["timeseal"] == "On") 314 elif groupdict["adminlevel"] is not None: 315 finger.setAdminLevel(groupdict["adminlevel"]) 316 elif groupdict["noteno"] is not None: 317 finger.setNote(int(groupdict["noteno"]) - 1, groupdict["note"]) 318 else: 319 log.debug("Ignored fingerline: %s" % repr(match.group())) 320 321 self.emit("fingeringFinished", finger) 322 323 onFinger.BLKCMD = BLKCMD_FINGER 324 325 def onRatingAdjust(self, match): 326 # Notice: This is only recived for us, not for other persons we finger 327 rating_type, old, new = match.groups() 328 self.emit("ratingAdjusted", rating_type, new) 329 330 def finger(self, user): 331 self.connection.client.run_command("finger %s" % user) 332 333 def setFingerNote(self, note, message): 334 assert 1 <= note <= 10 335 self.connection.client.run_command("set %d %s" % (note, message)) 336 337 def setBusyMessage(self, message): 338 """ Like set busy is really busy right now. """ 339 self.connection.client.run_command("set busy %s" % message) 340