1# -*- coding: utf-8 -*- 2 3import os 4from io import StringIO 5 6from gi.repository import GLib, Gtk, GObject 7 8from pychess.compat import create_task 9from pychess.ic import IC_POS_EXAMINATING, IC_POS_OBSERVING_EXAMINATION, \ 10 get_infobarmessage_content, get_infobarmessage_content2, TITLES 11from pychess.ic.ICGameModel import ICGameModel 12from pychess.perspectives.fics.FicsHome import UserInfoSection 13from pychess.perspectives.fics.SeekChallenge import SeekChallengeSection 14from pychess.System import conf, uistuff 15from pychess.System.prefix import addUserConfigPrefix 16from pychess.System.Log import log 17from pychess.widgets import new_notebook 18from pychess.widgets.InfoBar import InfoBarMessage, InfoBarNotebook, InfoBarMessageButton 19from pychess.widgets.pydock.PyDockTop import PyDockTop 20from pychess.widgets.pydock import SOUTH, WEST, CENTER 21 22from pychess.Utils.const import LOCAL, WHITE, BLACK, REMOTE 23from pychess.Utils.TimeModel import TimeModel 24from pychess.Players.ICPlayer import ICPlayer 25from pychess.Players.Human import Human 26from pychess.Savers import pgn, fen 27from pychess.perspectives import Perspective, perspective_manager, panel_name 28 29 30if not hasattr(Gtk.TreeModelFilter, "new_with_model"): 31 # Fix #1811: TreeModelFilter.sort_new_with_model() is missing on some Gtk versions 32 # due to API changes. Let's keep compatibility with older versions. 33 34 def sort_new_with_model(self): 35 super_object = super(Gtk.TreeModel, self) 36 if hasattr(super_object, "sort_new_with_model"): 37 return super_object.sort_new_with_model() 38 return Gtk.TreeModelSort.new_with_model(self) 39 40 @classmethod 41 def new_with_model(self, child_model): 42 return Gtk.TreeModel.sort_new_with_model(child_model) 43 44 Gtk.TreeModel.sort_new_with_model = sort_new_with_model 45 Gtk.TreeModelFilter.new_with_model = new_with_model 46 47 48class PlayerNotificationMessage(InfoBarMessage): 49 50 def __init__(self, message_type, content, callback, player, text): 51 InfoBarMessage.__init__(self, message_type, content, callback) 52 self.player = player 53 self.text = text 54 55 56class FICS(GObject.GObject, Perspective): 57 __gsignals__ = { 58 'logout': (GObject.SignalFlags.RUN_FIRST, None, ()), 59 'autoLogout': (GObject.SignalFlags.RUN_FIRST, None, ()), 60 } 61 62 def __init__(self): 63 log.debug("FICS.__init__: starting") 64 GObject.GObject.__init__(self) 65 Perspective.__init__(self, "fics", _("ICS")) 66 self.dockLocation = addUserConfigPrefix("pydock-fics.xml") 67 self.first_run = True 68 69 def create_toolbuttons(self): 70 def on_logoff_clicked(button): 71 self.emit("logout") 72 self.close() 73 74 self.logoff_button = Gtk.ToolButton.new_from_stock(Gtk.STOCK_DISCONNECT) 75 self.logoff_button.set_tooltip_text(_("Log Off")) 76 self.logoff_button.set_label("logoff") 77 self.logoff_button.connect("clicked", on_logoff_clicked) 78 79 def on_minute_1_clicked(button): 80 self.connection.client.run_command("1-minute") 81 82 def on_minute_3_clicked(button): 83 self.connection.client.run_command("3-minute") 84 85 def on_minute_5_clicked(button): 86 self.connection.client.run_command("5-minute") 87 88 def on_minute_15_clicked(button): 89 self.connection.client.run_command("15-minute") 90 91 def on_minute_25_clicked(button): 92 self.connection.client.run_command("25-minute") 93 94 def on_chess960_clicked(button): 95 self.connection.client.run_command("chess960") 96 97 self.minute_1_button = Gtk.ToggleToolButton() 98 self.minute_1_button.set_label("1") 99 self.minute_1_button.set_tooltip_text(_("New game from 1-minute playing pool")) 100 self.minute_1_button.connect("clicked", on_minute_1_clicked) 101 102 self.minute_3_button = Gtk.ToggleToolButton() 103 self.minute_3_button.set_label("3") 104 self.minute_3_button.set_tooltip_text(_("New game from 3-minute playing pool")) 105 self.minute_3_button.connect("clicked", on_minute_3_clicked) 106 107 self.minute_5_button = Gtk.ToggleToolButton() 108 self.minute_5_button.set_label("5") 109 self.minute_5_button.set_tooltip_text(_("New game from 5-minute playing pool")) 110 self.minute_5_button.connect("clicked", on_minute_5_clicked) 111 112 self.minute_15_button = Gtk.ToggleToolButton() 113 self.minute_15_button.set_label("15") 114 self.minute_15_button.set_tooltip_text(_("New game from 15-minute playing pool")) 115 self.minute_15_button.connect("clicked", on_minute_15_clicked) 116 117 self.minute_25_button = Gtk.ToggleToolButton() 118 self.minute_25_button.set_label("25") 119 self.minute_25_button.set_tooltip_text(_("New game from 25-minute playing pool")) 120 self.minute_25_button.connect("clicked", on_minute_25_clicked) 121 122 self.chess960_button = Gtk.ToggleToolButton() 123 self.chess960_button.set_label("960") 124 self.chess960_button.set_tooltip_text(_("New game from Chess960 playing pool")) 125 self.chess960_button.connect("clicked", on_chess960_clicked) 126 127 def init_layout(self): 128 perspective_widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) 129 perspective_manager.set_perspective_widget("fics", perspective_widget) 130 131 self.infobar = InfoBarNotebook("fics_lounge_infobar") 132 self.infobar.hide() 133 perspective_widget.pack_start(self.infobar, False, False, 0) 134 135 self.dock = PyDockTop("fics", self) 136 align = Gtk.Alignment() 137 align.show() 138 align.add(self.dock) 139 self.dock.show() 140 perspective_widget.pack_start(align, True, True, 0) 141 142 self.notebooks = {"ficshome": new_notebook()} 143 self.main_notebook = self.notebooks["ficshome"] 144 for panel in self.sidePanels: 145 self.notebooks[panel_name(panel.__name__)] = new_notebook(panel_name(panel.__name__)) 146 147 self.docks["ficshome"] = (Gtk.Label(label="ficshome"), self.notebooks["ficshome"], None) 148 for panel in self.sidePanels: 149 self.docks[panel_name(panel.__name__)][1] = self.notebooks[panel_name(panel.__name__)] 150 151 self.load_from_xml() 152 153 # Default layout of side panels 154 first_time_layout = False 155 if not os.path.isfile(self.dockLocation): 156 first_time_layout = True 157 leaf = self.dock.dock(self.docks["ficshome"][1], CENTER, self.docks["ficshome"][0], "ficshome") 158 leaf.setDockable(False) 159 160 console_leaf = leaf.dock(self.docks["ConsolePanel"][1], SOUTH, self.docks["ConsolePanel"][0], "ConsolePanel") 161 console_leaf.dock(self.docks["NewsPanel"][1], CENTER, self.docks["NewsPanel"][0], "NewsPanel") 162 163 seek_leaf = leaf.dock(self.docks["SeekListPanel"][1], WEST, self.docks["SeekListPanel"][0], "SeekListPanel") 164 seek_leaf.dock(self.docks["SeekGraphPanel"][1], CENTER, self.docks["SeekGraphPanel"][0], "SeekGraphPanel") 165 seek_leaf.dock(self.docks["PlayerListPanel"][1], CENTER, self.docks["PlayerListPanel"][0], "PlayerListPanel") 166 seek_leaf.dock(self.docks["GameListPanel"][1], CENTER, self.docks["GameListPanel"][0], "GameListPanel") 167 seek_leaf.dock(self.docks["ArchiveListPanel"][1], CENTER, self.docks["ArchiveListPanel"][0], "ArchiveListPanel") 168 169 leaf = leaf.dock(self.docks["ChatPanel"][1], SOUTH, self.docks["ChatPanel"][0], "ChatPanel") 170 # leaf.dock(self.docks["LecturesPanel"][1], CENTER, self.docks["LecturesPanel"][0], "LecturesPanel") 171 172 def unrealize(dock): 173 dock.saveToXML(self.dockLocation) 174 dock._del() 175 176 self.dock.connect("unrealize", unrealize) 177 178 self.dock.show_all() 179 perspective_widget.show_all() 180 181 perspective_manager.set_perspective_menuitems("fics", self.menuitems, default=first_time_layout) 182 183 log.debug("FICS.__init__: finished") 184 185 def open_lounge(self, connection, helperconn, host): 186 if self.first_run: 187 self.init_layout() 188 189 self.connection = connection 190 self.helperconn = helperconn 191 self.host = host 192 193 self.finger_sent = False 194 self.messages = [] 195 self.players = [] 196 self.game_cids = {} 197 198 self.widgets = uistuff.GladeWidgets("fics_lounge.glade") 199 self.widgets["fics_lounge"].hide() 200 201 fics_home = self.widgets["fics_home"] 202 self.widgets["fics_lounge_content_hbox"].remove(fics_home) 203 204 self.archive_list = self.widgets["archiveListContent"] 205 self.widgets["fics_panels_notebook"].remove(self.archive_list) 206 207 self.games_list = self.widgets["gamesListContent"] 208 self.widgets["fics_panels_notebook"].remove(self.games_list) 209 210 self.news_list = self.widgets["news"] 211 self.widgets["fics_home"].remove(self.news_list) 212 213 self.players_list = self.widgets["playersListContent"] 214 self.widgets["fics_panels_notebook"].remove(self.players_list) 215 216 self.seek_graph = self.widgets["seekGraphContent"] 217 self.widgets["fics_panels_notebook"].remove(self.seek_graph) 218 219 self.seek_list = self.widgets["seekListContent"] 220 self.widgets["fics_panels_notebook"].remove(self.seek_list) 221 222 self.seek_challenge = SeekChallengeSection(self) 223 224 def on_autoLogout(alm): 225 self.emit("autoLogout") 226 self.close() 227 228 self.connection.alm.connect("logOut", on_autoLogout) 229 self.connection.connect("disconnected", lambda connection: self.close()) 230 self.connection.connect("error", self.on_connection_error) 231 if self.connection.isRegistred(): 232 numtimes = conf.get("numberOfTimesLoggedInAsRegisteredUser") + 1 233 conf.set("numberOfTimesLoggedInAsRegisteredUser", numtimes) 234 self.connection.em.connect("onCommandNotFound", lambda em, cmd: log.error( 235 "Fics answered '%s': Command not found" % cmd)) 236 self.connection.bm.connect("playGameCreated", self.onPlayGameCreated) 237 self.connection.bm.connect("obsGameCreated", self.onObserveGameCreated) 238 self.connection.bm.connect("exGameCreated", self.onObserveGameCreated) 239 self.connection.fm.connect("fingeringFinished", self.onFinger) 240 # the rest of these relay server messages to the lounge infobar 241 self.connection.bm.connect("tooManySeeks", self.tooManySeeks) 242 self.connection.bm.connect("nonoWhileExamine", self.nonoWhileExamine) 243 self.connection.bm.connect("matchDeclined", self.matchDeclined) 244 self.connection.bm.connect("player_on_censor", self.player_on_censor) 245 self.connection.bm.connect("player_on_noplay", self.player_on_noplay) 246 self.connection.bm.connect("req_not_fit_formula", self.req_not_fit_formula) 247 self.connection.glm.connect("seek-updated", self.on_seek_updated) 248 self.connection.glm.connect("our-seeks-removed", self.our_seeks_removed) 249 self.connection.cm.connect("arrivalNotification", self.onArrivalNotification) 250 self.connection.cm.connect("departedNotification", self.onDepartedNotification) 251 252 def get_top_games(): 253 if perspective_manager.current_perspective == self: 254 self.connection.client.run_command("games *19") 255 return True 256 257 if self.connection.ICC: 258 self.event_id = GLib.timeout_add_seconds(5, get_top_games) 259 260 for user in self.connection.notify_users: 261 user = self.connection.players.get(user) 262 self.user_from_notify_list_is_present(user) 263 264 self.userinfo = UserInfoSection(self.widgets, self.connection, self.host, self) 265 if not self.first_run: 266 self.notebooks["ficshome"].remove_page(-1) 267 self.notebooks["ficshome"].append_page(fics_home) 268 269 self.panels = [panel.Sidepanel().load(self.widgets, self.connection, self) for panel in self.sidePanels] 270 271 for panel, instance in zip(self.sidePanels, self.panels): 272 if not self.first_run: 273 self.notebooks[panel_name(panel.__name__)].remove_page(-1) 274 self.notebooks[panel_name(panel.__name__)].append_page(instance) 275 instance.show() 276 277 tool_buttons = [self.logoff_button, ] 278 self.quick_seek_buttons = [] 279 if self.connection.ICC: 280 self.quick_seek_buttons = [self.minute_1_button, self.minute_3_button, self.minute_5_button, 281 self.minute_15_button, self.minute_25_button, self.chess960_button] 282 tool_buttons += self.quick_seek_buttons 283 perspective_manager.set_perspective_toolbuttons("fics", tool_buttons) 284 285 if self.first_run: 286 self.first_run = False 287 288 # After all panel is set up we can push initial messages out 289 self.connection.com.onConsoleMessage("", self.connection.ini_messages) 290 291 def show(self): 292 perspective_manager.activate_perspective("fics") 293 294 def present(self): 295 perspective_manager.activate_perspective("fics") 296 297 def on_connection_error(self, connection, error): 298 log.warning("FICS.on_connection_error: %s" % repr(error)) 299 self.close() 300 301 def close(self): 302 try: 303 self.widgets = None 304 except TypeError: 305 pass 306 except AttributeError: 307 pass 308 perspective_manager.disable_perspective("fics") 309 310 def onPlayGameCreated(self, bm, ficsgame): 311 log.debug("FICS.onPlayGameCreated: %s" % ficsgame) 312 313 for message in self.messages: 314 message.dismiss() 315 del self.messages[:] 316 317 if self.connection.ICC: 318 for button in self.quick_seek_buttons: 319 button.set_active(False) 320 321 timemodel = TimeModel(ficsgame.minutes * 60, ficsgame.inc) 322 323 gamemodel = ICGameModel(self.connection, ficsgame, timemodel) 324 gamemodel.connect("game_started", self.onGameModelStarted, ficsgame) 325 326 wplayer, bplayer = ficsgame.wplayer, ficsgame.bplayer 327 328 # We start 329 if wplayer.name.lower() == self.connection.getUsername().lower(): 330 player0tup = (LOCAL, Human, 331 (WHITE, wplayer.long_name(), wplayer.name, 332 wplayer.getRatingForCurrentGame()), 333 wplayer.long_name()) 334 player1tup = (REMOTE, ICPlayer, ( 335 gamemodel, bplayer.name, ficsgame.gameno, BLACK, 336 bplayer.long_name(), bplayer.getRatingForCurrentGame()), 337 bplayer.long_name()) 338 339 # She starts 340 else: 341 player1tup = (LOCAL, Human, 342 (BLACK, bplayer.long_name(), bplayer.name, 343 bplayer.getRatingForCurrentGame()), 344 bplayer.long_name()) 345 player0tup = (REMOTE, ICPlayer, ( 346 gamemodel, wplayer.name, ficsgame.gameno, WHITE, 347 wplayer.long_name(), wplayer.getRatingForCurrentGame()), 348 wplayer.long_name()) 349 350 perspective = perspective_manager.get_perspective("games") 351 if not ficsgame.board.fen: 352 create_task(perspective.generalStart(gamemodel, player0tup, player1tup)) 353 else: 354 create_task(perspective.generalStart(gamemodel, player0tup, player1tup, ( 355 StringIO(ficsgame.board.fen), fen, 0, -1))) 356 357 def onGameModelStarted(self, gamemodel, ficsgame): 358 self.connection.bm.onGameModelStarted(ficsgame.gameno) 359 360 def onObserveGameCreated(self, bm, ficsgame): 361 log.debug("FICS.onObserveGameCreated: %s" % ficsgame) 362 363 timemodel = TimeModel(ficsgame.minutes * 60, ficsgame.inc) 364 365 gamemodel = ICGameModel(self.connection, ficsgame, timemodel) 366 gamemodel.connect("game_started", self.onGameModelStarted, ficsgame) 367 368 # The players need to start listening for moves IN this method if they 369 # want to be noticed of all moves the FICS server sends us from now on 370 wplayer, bplayer = ficsgame.wplayer, ficsgame.bplayer 371 372 player0tup = (REMOTE, ICPlayer, ( 373 gamemodel, wplayer.name, ficsgame.gameno, WHITE, 374 wplayer.long_name(), wplayer.getRatingForCurrentGame()), 375 wplayer.long_name()) 376 player1tup = (REMOTE, ICPlayer, ( 377 gamemodel, bplayer.name, ficsgame.gameno, BLACK, 378 bplayer.long_name(), bplayer.getRatingForCurrentGame()), 379 bplayer.long_name()) 380 381 perspective = perspective_manager.get_perspective("games") 382 create_task(perspective.generalStart(gamemodel, player0tup, player1tup, ( 383 StringIO(ficsgame.board.pgn), pgn, 0, -1))) 384 385 if ficsgame.relation == IC_POS_OBSERVING_EXAMINATION: 386 if 1: # int(self.connection.lvm.variablesBackup["kibitz"]) == 0: 387 self.connection.cm.whisper(_( 388 "You have to set kibitz on to see bot messages here.")) 389 self.connection.fm.finger(bplayer.name) 390 self.connection.fm.finger(wplayer.name) 391 elif ficsgame.relation == IC_POS_EXAMINATING: 392 gamemodel.examined = True 393 if not self.connection.ICC: 394 allob = 'allob ' + str(ficsgame.gameno) 395 gamemodel.connection.client.run_command(allob) 396 397 def onFinger(self, fm, finger): 398 titles = finger.getTitles() 399 if titles is not None: 400 name = finger.getName() 401 player = self.connection.players.get(name) 402 for title in titles: 403 player.titles.add(TITLES[title]) 404 405 def tooManySeeks(self, bm): 406 label = Gtk.Label(label=_( 407 "You may only have 3 outstanding seeks at the same time. If you want \ 408 to add a new seek you must clear your currently active seeks. Clear your seeks?")) 409 label.set_width_chars(80) 410 label.props.xalign = 0 411 label.set_line_wrap(True) 412 413 def response_cb(infobar, response, message): 414 if response == Gtk.ResponseType.YES: 415 self.connection.client.run_command("unseek") 416 message.dismiss() 417 return False 418 419 message = InfoBarMessage(Gtk.MessageType.QUESTION, label, response_cb) 420 message.add_button(InfoBarMessageButton(Gtk.STOCK_YES, 421 Gtk.ResponseType.YES)) 422 message.add_button(InfoBarMessageButton(Gtk.STOCK_NO, 423 Gtk.ResponseType.NO)) 424 self.messages.append(message) 425 self.infobar.push_message(message) 426 427 def nonoWhileExamine(self, bm): 428 label = Gtk.Label(_("You can't touch this! You are examining a game.")) 429 430 def response_cb(infobar, response, message): 431 message.dismiss() 432 return False 433 434 message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) 435 message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, 436 Gtk.ResponseType.CANCEL)) 437 self.messages.append(message) 438 self.infobar.push_message(message) 439 440 def matchDeclined(self, bm, player): 441 text = _(" has declined your offer for a match") 442 content = get_infobarmessage_content(player, text) 443 444 def response_cb(infobar, response, message): 445 message.dismiss() 446 return False 447 448 message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) 449 message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, 450 Gtk.ResponseType.CANCEL)) 451 self.messages.append(message) 452 self.infobar.push_message(message) 453 454 def player_on_censor(self, bm, player): 455 text = _(" is censoring you") 456 content = get_infobarmessage_content(player, text) 457 458 def response_cb(infobar, response, message): 459 message.dismiss() 460 return False 461 462 message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) 463 message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, 464 Gtk.ResponseType.CANCEL)) 465 self.messages.append(message) 466 self.infobar.push_message(message) 467 468 def player_on_noplay(self, bm, player): 469 text = _(" noplay listing you") 470 content = get_infobarmessage_content(player, text) 471 472 def response_cb(infobar, response, message): 473 message.dismiss() 474 return False 475 476 message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) 477 message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, 478 Gtk.ResponseType.CANCEL)) 479 self.messages.append(message) 480 self.infobar.push_message(message) 481 482 def req_not_fit_formula(self, bm, player, formula): 483 content = get_infobarmessage_content2( 484 player, _(" uses a formula not fitting your match request:"), 485 formula) 486 487 def response_cb(infobar, response, message): 488 message.dismiss() 489 return False 490 491 message = InfoBarMessage(Gtk.MessageType.INFO, content, response_cb) 492 message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, 493 Gtk.ResponseType.CANCEL)) 494 self.messages.append(message) 495 self.infobar.push_message(message) 496 497 def on_seek_updated(self, glm, message_text): 498 if "manual accept" in message_text: 499 message_text.replace("to manual accept", _("to manual accept")) 500 elif "automatic accept" in message_text: 501 message_text.replace("to automatic accept", 502 _("to automatic accept")) 503 if "rating range now" in message_text: 504 message_text.replace("rating range now", _("rating range now")) 505 label = Gtk.Label(label=_("Seek updated") + ": " + message_text) 506 507 def response_cb(infobar, response, message): 508 message.dismiss() 509 return False 510 511 message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) 512 message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, 513 Gtk.ResponseType.CANCEL)) 514 self.messages.append(message) 515 self.infobar.push_message(message) 516 517 def our_seeks_removed(self, glm): 518 label = Gtk.Label(label=_("Your seeks have been removed")) 519 520 def response_cb(infobar, response, message): 521 message.dismiss() 522 return False 523 524 message = InfoBarMessage(Gtk.MessageType.INFO, label, response_cb) 525 message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, 526 Gtk.ResponseType.CANCEL)) 527 self.messages.append(message) 528 self.infobar.push_message(message) 529 530 def _connect_to_player_changes(self, player): 531 player.connect("ratings_changed", self._replace_notification_message, player) 532 player.connect("notify::titles", self._replace_notification_message, None, player) 533 534 def onArrivalNotification(self, cm, player): 535 log.debug("%s" % player, 536 extra={"task": (self.connection.username, 537 "onArrivalNotification")}) 538 self._add_notification_message(player, _(" has arrived"), chat=True, replace=True) 539 if player not in self.players: 540 self.players.append(player) 541 self._connect_to_player_changes(player) 542 543 def onDepartedNotification(self, cm, player): 544 self._add_notification_message(player, _(" has departed"), replace=True) 545 546 def user_from_notify_list_is_present(self, player): 547 self._add_notification_message(player, _(" is present"), chat=True, replace=True) 548 if player not in self.players: 549 self.players.append(player) 550 self._connect_to_player_changes(player) 551 552 def _add_notification_message(self, player, text, chat=False, replace=False): 553 if replace: 554 for message in self.messages: 555 if isinstance(message, PlayerNotificationMessage) and message.player == player: 556 message.dismiss() 557 558 content = get_infobarmessage_content(player, text) 559 560 def response_cb(infobar, response, message): 561 if response == 1: 562 if player is None: 563 return 564 self.chat.openChatWithPlayer(player.name) 565 if response == 2: 566 if player is None: 567 return 568 self.connection.client.run_command("follow %s" % player.name) 569 message.dismiss() 570 # self.messages.remove(message) 571 return False 572 573 message = PlayerNotificationMessage(Gtk.MessageType.INFO, content, 574 response_cb, player, text) 575 if chat: 576 message.add_button(InfoBarMessageButton(_("Chat"), 1)) 577 message.add_button(InfoBarMessageButton(_("Follow"), 2)) 578 message.add_button(InfoBarMessageButton(Gtk.STOCK_CLOSE, 579 Gtk.ResponseType.CANCEL)) 580 self.messages.append(message) 581 self.infobar.push_message(message) 582 583 def _replace_notification_message(self, obj, prop, rating_type, player): 584 log.debug("%s %s" % (repr(obj), player), 585 extra={"task": (self.connection.username, 586 "_replace_notification_message")}) 587 for message in self.messages: 588 if isinstance(message, PlayerNotificationMessage) and \ 589 message.player == player: 590 message.update_content(get_infobarmessage_content( 591 player, message.text)) 592 return False 593