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