Exemple #1
0
    def makeReady(self):
        signal.signal(signal.SIGINT, Gtk.main_quit)

        PyChess.makeReady(self)

        self.connection = FICSMainConnection("freechess.org", self.ports,
                                             self.username, self.password)
        self.connection.connect("connectingMsg", self.__showConnectLog)
        self.connection._connect()

        self.connection.glm.connect("addPlayer", self.__onAddPlayer)
        self.connection.glm.connect("removePlayer", self.__onRemovePlayer)
        self.connection.cm.connect("privateMessage", self.__onTell)
        self.connection.alm.connect("logOut", self.__onLogOut)
        self.connection.bm.connect("playGameCreated", self.__onGameCreated)
        self.connection.bm.connect("curGameEnded", self.__onGameEnded)
        self.connection.bm.connect("boardUpdate", self.__onBoardUpdate)
        self.connection.om.connect("onChallengeAdd", self.__onChallengeAdd)
        self.connection.om.connect("onOfferAdd", self.__onOfferAdd)
        self.connection.adm.connect("onAdjournmentsList",
                                    self.__onAdjournmentsList)
        self.connection.em.connect("onAmbiguousMove", self.__onAmbiguousMove)
        self.connection.em.connect("onIllegalMove", self.__onAmbiguousMove)

        self.connection.adm.queryAdjournments()
        self.connection.lvm.setVariable("autoflag", 1)

        self.connection.fm.setFingerNote(
            1, "PyChess is the chess engine bundled with the PyChess %s " %
            pychess.VERSION +
            "chess client. This instance is owned by %s, but acts " %
            self.owner + "quite autonomously.")

        self.connection.fm.setFingerNote(
            2,
            "PyChess is 100% Python code and is released under the terms of " +
            "the GPL. The evalution function is largely equal to the one of" +
            "GnuChess, but it plays quite differently.")

        self.connection.fm.setFingerNote(
            3,
            "PyChess runs on an elderly AMD Sempron(tm) Processor 3200+, 512 "
            +
            "MB DDR2 Ram, but is built to take use of 64bit calculating when "
            + "accessible, through the gpm library.")

        self.connection.fm.setFingerNote(
            4,
            "PyChess uses a small 500 KB openingbook based solely on Kasparov "
            +
            "games. The engine doesn't have much endgame knowledge, but might "
            + "in some cases access an online endgamedatabase.")

        self.connection.fm.setFingerNote(
            5,
            "PyChess will allow any pause/resume and adjourn wishes, but will "
            + "deny takebacks. Draw, abort and switch offers are accepted, " +
            "if they are found to be an advance. Flag is auto called, but " +
            "PyChess never resigns. We don't want you to forget your basic " +
            "mating skills.")
Exemple #2
0
    def onConnectClicked(self, button):
        self.canceled = False
        self.widgets["messagePanel"].hide()

        if self.widgets["logOnAsGuest"].get_active():
            username = self.widgets["nameEntry"].get_text()
            password = ""
        else:
            username = self.widgets["nameEntry"].get_text()
            password = self.widgets["passEntry"].get_text()

        if port:
            ports = (port, )
        else:
            ports = self.widgets["portsEntry"].get_text()
            ports = list(map(int, re.findall("\d+", ports)))
            if 5000 not in ports:
                ports.append(5000)
            if 23 not in ports:
                ports.append(23)
        alternate_host = self.widgets["hostEntry"].get_text()

        timeseal = self.widgets["timesealCheck"].get_active()

        self.showConnecting()
        self.host = (host if host is not None else
                     alternate_host if alternate_host else "freechess.org")
        self.connection = FICSMainConnection(self.host, ports, timeseal,
                                             username, password)
        for signal, callback in (
            ("connected", self.onConnected),
            ("error", self.onConnectionError),
            ("connectingMsg", self.showMessage),
        ):
            self.cids[self.connection].append(
                self.connection.connect(signal, callback))
        self.main_connected_event = asyncio.Event()
        self.connection_task = create_task(self.connection.start())

        # guest users are rather limited on ICC (helper connection is useless)
        if self.host not in ("localhost", "127.0.0.1", "chessclub.com"):
            self.helperconn = FICSHelperConnection(self.connection, self.host,
                                                   ports, timeseal)
            self.helperconn.connect("error", self.onHelperConnectionError)

            @asyncio.coroutine
            def coro():
                yield from self.main_connected_event.wait()
                yield from self.helperconn.start()

            self.helperconn_task = create_task(coro())
Exemple #3
0
    def makeReady(self):
        signal.signal(signal.SIGINT, Gtk.main_quit)

        PyChess.makeReady(self)

        self.connection = FICSMainConnection("freechess.org", self.ports,
                                             self.username, self.password)
        self.connection.connect("connectingMsg", self.__showConnectLog)
        self.connection._connect()

        self.connection.glm.connect("addPlayer", self.__onAddPlayer)
        self.connection.glm.connect("removePlayer", self.__onRemovePlayer)
        self.connection.cm.connect("privateMessage", self.__onTell)
        self.connection.alm.connect("logOut", self.__onLogOut)
        self.connection.bm.connect("playGameCreated", self.__onGameCreated)
        self.connection.bm.connect("curGameEnded", self.__onGameEnded)
        self.connection.bm.connect("boardUpdate", self.__onBoardUpdate)
        self.connection.om.connect("onChallengeAdd", self.__onChallengeAdd)
        self.connection.om.connect("onOfferAdd", self.__onOfferAdd)
        self.connection.adm.connect("onAdjournmentsList",
                                    self.__onAdjournmentsList)
        self.connection.em.connect("onAmbiguousMove", self.__onAmbiguousMove)
        self.connection.em.connect("onIllegalMove", self.__onAmbiguousMove)

        self.connection.adm.queryAdjournments()
        self.connection.lvm.setVariable("autoflag", 1)

        self.connection.fm.setFingerNote(
            1, "PyChess is the chess engine bundled with the PyChess %s " %
            pychess.VERSION +
            "chess client. This instance is owned by %s, but acts " %
            self.owner + "quite autonomously.")

        self.connection.fm.setFingerNote(
            2,
            "PyChess is 100% Python code and is released under the terms of " +
            "the GPL. The evalution function is largely equal to the one of" +
            "GnuChess, but it plays quite differently.")

        self.connection.fm.setFingerNote(
            3,
            "PyChess runs on an elderly AMD Sempron(tm) Processor 3200+, 512 "
            +
            "MB DDR2 Ram, but is built to take use of 64bit calculating when "
            + "accessible, through the gpm library.")

        self.connection.fm.setFingerNote(
            4,
            "PyChess uses a small 500 KB openingbook based solely on Kasparov "
            +
            "games. The engine doesn't have much endgame knowledge, but might "
            + "in some cases access an online endgamedatabase.")

        self.connection.fm.setFingerNote(
            5,
            "PyChess will allow any pause/resume and adjourn wishes, but will "
            + "deny takebacks. Draw, abort and switch offers are accepted, " +
            "if they are found to be an advance. Flag is auto called, but " +
            "PyChess never resigns. We don't want you to forget your basic " +
            "mating skills.")
Exemple #4
0
    def onConnectClicked(self, button):
        self.canceled = False
        self.widgets["messagePanel"].hide()

        if self.widgets["logOnAsGuest"].get_active():
            username = self.widgets["nameEntry"].get_text()
            password = ""
        else:
            username = self.widgets["nameEntry"].get_text()
            password = self.widgets["passEntry"].get_text()

        if port:
            ports = (port, )
        else:
            ports = self.widgets["portsEntry"].get_text()
            ports = list(map(int, re.findall("\d+", ports)))
            if 5000 not in ports:
                ports.append(5000)
            if 23 not in ports:
                ports.append(23)
        alternate_host = self.widgets["hostEntry"].get_text()

        timeseal = self.widgets["timesealCheck"].get_active()

        self.showConnecting()
        self.host = host if host is not None else alternate_host if alternate_host else "freechess.org"
        self.connection = FICSMainConnection(self.host, ports, timeseal, username, password)
        for signal, callback in (("connected", self.onConnected),
                                 ("error", self.onConnectionError),
                                 ("connectingMsg", self.showMessage)):
            self.cids[self.connection].append(self.connection.connect(signal, callback))
        self.main_connected_event = asyncio.Event()
        self.connection_task = asyncio.async(self.connection.start())

        # guest users are rather limited on ICC (helper connection is useless)
        if self.host not in ("localhost", "127.0.0.1", "chessclub.com"):
            self.helperconn = FICSHelperConnection(self.connection, self.host, ports, timeseal)
            self.helperconn.connect("error", self.onHelperConnectionError)

            @asyncio.coroutine
            def coro():
                yield from self.main_connected_event.wait()
                yield from self.helperconn.start()

            self.helperconn_task = asyncio.async(coro())
Exemple #5
0
class ICLogon:
    def __init__(self):
        self.connection = None
        self.helperconn = None
        self.connection_task = None
        self.helperconn_task = None
        self.lounge = None
        self.canceled = False
        self.cids = defaultdict(list)
        self.widgets = uistuff.GladeWidgets("fics_logon.glade")
        uistuff.keepWindowSize("fics_logon",
                               self.widgets["fics_logon"],
                               defaultPosition=uistuff.POSITION_GOLDEN)
        self.widgets["fics_logon"].connect(
            'key-press-event',
            lambda w, e: e.keyval == Gdk.KEY_Escape and w.hide())

        self.ics = "FICS"
        self.as_guest = self.widgets["logOnAsGuest"]

        self.widgets["logOnAsGuest"].connect("toggled",
                                             self.on_logOnAsGuest_toggled)

        def on_username_changed(widget):
            conf.set("usernameEntry",
                     self.user_name_get_value(widget),
                     section=self.ics)

        self.widgets["nameEntry"].connect("changed", on_username_changed)

        def on_password_changed(widget):
            conf.set("passwordEntry", widget.get_text(), section=self.ics)

        self.widgets["passEntry"].connect("changed", on_password_changed)

        def on_host_changed(widget):
            conf.set("hostEntry",
                     self.host_get_value(widget),
                     section=self.ics)

        self.widgets["hostEntry"].connect("changed", on_host_changed)

        self.widgets["timesealCheck"].connect("toggled",
                                              self.on_timeseal_toggled)

        self.infobar = Gtk.InfoBar()
        self.infobar.set_message_type(Gtk.MessageType.WARNING)
        # self.widgets["messagePanelHBox"].pack_start(self.infobar,
        #    expand=False, fill=False)
        self.widgets["messagePanelHBox"].pack_start(self.infobar, False, False,
                                                    0)
        self.widgets["cancelButton"].connect("clicked", self.onCancel, True)
        self.widgets["stopButton"].connect("clicked", self.onCancel, False)
        self.widgets["createNewButton"].connect("clicked", self.onCreateNew)
        self.widgets["connectButton"].connect("clicked", self.onConnectClicked)

        self.widgets["progressbar"].set_show_text(True)

    def get_user_names(self, value=None):
        """ Split and return usernameEntry config item into registered and guest username
        """
        if value is not None:
            names = value.split("|")
        else:
            names = conf.get("usernameEntry", section=self.ics).split("|")
        if len(names) == 0:
            names = ["", ""]
        elif len(names) < 2:
            names.append(names[0])
        return names

    def user_name_get_value(self, entry):
        names = self.get_user_names()
        if self.as_guest.get_active():
            text = "%s|%s" % (names[0], entry.get_text())
        else:
            text = "%s|%s" % (entry.get_text(), names[1])
        return text

    def user_name_set_value(self, entry, value):
        names = self.get_user_names(value=value)
        if self.as_guest.get_active():
            entry.set_text(names[1])
        else:
            entry.set_text(names[0])

    # workaround to FICS Password input doesnt handle strings starting with a number
    # https://github.com/pychess/pychess/issues/1375
    def password_set_value(self, entry, value):
        entry.set_text(str(value))

    # workaround to Can't type IP to FICS login dialog
    # https://github.com/pychess/pychess/issues/1360
    def host_get_value(self, entry):
        return entry.get_text().replace(".", "|")

    def host_set_value(self, entry, value):
        entry.set_text(str(value).replace("|", "."))

    def on_logOnAsGuest_toggled(self, widget):
        names = self.get_user_names()
        self.widgets["nameEntry"].set_text(
            names[1] if widget.get_active() else names[0])
        if self.ics == "ICC":
            self.widgets["nameLabel"].set_sensitive(not widget.get_active())
            self.widgets["nameEntry"].set_sensitive(not widget.get_active())
        else:
            self.widgets["nameLabel"].set_sensitive(True)
            self.widgets["nameEntry"].set_sensitive(True)
        self.widgets["passwordLabel"].set_sensitive(not widget.get_active())
        self.widgets["passEntry"].set_sensitive(not widget.get_active())
        conf.set("asGuestCheck", widget.get_active(), section=self.ics)

    def on_timeseal_toggled(self, widget):
        conf.set("timesealCheck", widget.get_active(), section=self.ics)

    def on_ics_combo_changed(self, combo):
        tree_iter = combo.get_active_iter()
        if tree_iter is not None:
            model = combo.get_model()
            self.ics = model[tree_iter][0]
            # print("Selected: %s" % self.ics)
            self.widgets["logOnAsGuest"].set_active(
                conf.get("asGuestCheck", section=self.ics))
            self.on_logOnAsGuest_toggled(self.widgets["logOnAsGuest"])
            self.user_name_set_value(
                self.widgets["nameEntry"],
                conf.get("usernameEntry", section=self.ics))
            self.password_set_value(
                self.widgets["passEntry"],
                conf.get("passwordEntry", section=self.ics))
            self.host_set_value(self.widgets["hostEntry"],
                                conf.get("hostEntry", section=self.ics))
            self.widgets["timesealCheck"].set_active(
                conf.get("timesealCheck", section=self.ics))
            self.on_timeseal_toggled(self.widgets["timesealCheck"])

    def _disconnect(self):
        for obj in self.cids:
            for cid in self.cids[obj]:
                if obj.handler_is_connected(cid):
                    obj.disconnect(cid)
        self.cids.clear()

        if self.connection is not None:
            self.connection.close()
            if not self.connection_task.cancelled():
                self.connection_task.cancel()
            self.connection = None
        if self.helperconn is not None:
            self.helperconn.close()
            if not self.helperconn_task.cancelled():
                self.helperconn_task.cancel()
            self.helperconn = None
        self.lounge = None

    def _cancel(self):
        if self.connection is not None:
            self.connection.cancel()
        if self.helperconn is not None:
            self.helperconn.cancel()
        self._disconnect()

    def show(self):
        self.widgets["fics_logon"].show()

    def present(self):
        self.widgets["fics_logon"].present()

    def hide(self):
        self.widgets["fics_logon"].hide()

    def showConnecting(self):
        self.widgets["progressbarBox"].show()
        self.widgets["mainbox"].set_sensitive(False)
        self.widgets["connectButton"].hide()
        self.widgets["stopButton"].show()

        def pulse():
            self.widgets["progressbar"].pulse()
            if self.connection is None:
                return False
            else:
                return not self.connection.isConnected()

        self.pulser = GLib.timeout_add(30, pulse)

    def showNormal(self):
        self.widgets["mainbox"].set_sensitive(True)
        self.widgets["connectButton"].show()
        self.widgets["fics_logon"].set_default(self.widgets["connectButton"])
        self.widgets["stopButton"].hide()
        self.widgets["progressbarBox"].hide()
        self.widgets["progressbar"].set_text("")
        GObject.source_remove(self.pulser)

    def showMessage(self, connection, message):
        self.widgets["progressbar"].set_text(message)

    def showError(self, connection, error):
        text = str(error)
        if isinstance(error, IOError):
            title = _("Connection Error")
        elif isinstance(error, LogOnException):
            title = _("Log on Error")
        elif isinstance(error, EOFError):
            title = _("Connection was closed")
        elif isinstance(error, socket.error):
            title = _("Connection Error")
            text = ", ".join(map(str, error.args))
        elif isinstance(error, socket.gaierror) or \
                isinstance(error, socket.herror):
            title = _("Address Error")
            text = ", ".join(map(str, error.args))
        elif isinstance(error, AutoLogoutException):
            title = _("Auto-logout")
            text = _(
                "You have been logged out because you were idle more than 60 minutes"
            )
        else:
            title = str(error.__class__)

        self.showNormal()

        content_area = self.infobar.get_content_area()
        for widget in content_area:
            content_area.remove(widget)
        content = Gtk.HBox()
        image = Gtk.Image()
        image.set_from_stock(Gtk.STOCK_DIALOG_WARNING, Gtk.IconSize.DIALOG)
        content.pack_start(image, False, False, 0)
        vbox = Gtk.VBox()
        label = Gtk.Label()
        label.props.xalign = 0
        label.props.justify = Gtk.Justification.LEFT
        label.set_markup("<b><big>%s</big></b>" % title)
        vbox.pack_start(label, True, False, 0)
        for line in str(text).split("\n"):
            label = Gtk.Label()
            label.set_size_request(476, -1)
            label.props.selectable = True
            label.props.wrap = True
            label.props.xalign = 0
            label.props.justify = Gtk.Justification.LEFT
            label.set_markup(line)
            vbox.pack_start(label, True, False, 0)
        content.pack_start(vbox, False, False, 7)
        content_area.add(content)
        self.widgets["messagePanel"].show_all()

    def onCreateNew(self, button):
        if self.widgets["hostEntry"].get_text() == "chessclub.com":
            webbrowser.open(
                "https://store.chessclub.com/customer/account/create/")
        else:
            webbrowser.open("http://www.freechess.org/Register/index.html")

    def onConnectClicked(self, button):
        self.canceled = False
        self.widgets["messagePanel"].hide()

        if self.widgets["logOnAsGuest"].get_active():
            username = self.widgets["nameEntry"].get_text()
            password = ""
        else:
            username = self.widgets["nameEntry"].get_text()
            password = self.widgets["passEntry"].get_text()

        if port:
            ports = (port, )
        else:
            ports = self.widgets["portsEntry"].get_text()
            ports = list(map(int, re.findall(r"\d+", ports)))
            if 5000 not in ports:
                ports.append(5000)
            if 23 not in ports:
                ports.append(23)
        alternate_host = self.widgets["hostEntry"].get_text()

        timeseal = self.widgets["timesealCheck"].get_active()

        self.showConnecting()
        self.host = host if host is not None else alternate_host if alternate_host else "freechess.org"
        self.connection = FICSMainConnection(self.host, ports, timeseal,
                                             username, password)
        for signal, callback in (("connected", self.onConnected),
                                 ("error", self.onConnectionError),
                                 ("connectingMsg", self.showMessage)):
            self.cids[self.connection].append(
                self.connection.connect(signal, callback))
        self.main_connected_event = asyncio.Event()
        self.connection_task = create_task(self.connection.start())

        # guest users are rather limited on ICC (helper connection is useless)
        if self.host not in ("localhost", "127.0.0.1", "chessclub.com"):
            self.helperconn = FICSHelperConnection(self.connection, self.host,
                                                   ports, timeseal)
            self.helperconn.connect("error", self.onHelperConnectionError)

            @asyncio.coroutine
            def coro():
                yield from self.main_connected_event.wait()
                yield from self.helperconn.start()

            self.helperconn_task = create_task(coro())

    def onHelperConnectionError(self, connection, error):
        if self.helperconn is not None:
            dialog = Gtk.MessageDialog(mainwindow(),
                                       type=Gtk.MessageType.QUESTION,
                                       buttons=Gtk.ButtonsType.YES_NO)
            dialog.set_markup(_("Guest logins disabled by FICS server"))
            text = "PyChess can maintain users status and games list only if it changes\n\
            'open', 'gin' and 'availinfo' user variables.\n\
            Do you enable to set these variables on?"

            dialog.format_secondary_text(text)
            response = dialog.run()
            dialog.destroy()

            self.helperconn.cancel()
            self.helperconn.close()
            self.helperconn = None

            set_user_vars = response == Gtk.ResponseType.YES

            @asyncio.coroutine
            def coro():
                yield from self.main_connected_event.wait()
                self.connection.start_helper_manager(set_user_vars)

            create_task(coro())

    def onConnected(self, connection):
        self.main_connected_event.set()
        if connection.ICC:
            self.connection.start_helper_manager(True)

        self.lounge = perspective_manager.get_perspective("fics")
        self.lounge.open_lounge(connection, self.helperconn, self.host)
        self.hide()
        self.lounge.show()
        self.lounge.connect("logout",
                            lambda iclounge: self.onLogout(connection))
        self.cids[self.lounge].append(
            self.lounge.connect("autoLogout",
                                lambda lounge: self.onAutologout(connection)))

        self.showNormal()
        self.widgets["messagePanel"].hide()

    def onCancel(self, widget, hide):
        self.canceled = True

        if self.connection and self.connection.isConnecting():
            self._cancel()
            self.showNormal()
        if hide:
            self.widgets["fics_logon"].hide()

    def onConnectionError(self, connection, error):
        self._disconnect()
        if not self.canceled:
            self.showError(connection, error)
            self.present()

    def onLogout(self, connection):
        self._disconnect()

    def onAutologout(self, connection):
        self._disconnect()
        self.showError(connection, AutoLogoutException())
        self.present()
Exemple #6
0
class ICLogon(object):
    def __init__(self):
        self.connection = None
        self.helperconn = None
        self.connection_task = None
        self.helperconn_task = None
        self.lounge = None
        self.canceled = False
        self.cids = defaultdict(list)
        self.widgets = uistuff.GladeWidgets("fics_logon.glade")
        uistuff.keepWindowSize("fics_logon",
                               self.widgets["fics_logon"],
                               defaultPosition=uistuff.POSITION_GOLDEN)
        self.widgets["fics_logon"].connect(
            'key-press-event',
            lambda w, e: e.keyval == Gdk.KEY_Escape and w.hide())

        self.ics = "FICS"
        self.as_guest = self.widgets["logOnAsGuest"]

        self.widgets["logOnAsGuest"].connect("toggled", self.on_logOnAsGuest_toggled)

        def on_username_changed(widget):
            conf.set("usernameEntry", self.user_name_get_value(widget), section=self.ics)
        self.widgets["nameEntry"].connect("changed", on_username_changed)

        def on_password_changed(widget):
            conf.set("passwordEntry", widget.get_text(), section=self.ics)
        self.widgets["passEntry"].connect("changed", on_password_changed)

        def on_host_changed(widget):
            conf.set("hostEntry", self.host_get_value(widget), section=self.ics)
        self.widgets["hostEntry"].connect("changed", on_host_changed)

        self.widgets["timesealCheck"].connect("toggled", self.on_timeseal_toggled)

        self.infobar = Gtk.InfoBar()
        self.infobar.set_message_type(Gtk.MessageType.WARNING)
        # self.widgets["messagePanelHBox"].pack_start(self.infobar,
        #    expand=False, fill=False)
        self.widgets["messagePanelHBox"].pack_start(self.infobar, False, False,
                                                    0)
        self.widgets["cancelButton"].connect("clicked", self.onCancel, True)
        self.widgets["stopButton"].connect("clicked", self.onCancel, False)
        self.widgets["createNewButton"].connect("clicked", self.onCreateNew)
        self.widgets["connectButton"].connect("clicked", self.onConnectClicked)

        self.widgets["progressbar"].set_show_text(True)

    def get_user_names(self, value=None):
        """ Split and return usernameEntry config item into registered and guest username
        """
        if value is not None:
            names = value.split("|")
        else:
            names = conf.get("usernameEntry", "", section=self.ics).split("|")
        if len(names) == 0:
            names = ["", ""]
        elif len(names) < 2:
            names.append(names[0])
        return names

    def user_name_get_value(self, entry):
        names = self.get_user_names()
        if self.as_guest.get_active():
            text = "%s|%s" % (names[0], entry.get_text())
        else:
            text = "%s|%s" % (entry.get_text(), names[1])
        return text

    def user_name_set_value(self, entry, value):
        names = self.get_user_names(value=value)
        if self.as_guest.get_active():
            entry.set_text(names[1])
        else:
            entry.set_text(names[0])

    # workaround to FICS Password input doesnt handle strings starting with a number
    # https://github.com/pychess/pychess/issues/1375
    def password_set_value(self, entry, value):
        entry.set_text(str(value))

    # workaround to Can't type IP to FICS login dialog
    # https://github.com/pychess/pychess/issues/1360
    def host_get_value(self, entry):
        return entry.get_text().replace(".", "|")

    def host_set_value(self, entry, value):
        entry.set_text(str(value).replace("|", "."))

    def on_logOnAsGuest_toggled(self, widget):
        names = self.get_user_names()
        self.widgets["nameEntry"].set_text(names[1] if widget.get_active() else names[0])
        if self.ics == "ICC":
            self.widgets["nameLabel"].set_sensitive(not widget.get_active())
            self.widgets["nameEntry"].set_sensitive(not widget.get_active())
        else:
            self.widgets["nameLabel"].set_sensitive(True)
            self.widgets["nameEntry"].set_sensitive(True)
        self.widgets["passwordLabel"].set_sensitive(not widget.get_active())
        self.widgets["passEntry"].set_sensitive(not widget.get_active())
        conf.set("asGuestCheck", widget.get_active(), section=self.ics)

    def on_timeseal_toggled(self, widget):
        conf.set("timesealCheck", widget.get_active(), section=self.ics)

    def on_ics_combo_changed(self, combo):
        tree_iter = combo.get_active_iter()
        if tree_iter is not None:
            model = combo.get_model()
            self.ics = model[tree_iter][0]
            # print("Selected: %s" % self.ics)
            self.widgets["logOnAsGuest"].set_active(conf.get("asGuestCheck", True, section=self.ics))
            self.on_logOnAsGuest_toggled(self.widgets["logOnAsGuest"])
            self.user_name_set_value(self.widgets["nameEntry"], conf.get("usernameEntry", "", section=self.ics))
            self.password_set_value(self.widgets["passEntry"], conf.get("passwordEntry", "", section=self.ics))
            default_host = "freechess.org" if self.ics == "FICS" else "chessclub.com"
            self.host_set_value(self.widgets["hostEntry"], conf.get("hostEntry", default_host, section=self.ics))
            self.widgets["timesealCheck"].set_active(conf.get("timesealCheck", True, section=self.ics))
            self.on_timeseal_toggled(self.widgets["timesealCheck"])

    def _disconnect(self):
        for obj in self.cids:
            for cid in self.cids[obj]:
                if obj.handler_is_connected(cid):
                    obj.disconnect(cid)
        self.cids.clear()

        if self.connection is not None:
            self.connection.close()
            if not self.connection_task.cancelled():
                self.connection_task.cancel()
            self.connection = None
        if self.helperconn is not None:
            self.helperconn.close()
            if not self.helperconn_task.cancelled():
                self.helperconn_task.cancel()
            self.helperconn = None
        self.lounge = None

    def _cancel(self):
        if self.connection is not None:
            self.connection.cancel()
        if self.helperconn is not None:
            self.helperconn.cancel()
        self._disconnect()

    def show(self):
        self.widgets["fics_logon"].show()

    def present(self):
        self.widgets["fics_logon"].present()

    def hide(self):
        self.widgets["fics_logon"].hide()

    def showConnecting(self):
        self.widgets["progressbarBox"].show()
        self.widgets["mainbox"].set_sensitive(False)
        self.widgets["connectButton"].hide()
        self.widgets["stopButton"].show()

        def pulse():
            self.widgets["progressbar"].pulse()
            if self.connection is None:
                return False
            else:
                return not self.connection.isConnected()

        self.pulser = GObject.timeout_add(30, pulse)

    def showNormal(self):
        self.widgets["mainbox"].set_sensitive(True)
        self.widgets["connectButton"].show()
        self.widgets["fics_logon"].set_default(self.widgets["connectButton"])
        self.widgets["stopButton"].hide()
        self.widgets["progressbarBox"].hide()
        self.widgets["progressbar"].set_text("")
        GObject.source_remove(self.pulser)

    def showMessage(self, connection, message):
        self.widgets["progressbar"].set_text(message)

    def showError(self, connection, error):
        text = str(error)
        if isinstance(error, IOError):
            title = _("Connection Error")
        elif isinstance(error, LogOnException):
            title = _("Log on Error")
        elif isinstance(error, EOFError):
            title = _("Connection was closed")
        elif isinstance(error, socket.error):
            title = _("Connection Error")
            text = ", ".join(map(str, error.args))
        elif isinstance(error, socket.gaierror) or \
                isinstance(error, socket.herror):
            title = _("Address Error")
            text = ", ".join(map(str, error.args))
        elif isinstance(error, AutoLogoutException):
            title = _("Auto-logout")
            text = _(
                "You have been logged out because you were idle more than 60 minutes")
        else:
            title = str(error.__class__)

        self.showNormal()

        content_area = self.infobar.get_content_area()
        for widget in content_area:
            content_area.remove(widget)
        content = Gtk.HBox()
        image = Gtk.Image()
        image.set_from_stock(Gtk.STOCK_DIALOG_WARNING, Gtk.IconSize.DIALOG)
        content.pack_start(image, False, False, 0)
        vbox = Gtk.VBox()
        label = Gtk.Label()
        label.props.xalign = 0
        label.props.justify = Gtk.Justification.LEFT
        label.set_markup("<b><big>%s</big></b>" % title)
        vbox.pack_start(label, True, False, 0)
        for line in str(text).split("\n"):
            label = Gtk.Label()
            label.set_size_request(476, -1)
            label.props.selectable = True
            label.props.wrap = True
            label.props.xalign = 0
            label.props.justify = Gtk.Justification.LEFT
            label.set_markup(line)
            vbox.pack_start(label, True, False, 0)
        content.pack_start(vbox, False, False, 7)
        content_area.add(content)
        self.widgets["messagePanel"].show_all()

    def onCreateNew(self, button):
        if self.widgets["hostEntry"].get_text() == "chessclub.com":
            webbrowser.open("https://store.chessclub.com/customer/account/create/")
        else:
            webbrowser.open("http://www.freechess.org/Register/index.html")

    def onConnectClicked(self, button):
        self.canceled = False
        self.widgets["messagePanel"].hide()

        if self.widgets["logOnAsGuest"].get_active():
            username = self.widgets["nameEntry"].get_text()
            password = ""
        else:
            username = self.widgets["nameEntry"].get_text()
            password = self.widgets["passEntry"].get_text()

        if port:
            ports = (port, )
        else:
            ports = self.widgets["portsEntry"].get_text()
            ports = list(map(int, re.findall("\d+", ports)))
            if 5000 not in ports:
                ports.append(5000)
            if 23 not in ports:
                ports.append(23)
        alternate_host = self.widgets["hostEntry"].get_text()

        timeseal = self.widgets["timesealCheck"].get_active()

        self.showConnecting()
        self.host = host if host is not None else alternate_host if alternate_host else "freechess.org"
        self.connection = FICSMainConnection(self.host, ports, timeseal, username, password)
        for signal, callback in (("connected", self.onConnected),
                                 ("error", self.onConnectionError),
                                 ("connectingMsg", self.showMessage)):
            self.cids[self.connection].append(self.connection.connect(signal, callback))
        self.main_connected_event = asyncio.Event()
        self.connection_task = asyncio.async(self.connection.start())

        # guest users are rather limited on ICC (helper connection is useless)
        if self.host not in ("localhost", "127.0.0.1", "chessclub.com"):
            self.helperconn = FICSHelperConnection(self.connection, self.host, ports, timeseal)
            self.helperconn.connect("error", self.onHelperConnectionError)

            @asyncio.coroutine
            def coro():
                yield from self.main_connected_event.wait()
                yield from self.helperconn.start()

            self.helperconn_task = asyncio.async(coro())

    def onHelperConnectionError(self, connection, error):
        if self.helperconn is not None:
            dialog = Gtk.MessageDialog(mainwindow(), type=Gtk.MessageType.QUESTION,
                                       buttons=Gtk.ButtonsType.YES_NO)
            dialog.set_markup(_("Guest logins disabled by FICS server"))
            text = "PyChess can maintain users status and games list only if it changes\n\
            'open', 'gin' and 'availinfo' user variables.\n\
            Do you enable to set these variables on?"
            dialog.format_secondary_text(text)
            response = dialog.run()
            dialog.destroy()

            self.helperconn.cancel()
            self.helperconn.close()
            self.helperconn = None

            set_user_vars = response == Gtk.ResponseType.YES

            @asyncio.coroutine
            def coro():
                yield from self.main_connected_event.wait()
                self.connection.start_helper_manager(set_user_vars)
            asyncio.async(coro())

    def onConnected(self, connection):
        self.main_connected_event.set()
        if connection.ICC:
            self.connection.start_helper_manager(True)

        self.lounge = perspective_manager.get_perspective("fics")
        self.lounge.open_lounge(connection, self.helperconn, self.host)
        self.hide()
        self.lounge.show()
        self.lounge.connect("logout",
                            lambda iclounge: self.onLogout(connection))
        self.cids[self.lounge].append(self.lounge.connect(
            "autoLogout", lambda lounge: self.onAutologout(connection)))

        self.showNormal()
        self.widgets["messagePanel"].hide()

    def onCancel(self, widget, hide):
        self.canceled = True

        if self.connection and self.connection.isConnecting():
            self._cancel()
            self.showNormal()
        if hide:
            self.widgets["fics_logon"].hide()

    def onConnectionError(self, connection, error):
        self._disconnect()
        if not self.canceled:
            self.showError(connection, error)
            self.present()

    def onLogout(self, connection):
        self._disconnect()

    def onAutologout(self, connection):
        self._disconnect()
        self.showError(connection, AutoLogoutException())
        self.present()
Exemple #7
0
class PyChessFICS(PyChess):
    def __init__ (self, password, from_address, to_address):
        PyChess.__init__(self)
        
        self.ports = (23, 5000)
        if not password:
            self.username = "******"
        else: self.username = "******"
        self.owner = "Lobais"
        self.password = password
        self.from_address = "The PyChess Bot <%s>" % from_address
        self.to_address = "Thomas Dybdahl Ahle <%s>" % to_address
        
        # Possible start times
        self.minutes = (1,2,3,4,5,6,7,8,9,10)
        self.gains = (0,5,10,15,20)
        # Possible colors. None == random
        self.colors = (WHITE, BLACK, None) 
        # The amount of random challenges, that PyChess sends with each seek
        self.challenges = 10
        enableEGTB()
        
        self.sudos = set()
        self.ownerOnline = False
        self.waitingForPassword = None
        self.log = []
        self.acceptedTimesettings = []
        
        self.worker = None
        
        repeat_sleep(self.sendChallenges, 60*1)
    
    def __triangular(self, low, high, mode):
        """Triangular distribution.
        Continuous distribution bounded by given lower and upper limits,
        and having a given mode value in-between.
        http://en.wikipedia.org/wiki/Triangular_distribution
        """
        u = random.random()
        c = (mode - low) / (high - low)
        if u > c:
            u = 1 - u
            c = 1 - c
            low, high = high, low
        tri = low + (high - low) * (u * c) ** 0.5
        if tri < mode:
            return int(tri)
        elif tri > mode:
            return int(math.ceil(tri))
        return int(round(tri))
    
    def sendChallenges(self):
        if self.connection.bm.isPlaying():
            return True
        
        statsbased = ((0.39197722779282, 3, 0),
                      (0.59341408108783, 5, 0),
                      (0.77320877377846, 1, 0),
                      (0.8246379941394, 10, 0),
                      (0.87388717406441, 2, 12),
                      (0.91443760169489, 15, 0),
                      (0.9286423058163, 4, 0),
                      (0.93891977227793, 2, 0),
                      (0.94674539138335, 20, 0),
                      (0.95321476842423, 2, 2),
                      (0.9594588808257, 5, 2),
                      (0.96564528079889, 3, 2),
                      (0.97173859621034, 7, 0),
                      (0.97774906636184, 3, 1),
                      (0.98357243654425, 5, 12),
                      (0.98881309737017, 5, 5),
                      (0.99319644938247, 6, 0),
                      (0.99675879556023, 3, 12),
                      (1, 5, 3))
        
        #n = random.random()
        #for culminativeChance, minute, gain in statsbased:
        #    if n < culminativeChance:
        #        break
        
        culminativeChance, minute, gain = random.choice(statsbased)
        
        #type = random.choice((TYPE_LIGHTNING, TYPE_BLITZ, TYPE_STANDARD))
        #if type == TYPE_LIGHTNING:
        #    minute = self.__triangular(0,2+1,1)
        #    mingain = not minute and 1 or 0
        #    maxgain = int((3-minute)*3/2)
        #    gain = random.randint(mingain, maxgain)
        #elif type == TYPE_BLITZ:
        #    minute = self.__triangular(0,14+1,5)
        #    mingain = max(int((3-minute)*3/2+1), 0)
        #    maxgain = int((15-minute)*3/2)
        #    gain = random.randint(mingain, maxgain)
        #elif type == TYPE_STANDARD:
        #    minute = self.__triangular(0,20+1,12)
        #    mingain = max(int((15-minute)*3/2+1), 0)
        #    maxgain = int((20-minute)*3/2)
        #    gain = self.__triangular(mingain, maxgain, mingain)
        
        #color = random.choice(self.colors)
        self.extendlog(["Seeking %d %d" % (minute, gain)])
        self.connection.glm.seek(minute, gain, True)
        opps = random.sample(self.connection.players.get_online_playernames(),
                             self.challenges)
        self.extendlog("Challenging %s" % op for op in opps)
        for player in opps:
            self.connection.om.challenge(player, minute, gain, True)
        
        return True
    
    def makeReady(self):
        signal.signal(signal.SIGINT, Gtk.main_quit)
        
        PyChess.makeReady(self)
        
        self.connection = FICSMainConnection("freechess.org", self.ports,
                                             self.username, self.password)
        self.connection.connect("connectingMsg", self.__showConnectLog)
        self.connection._connect()
        
        self.connection.glm.connect("addPlayer", self.__onAddPlayer)
        self.connection.glm.connect("removePlayer", self.__onRemovePlayer)
        self.connection.cm.connect("privateMessage", self.__onTell)
        self.connection.alm.connect("logOut", self.__onLogOut)
        self.connection.bm.connect("playGameCreated", self.__onGameCreated)
        self.connection.bm.connect("curGameEnded", self.__onGameEnded)
        self.connection.bm.connect("boardUpdate", self.__onBoardUpdate)
        self.connection.om.connect("onChallengeAdd", self.__onChallengeAdd)
        self.connection.om.connect("onOfferAdd", self.__onOfferAdd)
        self.connection.adm.connect("onAdjournmentsList", self.__onAdjournmentsList)
        self.connection.em.connect("onAmbiguousMove", self.__onAmbiguousMove)
        self.connection.em.connect("onIllegalMove", self.__onAmbiguousMove)
        
        self.connection.adm.queryAdjournments()
        self.connection.lvm.setVariable("autoflag", 1)
        
        self.connection.fm.setFingerNote(1,
            "PyChess is the chess engine bundled with the PyChess %s " % pychess.VERSION +
            "chess client. This instance is owned by %s, but acts " % self.owner +
            "quite autonomously.")
        
        self.connection.fm.setFingerNote(2,
            "PyChess is 100% Python code and is released under the terms of " +
            "the GPL. The evalution function is largely equal to the one of" +
            "GnuChess, but it plays quite differently.")
        
        self.connection.fm.setFingerNote(3,
            "PyChess runs on an elderly AMD Sempron(tm) Processor 3200+, 512 " +
            "MB DDR2 Ram, but is built to take use of 64bit calculating when " +
            "accessible, through the gpm library.")
        
        self.connection.fm.setFingerNote(4,
            "PyChess uses a small 500 KB openingbook based solely on Kasparov " +
            "games. The engine doesn't have much endgame knowledge, but might " +
            "in some cases access an online endgamedatabase.")
        
        self.connection.fm.setFingerNote(5,
            "PyChess will allow any pause/resume and adjourn wishes, but will " +
            "deny takebacks. Draw, abort and switch offers are accepted, " +
            "if they are found to be an advance. Flag is auto called, but " +
            "PyChess never resigns. We don't want you to forget your basic " +
            "mating skills.")
    
    def main(self):
        self.connection.run()
        self.extendlog([str(self.acceptedTimesettings)])
        self.phoneHome("Session ended\n"+"\n".join(self.log))
        print("Session ended")
    
    def run(self):
        t = Thread(target=self.main, name=fident(self.main))
        t.daemon = True
        t.start()
        Gdk.threads_init()
        Gtk.main()
    
    #===========================================================================
    # General
    #===========================================================================
    
    def __showConnectLog (self, connection, message):
        print(message)
    
    def __onLogOut (self, autoLogoutManager):
        self.connection.close()
        #sys.exit()
    
    def __onAddPlayer (self, gameListManager, player):
        if player["name"] in self.sudos:
            self.sudos.remove(player["name"])
        if player["name"] == self.owner:
            self.connection.cm.tellPlayer(self.owner, "Greetings")
            self.ownerOnline = True
    
    def __onRemovePlayer (self, gameListManager, playername):
        if playername == self.owner:
            self.ownerOnline = False
    
    def __onAdjournmentsList (self, adjournManager, adjournments):
        for adjournment in adjournments:
            if adjournment["online"]:
                adjournManager.challenge(adjournment["opponent"])
    
    def __usage (self):
        return "|| PyChess bot help file || " +\
               "# help 'Displays this help file' " +\
               "# sudo <password> <command> 'Lets PyChess execute the given command' "+\
               "# sendlog 'Makes PyChess send you its current log'"
    
    def __onTell (self, chatManager, name, title, isadmin, text):
        
        if self.waitingForPassword:
            if text.strip() == self.password or (not self.password and text == "none"):
                self.sudos.add(name)
                self.tellHome("%s gained sudo access" % name)
                self.connection.client.run_command(self.waitingForPassword)
            else:
                chatManager.tellPlayer(name, "Wrong password")
                self.tellHome("%s failed sudo access" % name)
            self.waitingForPassword = None
            return
        
        args = text.split()
        
        #if args == ["help"]:
        #    chatManager.tellPlayer(name, self.__usage())
        
        if args[0] == "sudo":
            command = " ".join(args[1:])
            if name in self.sudos or name == self.owner:
                # Notice: This can be used to make nasty loops
                print(command, file=self.connection.client)
            else:
                print(repr(name), self.sudos)
                chatManager.tellPlayer(name, "Please send me the password")
                self.waitingForPassword = command
        
        elif args == ["sendlog"]:
            if self.log:
                # TODO: Consider email
                chatManager.tellPlayer(name, "\\n".join(self.log))
            else:
                chatManager.tellPlayer(name, "The log is currently empty")
        
        else:
            if self.ownerOnline:
                self.tellHome("%s told me '%s'" % (name, text))
            else:
                def onlineanswer (message):
                    data = urlopen("http://www.pandorabots.com/pandora/talk?botid=8d034368fe360895",
                                   urlencode({"message":message, "botcust2":"x"}).encode("utf-8")).read().decode('utf-8')
                    ss = "<b>DMPGirl:</b>"
                    es = "<br>"
                    answer = data[data.find(ss)+len(ss) : data.find(es,data.find(ss))]
                    chatManager.tellPlayer(name, answer)
                t = Thread(target=onlineanswer,
                           name=fident(onlineanswer),
                           args=(text,))
                t.daemon = True
                t.start()
            #chatManager.tellPlayer(name, "Sorry, your request was nonsense.\n"+\
            #                           "Please read my help file for more info")
    
    #===========================================================================
    # Challenges and other offers
    #===========================================================================
    
    def __onChallengeAdd (self, offerManager, index, match):
        #match = {"tp": type, "w": fname, "rt": rating, "color": color,
        #         "r": rated, "t": mins, "i": incr}
        offerManager.acceptIndex(index)
    
    def __onOfferAdd (self, offerManager, offer):
        if offer.type in (PAUSE_OFFER, RESUME_OFFER, ADJOURN_OFFER):
            offerManager.accept(offer)
        elif offer.type in (TAKEBACK_OFFER,):
            offerManager.decline(offer)
        elif offer.type in (DRAW_OFFER, ABORT_OFFER, SWITCH_OFFER):
            if self.__willingToDraw():
                offerManager.accept(offer)
            else: offerManager.decline(offer)
    
    #===========================================================================
    # Playing
    #===========================================================================
    
    def __onGameCreated (self, boardManager, ficsgame):
        
        base = int(ficsgame.minutes)*60
        inc = int(ficsgame.inc)
        self.clock[:] = base, base
        self.increment[:] = inc, inc
        self.gameno = ficsgame.gameno
        self.lastPly = -1
        
        self.acceptedTimesettings.append((base, inc))
        
        self.tellHome("Starting a game (%s, %s) gameno: %s" %
                (ficsgame.wplayer.name, ficsgame.bplayer.name, ficsgame.gameno))
        
        if ficsgame.bplayer.name.lower() == self.connection.getUsername().lower():
            self.playingAs = BLACK
        else:
            self.playingAs = WHITE
        
        self.board = LBoard(NORMALCHESS)
        # Now we wait until we recieve the board.
    
    def __go (self):
        if self.worker:
            self.worker.cancel()
        self.worker = GtkWorker(lambda worker: PyChess._PyChess__go(self, worker))
        self.worker.connect("published", lambda w, msg: self.extendlog(msg))
        self.worker.connect("done", self.__onMoveCalculated)
        self.worker.execute()

    def __willingToDraw (self):
        return self.scr <= 0 # FIXME: this misbehaves in all but the simplest use cases
    
    def __onGameEnded (self, boardManager, ficsgame):
        self.tellHome(reprResult_long[ficsgame.result] + " " + reprReason_long[ficsgame.reason])
        lsearch.searching = False
        if self.worker:
            self.worker.cancel()
            self.worker = None
    
    def __onMoveCalculated (self, worker, sanmove):
        if worker.isCancelled() or not sanmove:
            return
        self.board.applyMove(parseSAN(self.board,sanmove))
        self.connection.bm.sendMove(sanmove)
        self.extendlog(["Move sent %s" % sanmove])
    
    def __onBoardUpdate (self, boardManager, gameno, ply, curcol, lastmove, fen, wname, bname, wms, bms):
        self.extendlog(["","I got move %d %s for gameno %s" % (ply, lastmove, gameno)])
        
        if self.gameno != gameno:
            return
        
        self.board.applyFen(fen)
        
        self.clock[:] = wms/1000., bms/1000.
        
        if curcol == self.playingAs:
            self.__go()
    
    def __onAmbiguousMove (self, errorManager, move):
        # This is really a fix for fics, but sometimes it is necessary
        if determineAlgebraicNotation(move) == SAN:
            self.board.popMove()
            move_ = parseSAN(self.board, move)
            lanmove = toLAN(self.board, move_)
            self.board.applyMove(move_)
            self.connection.bm.sendMove(lanmove)
        else:
            self.connection.cm.tellOpponent(
                    "I'm sorry, I wanted to move %s, but FICS called " % move +
                    "it 'Ambigious'. I can't find another way to express it, " +
                    "so you can win")
            self.connection.bm.resign()
    
    #===========================================================================
    # Utils
    #===========================================================================
    
    def extendlog(self, messages):
        [log.info(m+"\n") for m in messages]
        self.log.extend(messages)
        del self.log[:-10]
    
    def tellHome(self, message):
        print(message)
        if self.ownerOnline:
            self.connection.cm.tellPlayer(self.owner, message)
    
    def phoneHome(self, message):
        
        SENDMAIL = '/usr/sbin/sendmail'
        SUBJECT = "Besked fra botten"
        
        p = subprocess.Popen([SENDMAIL, '-f',
                              email.Utils.parseaddr(self.from_address)[1],
                              email.Utils.parseaddr(self.to_address)[1]],
                              stdin=subprocess.PIPE)
        
        print("MIME-Version: 1.0", file=p.stdin)
        print("Content-Type: text/plain; charset=UTF-8", file=p.stdin)
        print("Content-Disposition: inline", file=p.stdin)
        print("From: %s" % self.from_address, file=p.stdin)
        print("To: %s" % self.to_address, file=p.stdin)
        print("Subject: %s" % SUBJECT, file=p.stdin)
        print(file=p.stdin)
        print(message, file=p.stdin)
        print("Cheers", file=p.stdin)
        
        p.stdin.close()
        p.wait()
Exemple #8
0
class PyChessFICS(PyChess):
    def __init__(self, password, from_address, to_address):
        PyChess.__init__(self)

        self.ports = (23, 5000)
        if not password:
            self.username = "******"
        else:
            self.username = "******"
        self.owner = "Lobais"
        self.password = password
        self.from_address = "The PyChess Bot <%s>" % from_address
        self.to_address = "Thomas Dybdahl Ahle <%s>" % to_address

        # Possible start times
        self.minutes = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
        self.gains = (0, 5, 10, 15, 20)
        # Possible colors. None == random
        self.colors = (WHITE, BLACK, None)
        # The amount of random challenges, that PyChess sends with each seek
        self.challenges = 10
        enableEGTB()

        self.sudos = set()
        self.ownerOnline = False
        self.waitingForPassword = None
        self.log = []
        self.acceptedTimesettings = []

        self.worker = None

        repeat_sleep(self.sendChallenges, 60 * 1)

    def __triangular(self, low, high, mode):
        """Triangular distribution.
        Continuous distribution bounded by given lower and upper limits,
        and having a given mode value in-between.
        http://en.wikipedia.org/wiki/Triangular_distribution
        """
        u = random.random()
        c = (mode - low) / (high - low)
        if u > c:
            u = 1 - u
            c = 1 - c
            low, high = high, low
        tri = low + (high - low) * (u * c)**0.5
        if tri < mode:
            return int(tri)
        elif tri > mode:
            return int(math.ceil(tri))
        return int(round(tri))

    def sendChallenges(self):
        if self.connection.bm.isPlaying():
            return True

        statsbased = ((0.39197722779282, 3, 0), (0.59341408108783, 5, 0),
                      (0.77320877377846, 1, 0), (0.8246379941394, 10, 0),
                      (0.87388717406441, 2, 12), (0.91443760169489, 15, 0),
                      (0.9286423058163, 4, 0), (0.93891977227793, 2,
                                                0), (0.94674539138335, 20, 0),
                      (0.95321476842423, 2, 2), (0.9594588808257, 5,
                                                 2), (0.96564528079889, 3, 2),
                      (0.97173859621034, 7, 0), (0.97774906636184, 3,
                                                 1), (0.98357243654425, 5, 12),
                      (0.98881309737017, 5, 5), (0.99319644938247, 6,
                                                 0), (0.99675879556023, 3,
                                                      12), (1, 5, 3))

        #n = random.random()
        #for culminativeChance, minute, gain in statsbased:
        #    if n < culminativeChance:
        #        break

        culminativeChance, minute, gain = random.choice(statsbased)

        #type = random.choice((TYPE_LIGHTNING, TYPE_BLITZ, TYPE_STANDARD))
        #if type == TYPE_LIGHTNING:
        #    minute = self.__triangular(0,2+1,1)
        #    mingain = not minute and 1 or 0
        #    maxgain = int((3-minute)*3/2)
        #    gain = random.randint(mingain, maxgain)
        #elif type == TYPE_BLITZ:
        #    minute = self.__triangular(0,14+1,5)
        #    mingain = max(int((3-minute)*3/2+1), 0)
        #    maxgain = int((15-minute)*3/2)
        #    gain = random.randint(mingain, maxgain)
        #elif type == TYPE_STANDARD:
        #    minute = self.__triangular(0,20+1,12)
        #    mingain = max(int((15-minute)*3/2+1), 0)
        #    maxgain = int((20-minute)*3/2)
        #    gain = self.__triangular(mingain, maxgain, mingain)

        #color = random.choice(self.colors)
        self.extendlog(["Seeking %d %d" % (minute, gain)])
        self.connection.glm.seek(minute, gain, True)
        opps = random.sample(self.connection.players.get_online_playernames(),
                             self.challenges)
        self.extendlog("Challenging %s" % op for op in opps)
        for player in opps:
            self.connection.om.challenge(player, minute, gain, True)

        return True

    def makeReady(self):
        signal.signal(signal.SIGINT, Gtk.main_quit)

        PyChess.makeReady(self)

        self.connection = FICSMainConnection("freechess.org", self.ports,
                                             self.username, self.password)
        self.connection.connect("connectingMsg", self.__showConnectLog)
        self.connection._connect()

        self.connection.glm.connect("addPlayer", self.__onAddPlayer)
        self.connection.glm.connect("removePlayer", self.__onRemovePlayer)
        self.connection.cm.connect("privateMessage", self.__onTell)
        self.connection.alm.connect("logOut", self.__onLogOut)
        self.connection.bm.connect("playGameCreated", self.__onGameCreated)
        self.connection.bm.connect("curGameEnded", self.__onGameEnded)
        self.connection.bm.connect("boardUpdate", self.__onBoardUpdate)
        self.connection.om.connect("onChallengeAdd", self.__onChallengeAdd)
        self.connection.om.connect("onOfferAdd", self.__onOfferAdd)
        self.connection.adm.connect("onAdjournmentsList",
                                    self.__onAdjournmentsList)
        self.connection.em.connect("onAmbiguousMove", self.__onAmbiguousMove)
        self.connection.em.connect("onIllegalMove", self.__onAmbiguousMove)

        self.connection.adm.queryAdjournments()
        self.connection.lvm.setVariable("autoflag", 1)

        self.connection.fm.setFingerNote(
            1, "PyChess is the chess engine bundled with the PyChess %s " %
            pychess.VERSION +
            "chess client. This instance is owned by %s, but acts " %
            self.owner + "quite autonomously.")

        self.connection.fm.setFingerNote(
            2,
            "PyChess is 100% Python code and is released under the terms of " +
            "the GPL. The evalution function is largely equal to the one of" +
            "GnuChess, but it plays quite differently.")

        self.connection.fm.setFingerNote(
            3,
            "PyChess runs on an elderly AMD Sempron(tm) Processor 3200+, 512 "
            +
            "MB DDR2 Ram, but is built to take use of 64bit calculating when "
            + "accessible, through the gpm library.")

        self.connection.fm.setFingerNote(
            4,
            "PyChess uses a small 500 KB openingbook based solely on Kasparov "
            +
            "games. The engine doesn't have much endgame knowledge, but might "
            + "in some cases access an online endgamedatabase.")

        self.connection.fm.setFingerNote(
            5,
            "PyChess will allow any pause/resume and adjourn wishes, but will "
            + "deny takebacks. Draw, abort and switch offers are accepted, " +
            "if they are found to be an advance. Flag is auto called, but " +
            "PyChess never resigns. We don't want you to forget your basic " +
            "mating skills.")

    def main(self):
        self.connection.run()
        self.extendlog([str(self.acceptedTimesettings)])
        self.phoneHome("Session ended\n" + "\n".join(self.log))
        print("Session ended")

    def run(self):
        t = Thread(target=self.main, name=fident(self.main))
        t.daemon = True
        t.start()
        Gdk.threads_init()
        Gtk.main()

    #===========================================================================
    # General
    #===========================================================================

    def __showConnectLog(self, connection, message):
        print(message)

    def __onLogOut(self, autoLogoutManager):
        self.connection.close()
        #sys.exit()

    def __onAddPlayer(self, gameListManager, player):
        if player["name"] in self.sudos:
            self.sudos.remove(player["name"])
        if player["name"] == self.owner:
            self.connection.cm.tellPlayer(self.owner, "Greetings")
            self.ownerOnline = True

    def __onRemovePlayer(self, gameListManager, playername):
        if playername == self.owner:
            self.ownerOnline = False

    def __onAdjournmentsList(self, adjournManager, adjournments):
        for adjournment in adjournments:
            if adjournment["online"]:
                adjournManager.challenge(adjournment["opponent"])

    def __usage(self):
        return "|| PyChess bot help file || " +\
               "# help 'Displays this help file' " +\
               "# sudo <password> <command> 'Lets PyChess execute the given command' "+\
               "# sendlog 'Makes PyChess send you its current log'"

    def __onTell(self, chatManager, name, title, isadmin, text):

        if self.waitingForPassword:
            if text.strip() == self.password or (not self.password
                                                 and text == "none"):
                self.sudos.add(name)
                self.tellHome("%s gained sudo access" % name)
                self.connection.client.run_command(self.waitingForPassword)
            else:
                chatManager.tellPlayer(name, "Wrong password")
                self.tellHome("%s failed sudo access" % name)
            self.waitingForPassword = None
            return

        args = text.split()

        #if args == ["help"]:
        #    chatManager.tellPlayer(name, self.__usage())

        if args[0] == "sudo":
            command = " ".join(args[1:])
            if name in self.sudos or name == self.owner:
                # Notice: This can be used to make nasty loops
                print(command, file=self.connection.client)
            else:
                print(repr(name), self.sudos)
                chatManager.tellPlayer(name, "Please send me the password")
                self.waitingForPassword = command

        elif args == ["sendlog"]:
            if self.log:
                # TODO: Consider email
                chatManager.tellPlayer(name, "\\n".join(self.log))
            else:
                chatManager.tellPlayer(name, "The log is currently empty")

        else:
            if self.ownerOnline:
                self.tellHome("%s told me '%s'" % (name, text))
            else:

                def onlineanswer(message):
                    data = urlopen(
                        "http://www.pandorabots.com/pandora/talk?botid=8d034368fe360895",
                        urlencode({
                            "message": message,
                            "botcust2": "x"
                        }).encode("utf-8")).read().decode('utf-8')
                    ss = "<b>DMPGirl:</b>"
                    es = "<br>"
                    answer = data[data.find(ss) +
                                  len(ss):data.find(es, data.find(ss))]
                    chatManager.tellPlayer(name, answer)

                t = Thread(target=onlineanswer,
                           name=fident(onlineanswer),
                           args=(text, ))
                t.daemon = True
                t.start()
            #chatManager.tellPlayer(name, "Sorry, your request was nonsense.\n"+\
            #                           "Please read my help file for more info")

    #===========================================================================
    # Challenges and other offers
    #===========================================================================

    def __onChallengeAdd(self, offerManager, index, match):
        #match = {"tp": type, "w": fname, "rt": rating, "color": color,
        #         "r": rated, "t": mins, "i": incr}
        offerManager.acceptIndex(index)

    def __onOfferAdd(self, offerManager, offer):
        if offer.type in (PAUSE_OFFER, RESUME_OFFER, ADJOURN_OFFER):
            offerManager.accept(offer)
        elif offer.type in (TAKEBACK_OFFER, ):
            offerManager.decline(offer)
        elif offer.type in (DRAW_OFFER, ABORT_OFFER, SWITCH_OFFER):
            if self.__willingToDraw():
                offerManager.accept(offer)
            else:
                offerManager.decline(offer)

    #===========================================================================
    # Playing
    #===========================================================================

    def __onGameCreated(self, boardManager, ficsgame):

        base = int(ficsgame.minutes) * 60
        inc = int(ficsgame.inc)
        self.clock[:] = base, base
        self.increment[:] = inc, inc
        self.gameno = ficsgame.gameno
        self.lastPly = -1

        self.acceptedTimesettings.append((base, inc))

        self.tellHome(
            "Starting a game (%s, %s) gameno: %s" %
            (ficsgame.wplayer.name, ficsgame.bplayer.name, ficsgame.gameno))

        if ficsgame.bplayer.name.lower() == self.connection.getUsername(
        ).lower():
            self.playingAs = BLACK
        else:
            self.playingAs = WHITE

        self.board = LBoard(NORMALCHESS)
        # Now we wait until we recieve the board.

    def __go(self):
        if self.worker:
            self.worker.cancel()
        self.worker = GtkWorker(
            lambda worker: PyChess._PyChess__go(self, worker))
        self.worker.connect("published", lambda w, msg: self.extendlog(msg))
        self.worker.connect("done", self.__onMoveCalculated)
        self.worker.execute()

    def __willingToDraw(self):
        return self.scr <= 0  # FIXME: this misbehaves in all but the simplest use cases

    def __onGameEnded(self, boardManager, ficsgame):
        self.tellHome(reprResult_long[ficsgame.result] + " " +
                      reprReason_long[ficsgame.reason])
        lsearch.searching = False
        if self.worker:
            self.worker.cancel()
            self.worker = None

    def __onMoveCalculated(self, worker, sanmove):
        if worker.isCancelled() or not sanmove:
            return
        self.board.applyMove(parseSAN(self.board, sanmove))
        self.connection.bm.sendMove(sanmove)
        self.extendlog(["Move sent %s" % sanmove])

    def __onBoardUpdate(self, boardManager, gameno, ply, curcol, lastmove, fen,
                        wname, bname, wms, bms):
        self.extendlog(
            ["",
             "I got move %d %s for gameno %s" % (ply, lastmove, gameno)])

        if self.gameno != gameno:
            return

        self.board.applyFen(fen)

        self.clock[:] = wms / 1000., bms / 1000.

        if curcol == self.playingAs:
            self.__go()

    def __onAmbiguousMove(self, errorManager, move):
        # This is really a fix for fics, but sometimes it is necessary
        if determineAlgebraicNotation(move) == SAN:
            self.board.popMove()
            move_ = parseSAN(self.board, move)
            lanmove = toLAN(self.board, move_)
            self.board.applyMove(move_)
            self.connection.bm.sendMove(lanmove)
        else:
            self.connection.cm.tellOpponent(
                "I'm sorry, I wanted to move %s, but FICS called " % move +
                "it 'Ambigious'. I can't find another way to express it, " +
                "so you can win")
            self.connection.bm.resign()

    #===========================================================================
    # Utils
    #===========================================================================

    def extendlog(self, messages):
        [log.info(m + "\n") for m in messages]
        self.log.extend(messages)
        del self.log[:-10]

    def tellHome(self, message):
        print(message)
        if self.ownerOnline:
            self.connection.cm.tellPlayer(self.owner, message)

    def phoneHome(self, message):

        SENDMAIL = '/usr/sbin/sendmail'
        SUBJECT = "Besked fra botten"

        p = subprocess.Popen([
            SENDMAIL, '-f',
            email.Utils.parseaddr(self.from_address)[1],
            email.Utils.parseaddr(self.to_address)[1]
        ],
                             stdin=subprocess.PIPE)

        print("MIME-Version: 1.0", file=p.stdin)
        print("Content-Type: text/plain; charset=UTF-8", file=p.stdin)
        print("Content-Disposition: inline", file=p.stdin)
        print("From: %s" % self.from_address, file=p.stdin)
        print("To: %s" % self.to_address, file=p.stdin)
        print("Subject: %s" % SUBJECT, file=p.stdin)
        print(file=p.stdin)
        print(message, file=p.stdin)
        print("Cheers", file=p.stdin)

        p.stdin.close()
        p.wait()