Exemple #1
0
    def handle_error(self):
        """Handle any uncaptured error in the core.

        Overrides asyncore's handle_error.

        """
        trace = traceback.format_exc()
        stderr(trace)
        LOGGER.error('Fatal error in core, please review exception log')
        # TODO: make not hardcoded
        logfile = codecs.open(
            os.path.join(self.config.core.logdir, 'exceptions.log'),
            'a',
            encoding='utf-8'
        )
        logfile.write('Fatal error in core, handle_error() was called\n')
        logfile.write('last raw line was %s' % self.raw)
        logfile.write(trace)
        logfile.write('Buffer:\n')
        logfile.write(self.buffer)
        logfile.write('----------------------------------------\n\n')
        logfile.close()
        if self.error_count > 10:
            if (datetime.now() - self.last_error_timestamp).seconds < 5:
                print >> sys.stderr, "Too many errors, can't continue"
                os._exit(1)
        self.last_error_timestamp = datetime.now()
        self.error_count = self.error_count + 1
Exemple #2
0
 def _modules(self):
     home = os.getcwd()
     modules_dir = os.path.join(home, 'modules')
     filenames = sopel.loader.enumerate_modules(self)
     os.sys.path.insert(0, modules_dir)
     for name, mod_spec in iteritems(filenames):
         path, type_ = mod_spec
         try:
             module, _ = sopel.loader.load_module(name, path, type_)
         except Exception as e:
             filename, lineno = sopel.tools.get_raising_file_and_line()
             rel_path = os.path.relpath(filename, os.path.dirname(__file__))
             raising_stmt = "%s:%d" % (rel_path, lineno)
             stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
         else:
             if hasattr(module, 'configure'):
                 prompt = name + ' module'
                 if module.__doc__:
                     doc = module.__doc__.split('\n', 1)[0]
                     if doc:
                         prompt = doc
                 prompt = 'Configure {} (y/n)? [n]'.format(prompt)
                 do_configure = get_input(prompt)
                 do_configure = do_configure and do_configure.lower() == 'y'
                 if do_configure:
                     module.configure(self)
     self.save()
Exemple #3
0
def handle_init(options):
    """Use config wizard to initialize a new configuration file for the bot

    :param options: parsed arguments
    :type options: ``argparse.Namespace``

    .. note::

       Due to how the config wizard works, the configuration filename's
       extension **must be** ``.cfg``.

    """
    config_filename = utils.find_config(
        config.DEFAULT_HOMEDIR,
        getattr(options, 'config', None) or 'default')
    config_name, ext = os.path.splitext(config_filename)

    if ext and ext != '.cfg':
        tools.stderr('Configuration wizard accepts .cfg files only')
        return 1
    elif not ext:
        config_filename = config_name + '.cfg'

    if os.path.isfile(config_filename):
        tools.stderr('Configuration file %s already exists' % config_filename)
        return 1

    print('Starting Sopel config wizard for: %s' % config_filename)
    config._wizard('all', config_name)
Exemple #4
0
 def signal_handler(sig, frame):
     if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT:
         tools.stderr('Got quit signal, shutting down.')
         p.quit('Closing')
     elif sig == signal.SIGUSR2 or sig == signal.SIGILL:
         tools.stderr('Got restart signal.')
         p.restart('Restarting')
Exemple #5
0
 def _timeout_check(self):
     while self.connected or self.connecting:
         if (datetime.now() - self.last_ping_time).seconds > int(self.config.core.timeout):
             stderr('Ping timeout reached after %s seconds, closing connection' % self.config.core.timeout)
             self.handle_close()
             break
         else:
             time.sleep(int(self.config.core.timeout))
Exemple #6
0
def reload_module_tree(bot, name, seen=None, silent=False):
    from types import ModuleType

    old_module = sys.modules[name]

    if seen is None:
        seen = {}
    if name not in seen:
        seen[name] = []

    old_callables = {}
    for obj_name, obj in iteritems(vars(old_module)):
        if callable(obj):
            if (getattr(obj, '__name__', None) == 'shutdown' and
                        obj in bot.shutdown_methods):
                # If this is a shutdown method, call it first.
                try:
                    stderr(
                        "calling %s.%s" % (
                            obj.__module__, obj.__name__,
                        )
                    )
                    obj(bot)
                except Exception as e:
                    stderr(
                        "Error calling shutdown method for module %s:%s" % (
                            obj.__module__, e
                        )
                    )
            bot.unregister(obj)
        elif (type(obj) is ModuleType and
              obj.__name__.startswith(name + '.') and
              obj.__name__ not in sys.builtin_module_names):
            # recurse into submodules, see issue 1056
            if obj not in seen[name]:
                seen[name].append(obj)
                reload(obj)
                reload_module_tree(bot, obj.__name__, seen, silent)

    modules = sopel.loader.enumerate_modules(bot.config)
    if name not in modules:
        return  # Only reload the top-level module, once recursion is finished

    # Also remove all references to sopel callables from top level of the
    # module, so that they will not get loaded again if reloading the
    # module does not override them.
    for obj_name in old_callables.keys():
        delattr(old_module, obj_name)

    # Also delete the setup function
    # Sub-modules shouldn't have setup functions, so do after the recursion check
    if hasattr(old_module, "setup"):
        delattr(old_module, "setup")

    path, type_ = modules[name]
    load_module(bot, name, path, type_, silent)
Exemple #7
0
    def handle_close(self):
        self.connection_registered = False

        self._shutdown()
        stderr('Closed!')

        # This will eventually call asyncore dispatchers close method, which
        # will release the main thread. This should be called last to avoid
        # race conditions.
        self.close()
Exemple #8
0
def wizard(filename):
    """Global Configuration Wizard

    :param str filename: name of the new file to be created
    :return: the created configuration object

    This wizard function helps the creation of a Sopel configuration file,
    with its core section and its plugins' sections.
    """
    homedir, basename = os.path.split(filename)
    if not basename:
        raise config.ConfigurationError(
            'Sopel requires a filename for its configuration, not a directory')

    try:
        if not os.path.isdir(homedir):
            print('Creating config directory at {}'.format(homedir))
            os.makedirs(homedir)
            print('Config directory created')
    except Exception:
        tools.stderr('There was a problem creating {}'.format(homedir))
        raise

    name, ext = os.path.splitext(basename)
    if not ext:
        # Always add .cfg if filename does not have an extension
        filename = os.path.join(homedir, name + '.cfg')
    elif ext != '.cfg':
        # It is possible to use a non-cfg file for Sopel
        # but the wizard does not allow it at the moment
        raise config.ConfigurationError(
            'Sopel uses ".cfg" as configuration file extension, not "%s".' % ext)

    settings = config.Config(filename, validate=False)

    print("Please answer the following questions "
          "to create your configuration file (%s):\n" % filename)
    config.core_section.configure(settings)
    if settings.option(
        'Would you like to see if there are any modules '
        'that need configuring'
    ):
        _plugins_wizard(settings)

    try:
        settings.save()
    except Exception:  # TODO: Be specific
        tools.stderr("Encountered an error while writing the config file. "
                     "This shouldn't happen. Check permissions.")
        raise

    print("Config file written successfully!")
    return settings
Exemple #9
0
def _load(bot, plugin):
    # handle errors while loading (if any)
    try:
        plugin.load()
        if plugin.has_setup():
            plugin.setup(bot)
        plugin.register(bot)
    except Exception as e:
        filename, lineno = tools.get_raising_file_and_line()
        rel_path = os.path.relpath(filename, os.path.dirname(__file__))
        raising_stmt = "%s:%d" % (rel_path, lineno)
        tools.stderr(
            "Error loading %s: %s (%s)" % (plugin.name, e, raising_stmt))
        raise
Exemple #10
0
def command_restart(opts):
    """Restart a running Sopel instance"""
    # Get Configuration
    try:
        settings = utils.load_settings(opts)
    except config.ConfigurationNotFound as error:
        tools.stderr('Configuration "%s" not found' % error.filename)
        return ERR_CODE

    if settings.core.not_configured:
        tools.stderr('Sopel is not configured, can\'t stop')
        return ERR_CODE

    # Redirect Outputs
    utils.redirect_outputs(settings, opts.quiet)

    # Get Sopel's PID
    filename = get_pid_filename(opts, settings.core.pid_dir)
    pid = get_running_pid(filename)

    if pid is None or not tools.check_pid(pid):
        tools.stderr('Sopel is not running!')
        return ERR_CODE

    tools.stderr('Asking Sopel to restart')
    if hasattr(signal, 'SIGUSR2'):
        os.kill(pid, signal.SIGUSR2)
    else:
        # Windows will not generate SIGILL itself
        # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal
        os.kill(pid, signal.SIGILL)
Exemple #11
0
 def initiate_connect(self, host, port):
     stderr("Connecting to %s:%s..." % (host, port))
     source_address = (self.config.core.bind_host, 0) if self.config.core.bind_host else None
     self.set_socket(socket.create_connection((host, port), source_address=source_address))
     if self.config.core.use_ssl and has_ssl:
         self.send = self._ssl_send
         self.recv = self._ssl_recv
     elif not has_ssl and self.config.core.use_ssl:
         stderr("SSL is not avilable on your system, attempting connection " "without it")
     self.connect((host, port))
     try:
         asyncore.loop()
     except KeyboardInterrupt:
         print("KeyboardInterrupt")
         self.quit("KeyboardInterrupt")
Exemple #12
0
def _plugins_wizard(settings):
    usable_plugins = plugins.get_usable_plugins(settings)
    for plugin, is_enabled in usable_plugins.values():
        if not is_enabled:
            # Do not configure non-enabled modules
            continue

        name = plugin.name
        try:
            _plugin_wizard(settings, plugin)
        except Exception as e:
            filename, lineno = tools.get_raising_file_and_line()
            rel_path = os.path.relpath(filename, os.path.dirname(__file__))
            raising_stmt = "%s:%d" % (rel_path, lineno)
            tools.stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
Exemple #13
0
    def error(self, trigger=None):
        """Called internally when a module causes an error."""
        try:
            trace = traceback.format_exc()
            if sys.version_info.major < 3:
                trace = trace.decode("utf-8", errors="xmlcharrefreplace")
            stderr(trace)
            try:
                lines = list(reversed(trace.splitlines()))
                report = [lines[0].strip()]
                for line in lines:
                    line = line.strip()
                    if line.startswith('File "'):
                        report.append(line[0].lower() + line[1:])
                        break
                else:
                    report.append("source unknown")

                signature = "%s (%s)" % (report[0], report[1])
                # TODO: make not hardcoded
                log_filename = os.path.join(self.config.core.logdir, "exceptions.log")
                with codecs.open(log_filename, "a", encoding="utf-8") as logfile:
                    logfile.write("Signature: %s\n" % signature)
                    if trigger:
                        logfile.write(
                            "from {} at {}. Message was: {}\n".format(
                                trigger.nick, str(datetime.now()), trigger.group(0)
                            )
                        )
                    logfile.write(trace)
                    logfile.write("----------------------------------------\n\n")
            except Exception as e:
                stderr("Could not save full traceback!")
                LOGGER.error("Could not save traceback from %s to file: %s", trigger.sender, str(e))

            if trigger and self.config.core.reply_errors and trigger.sender is not None:
                self.msg(trigger.sender, signature)
            if trigger:
                LOGGER.error("Exception from {}: {} ({})".format(trigger.sender, str(signature), trigger.raw))
        except Exception as e:
            if trigger and self.config.core.reply_errors and trigger.sender is not None:
                self.msg(trigger.sender, "Got an error.")
            if trigger:
                LOGGER.error("Exception from {}: {} ({})".format(trigger.sender, str(e), trigger.raw))
Exemple #14
0
    def found_terminator(self):
        line = self.buffer
        if line.endswith('\r'):
            line = line[:-1]
        self.buffer = ''
        self.last_ping_time = datetime.now()
        pretrigger = PreTrigger(self.nick, line)

        if pretrigger.event == 'PING':
            self.write(('PONG', pretrigger.args[-1]))
        elif pretrigger.event == 'ERROR':
            LOGGER.error("ERROR recieved from server: %s", pretrigger.args[-1])
            if self.hasquit:
                self.close_when_done()
        elif pretrigger.event == '433':
            stderr('Nickname already in use!')
            self.handle_close()

        self.dispatch(pretrigger)
Exemple #15
0
    def found_terminator(self):
        line = self.buffer
        if line.endswith("\r"):
            line = line[:-1]
        self.buffer = ""
        self.last_ping_time = datetime.now()
        pretrigger = PreTrigger(self.nick, line)
        if all(cap not in self.enabled_capabilities for cap in ["account-tag", "extended-join"]):
            pretrigger.tags.pop("account", None)

        if pretrigger.event == "PING":
            self.write(("PONG", pretrigger.args[-1]))
        elif pretrigger.event == "ERROR":
            LOGGER.error("ERROR recieved from server: %s", pretrigger.args[-1])
            if self.hasquit:
                self.close_when_done()
        elif pretrigger.event == "433":
            stderr("Nickname already in use!")
            self.handle_close()

        self.dispatch(pretrigger)
Exemple #16
0
    def found_terminator(self):
        line = self.buffer
        if line.endswith('\r'):
            line = line[:-1]
        self.buffer = ''
        self.last_ping_time = datetime.now()
        pretrigger = PreTrigger(self.nick, line)
        if all(cap not in self.enabled_capabilities for cap in ['account-tag', 'extended-join']):
            pretrigger.tags.pop('account', None)

        if pretrigger.event == 'PING':
            self.write(('PONG', pretrigger.args[-1]))
        elif pretrigger.event == 'ERROR':
            LOGGER.error("ERROR received from server: %s", pretrigger.args[-1])
            if self.hasquit:
                self.close_when_done()
        elif pretrigger.event == '433':
            stderr('Nickname already in use!')
            self.handle_close()

        self.dispatch(pretrigger)
Exemple #17
0
def check_not_root():
    """Check if root is running the bot.

    It raises a ``RuntimeError`` if the user has root privileges on Linux or
    if it is the ``Administrator`` account on Windows.
    """
    opersystem = platform.system()
    if opersystem in ["Linux", "Darwin"]:
        # Linux/Mac
        if os.getuid() == 0 or os.geteuid() == 0:
            raise RuntimeError('Error: Do not run Sopel with root privileges.')
    elif opersystem in ["Windows"]:
        # Windows
        if os.environ.get("USERNAME") == "Administrator":
            raise RuntimeError('Error: Do not run Sopel as Administrator.')
    else:
        tools.stderr(
            "Warning: %s is an uncommon operating system platform. "
            "Sopel should still work, but please contact Sopel's developers "
            "if you experience issues."
            % opersystem)
Exemple #18
0
def processJoin(bot, trigger):
    ips = getIPList(trigger.host)

    for ip in ips:
        bot.msg(bot.config.killbot.control_channel, 'doing lookup on %s' % ip)
        blResult = baseRBLLookup(ip)
        ban = False
        why = ''
        for r in blResult:
            stderr(r)
            if r[1] != False:
                if r[1] != None:
                    ban = True
                    why += "HIT: %s %s " % (r[0], r[1])
        if ban == True:
            if trigger.sender == '##politics':
                bot.write(['MODE', trigger.sender, '+b', '*!*@' + trigger.host])
                bot.write(['REMOVE', trigger.sender, trigger.nick], "This host has violated channel policy.")
            bot.msg(bot.config.killbot.control_channel, 'I would have banned %s on %s because of a blacklist hit.' % (trigger.nick, trigger.sender))
            bot.msg(bot.config.killbot.control_channel, why)
        else:
            bot.msg(bot.config.killbot.control_channel, '%s is clean.' % trigger.host)
Exemple #19
0
    def handle_connect(self):
        if self.config.core.use_ssl and has_ssl:
            if not self.config.core.verify_ssl:
                self.ssl = ssl.wrap_socket(self.socket, do_handshake_on_connect=True, suppress_ragged_eofs=True)
            else:
                self.ssl = ssl.wrap_socket(
                    self.socket,
                    do_handshake_on_connect=True,
                    suppress_ragged_eofs=True,
                    cert_reqs=ssl.CERT_REQUIRED,
                    ca_certs=self.ca_certs,
                )
                try:
                    ssl.match_hostname(self.ssl.getpeercert(), self.config.core.host)
                except ssl.CertificateError:
                    stderr("Invalid certficate, hostname mismatch!")
                    os.unlink(self.config.core.pid_file_path)
                    os._exit(1)
            self.set_socket(self.ssl)

        # Request list of server capabilities. IRCv3 servers will respond with
        # CAP * LS (which we handle in coretasks). v2 servers will respond with
        # 421 Unknown command, which we'll ignore
        self.write(("CAP", "LS", "302"))

        if self.config.core.auth_method == "server":
            password = self.config.core.auth_password
            self.write(("PASS", password))
        self.write(("NICK", self.nick))
        self.write(("USER", self.user, "+iw", self.nick), self.name)

        stderr("Connected.")
        self.last_ping_time = datetime.now()
        timeout_check_thread = threading.Thread(target=self._timeout_check)
        timeout_check_thread.daemon = True
        timeout_check_thread.start()
        ping_thread = threading.Thread(target=self._send_ping)
        ping_thread.daemon = True
        ping_thread.start()
Exemple #20
0
def command_start(opts):
    """Start a Sopel instance"""
    # Step One: Get the configuration file and prepare to run
    try:
        config_module = get_configuration(opts)
    except config.ConfigurationError as e:
        tools.stderr(e)
        return ERR_CODE_NO_RESTART

    if config_module.core.not_configured:
        tools.stderr('Bot is not configured, can\'t start')
        return ERR_CODE_NO_RESTART

    # Step Two: Manage logfile, stdout and stderr
    utils.redirect_outputs(config_module, opts.quiet)

    # Step Three: Handle process-lifecycle options and manage the PID file
    pid_dir = config_module.core.pid_dir
    pid_file_path = get_pid_filename(opts, pid_dir)
    pid = get_running_pid(pid_file_path)

    if pid is not None and tools.check_pid(pid):
        tools.stderr('There\'s already a Sopel instance running '
                     'with this config file.')
        tools.stderr('Try using either the `sopel stop` '
                     'or the `sopel restart` command.')
        return ERR_CODE

    if opts.daemonize:
        child_pid = os.fork()
        if child_pid is not 0:
            return

    with open(pid_file_path, 'w') as pid_file:
        pid_file.write(str(os.getpid()))

    # Step Four: Run Sopel
    ret = run(config_module, pid_file_path)

    # Step Five: Shutdown Clean-Up
    os.unlink(pid_file_path)

    if ret == -1:
        # Restart
        os.execv(sys.executable, ['python'] + sys.argv)
    else:
        # Quit
        return ret
Exemple #21
0
def plugins_wizard(filename):
    """Plugins Configuration Wizard

    :param str filename: path to an existing Sopel configuration
    :return: the configuration object

    This wizard function helps to configure plugins for an existing Sopel
    config file.
    """
    if not os.path.isfile(filename):
        raise config.ConfigurationNotFound(filename)

    settings = config.Config(filename, validate=False)
    _plugins_wizard(settings)

    try:
        settings.save()
    except Exception:  # TODO: Be specific
        tools.stderr("Encountered an error while writing the config file. "
                     "This shouldn't happen. Check permissions.")
        raise

    return settings
Exemple #22
0
    def _shutdown(self):
        stderr("Calling shutdown for %d modules." % (len(self.shutdown_methods),))

        for shutdown_method in self.shutdown_methods:
            try:
                stderr("calling %s.%s" % (shutdown_method.__module__, shutdown_method.__name__))
                shutdown_method(self)
            except Exception as e:
                stderr("Error calling shutdown method for module %s:%s" % (shutdown_method.__module__, e))
Exemple #23
0
def main(argv=None):
    """Sopel run script entry point"""
    try:
        # Step One: Parse The Command Line
        parser = build_parser()

        # make sure to have an action first (`legacy` by default)
        # TODO: `start` should be the default in Sopel 8
        argv = argv or sys.argv[1:]
        if not argv:
            argv = ['legacy']
        elif argv[0].startswith('-') and argv[0] not in ['-h', '--help']:
            argv = ['legacy'] + argv

        opts = parser.parse_args(argv)

        # Step Two: "Do not run as root" checks
        try:
            check_not_root()
        except RuntimeError as err:
            tools.stderr('%s' % err)
            return ERR_CODE

        # Step Three: Handle command
        action = getattr(opts, 'action', 'legacy')
        command = {
            'legacy': command_legacy,
            'start': command_start,
            'configure': command_configure,
            'stop': command_stop,
            'restart': command_restart,
        }.get(action)
        return command(opts)
    except KeyboardInterrupt:
        print("\n\nInterrupted")
        return ERR_CODE
Exemple #24
0
def run(config, pid_file, daemon=False):
    import sopel.bot as bot
    import sopel.web as web
    import sopel.logger
    from sopel.tools import stderr
    delay = 20
    # Inject ca_certs from config to web for SSL validation of web requests
    if not config.core.ca_certs:
        stderr('Could not open CA certificates file. SSL will not '
               'work properly.')
    web.ca_certs = config.core.ca_certs

    def signal_handler(sig, frame):
        if sig == signal.SIGUSR1 or sig == signal.SIGTERM:
            stderr('Got quit signal, shutting down.')
            p.quit('Closing')
    while True:
        try:
            p = bot.Sopel(config, daemon=daemon)
            if hasattr(signal, 'SIGUSR1'):
                signal.signal(signal.SIGUSR1, signal_handler)
            if hasattr(signal, 'SIGTERM'):
                signal.signal(signal.SIGTERM, signal_handler)
            sopel.logger.setup_logging(p)
            p.run(config.core.host, int(config.core.port))
        except KeyboardInterrupt:
            break
        except Exception:
            trace = traceback.format_exc()
            try:
                stderr(trace)
            except:
                pass
            logfile = open(os.path.join(config.core.logdir, 'exceptions.log'), 'a')
            logfile.write('Critical exception in core')
            logfile.write(trace)
            logfile.write('----------------------------------------\n\n')
            logfile.close()
            os.unlink(pid_file)
            os._exit(1)

        if not isinstance(delay, int):
            break
        if p.hasquit:
            break
        stderr('Warning: Disconnected. Reconnecting in %s seconds...' % delay)
        time.sleep(delay)
    os.unlink(pid_file)
    os._exit(0)
Exemple #25
0
def getIPList(hostname):

    #takes a hostname or ip address, returns list containing
    #ip addresses that hostname resolves to, or original ip address
    #always returns a list

    ipList = []

    if hostname.find('/') != -1:
        #this is a cloak, dns lookups don't matter
        return ipList

    if validateIP(hostname) == True:
        ipList.append('%s' % hostname)
        return ipList

    else:
        stderr('resolving %s' % hostname)
        try:
            ipv4 = dnsResolver.query(hostname,'A')
        except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
            ipv4 = []
        try:
            ipv6 = dnsResolver.query(hostname,'AAAA')
        except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
            ipv6 = []
        for a in ipv4:
            stderr('    resolved A record to %s' % a)
            ipList.append('%s' % a)
        for aaaa in ipv6:
            stderr('    resolved AAAA record to %s' % aaaa)
            ipList.append('%s' % aaaa)
        return ipList

    #for some reason we couldn't find anything
    stderr('getIPList fell through for %s' % hostname)
    return ipList
Exemple #26
0
    def log_raw(self, line, prefix):
        """Log raw line to the raw log."""
        if not self.config.core.log_raw:
            return
        if not os.path.isdir(self.config.core.logdir):
            try:
                os.mkdir(self.config.core.logdir)
            except Exception as e:
                stderr("There was a problem creating the logs directory.")
                stderr("%s %s" % (str(e.__class__), str(e)))
                stderr("Please fix this and then run Sopel again.")
                os._exit(1)
        f = codecs.open(os.path.join(self.config.core.logdir, "raw.log"), "a", encoding="utf-8")
        f.write(prefix + unicode(time.time()) + "\t")
        temp = line.replace("\n", "")

        f.write(temp)
        f.write("\n")
        f.close()
Exemple #27
0
    def log_raw(self, line, prefix):
        """Log raw line to the raw log."""
        if not self.config.core.log_raw:
            return
        if not os.path.isdir(self.config.core.logdir):
            try:
                os.mkdir(self.config.core.logdir)
            except Exception as e:
                stderr('There was a problem creating the logs directory.')
                stderr('%s %s' % (str(e.__class__), str(e)))
                stderr('Please fix this and then run Sopel again.')
                os._exit(1)
        f = codecs.open(os.path.join(self.config.core.logdir, 'raw.log'),
                        'a', encoding='utf-8')
        f.write(prefix + str(time.time()) + "\t")
        temp = line.replace('\n', '')

        f.write(temp)
        f.write("\n")
        f.close()
Exemple #28
0
    def _shutdown(self):
        stderr(
            'Calling shutdown for %d modules.' % (len(self.shutdown_methods),)
        )

        for shutdown_method in self.shutdown_methods:
            try:
                stderr(
                    "calling %s.%s" % (
                        shutdown_method.__module__, shutdown_method.__name__,
                    )
                )
                shutdown_method(self)
            except Exception as e:
                stderr(
                    "Error calling shutdown method for module %s:%s" % (
                        shutdown_method.__module__, e
                    )
                )
        # Avoid calling shutdown methods if we already have.
        self.shutdown_methods = []
Exemple #29
0
def handle_get(options):
    """Read the settings to display the value of <section> <key>"""
    try:
        settings = utils.load_settings(options)
    except Exception as error:
        tools.stderr(error)
        return 2

    section = options.section
    option = options.option

    # Making sure the section.option exists
    if not settings.parser.has_section(section):
        tools.stderr('Section "%s" does not exist' % section)
        return 1
    if not settings.parser.has_option(section, option):
        tools.stderr(
            'Section "%s" does not have a "%s" option' % (section, option))
        return 1

    # Display the value
    print(settings.get(section, option))
Exemple #30
0
def handle_get(options):
    """Read the settings to display the value of <section> <key>"""
    try:
        settings = utils.load_settings(options)
    except Exception as error:
        tools.stderr(error)
        return 2

    section = options.section
    option = options.option

    # Making sure the section.option exists
    if not settings.parser.has_section(section):
        tools.stderr('Section "%s" does not exist' % section)
        return 1
    if not settings.parser.has_option(section, option):
        tools.stderr('Section "%s" does not have a "%s" option' %
                     (section, option))
        return 1

    # Display the value
    print(settings.get(section, option))
    return 0  # successful operation
Exemple #31
0
import argparse
import logging
import os
import platform
import signal
import sys
import time

from sopel import __version__, bot, config, logger, tools
from . import utils

# This is in case someone somehow manages to install Sopel on an old version
# of pip (<9.0.0), which doesn't know about `python_requires`, or tries to run
# from source on an unsupported version of Python.
if sys.version_info < (3, 7):
    tools.stderr('Error: Sopel requires Python 3.7+.')
    sys.exit(1)

# Py3.7 EOL: https://www.python.org/dev/peps/pep-0537/#and-beyond-schedule
if sys.version_info < (3, 8):
    # TODO check this warning before releasing Sopel 8.0
    print(
        'Warning: Python 3.7 will reach end of life by June 2022 '
        'and will receive no further updates. '
        'Sopel 9.0 will drop support for it.',
        file=sys.stderr,
    )

LOGGER = logging.getLogger(__name__)

ERR_CODE = 1
Exemple #32
0
    def _shutdown(self):
        # Stop Job Scheduler
        stderr('Stopping the Job Scheduler.')
        self.scheduler.stop()

        try:
            self.scheduler.join(timeout=15)
        except RuntimeError:
            stderr('Unable to stop the Job Scheduler.')
        else:
            stderr('Job Scheduler stopped.')

        self.scheduler.clear_jobs()

        # Shutdown plugins
        stderr('Calling shutdown for %d modules.' %
               (len(self.shutdown_methods), ))
        for shutdown_method in self.shutdown_methods:
            try:
                stderr("calling %s.%s" % (
                    shutdown_method.__module__,
                    shutdown_method.__name__,
                ))
                shutdown_method(self)
            except Exception as e:
                stderr("Error calling shutdown method for module %s:%s" %
                       (shutdown_method.__module__, e))
        # Avoid calling shutdown methods if we already have.
        self.shutdown_methods = []
Exemple #33
0
def startup_reconnect(bot, trigger):

    # Startup
    stderr("[Sopel-startupmonologue] " + bot.nick + " has reconnected.")
    bot.osd(" has reconnected.", bot.channels.keys(), 'ACTION')
Exemple #34
0
def main(argv=None):
    try:
        # Step One: Parse The Command Line
        parser = build_parser()
        opts = parser.parse_args(argv or None)

        # Step Two: "Do not run as root" checks
        try:
            check_not_root()
        except RuntimeError as err:
            stderr('%s' % err)
            return ERR_CODE

        # Step Three: Handle "No config needed" options
        if opts.version:
            print_version()
            return

        if opts.wizard:
            _wizard('all', opts.config)
            return

        if opts.mod_wizard:
            _wizard('mod', opts.config)
            return

        if opts.list_configs:
            print_config()
            return

        # Step Four: Get the configuration file and prepare to run
        try:
            config_module = get_configuration(opts)
        except ConfigurationError as e:
            stderr(e)
            return ERR_CODE_NO_RESTART

        if config_module.core.not_configured:
            stderr('Bot is not configured, can\'t start')
            return ERR_CODE_NO_RESTART

        # Step Five: Manage logfile, stdout and stderr
        logfile = os.path.os.path.join(config_module.core.logdir, 'stdio.log')
        sys.stderr = tools.OutputRedirect(logfile, True, opts.quiet)
        sys.stdout = tools.OutputRedirect(logfile, False, opts.quiet)

        # Step Six: Handle process-lifecycle options and manage the PID file
        pid_dir = config_module.core.pid_dir
        pid_file_path = get_pid_filename(opts, pid_dir)
        old_pid = get_running_pid(pid_file_path)

        if old_pid is not None and tools.check_pid(old_pid):
            if not opts.quit and not opts.kill and not opts.restart:
                stderr(
                    'There\'s already a Sopel instance running with this config file'
                )
                stderr(
                    'Try using either the --quit, --restart, or --kill option')
                return ERR_CODE
            elif opts.kill:
                stderr('Killing the Sopel')
                os.kill(old_pid, signal.SIGKILL)
                return
            elif opts.quit:
                stderr('Signaling Sopel to stop gracefully')
                if hasattr(signal, 'SIGUSR1'):
                    os.kill(old_pid, signal.SIGUSR1)
                else:
                    # Windows will not generate SIGTERM itself
                    # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal
                    os.kill(old_pid, signal.SIGTERM)
                return
            elif opts.restart:
                stderr('Asking Sopel to restart')
                if hasattr(signal, 'SIGUSR2'):
                    os.kill(old_pid, signal.SIGUSR2)
                else:
                    # Windows will not generate SIGILL itself
                    # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal
                    os.kill(old_pid, signal.SIGILL)
                return
        elif opts.kill or opts.quit or opts.restart:
            stderr('Sopel is not running!')
            return ERR_CODE

        if opts.daemonize:
            child_pid = os.fork()
            if child_pid is not 0:
                return
        with open(pid_file_path, 'w') as pid_file:
            pid_file.write(str(os.getpid()))

        # Step Seven: Initialize and run Sopel
        ret = run(config_module, pid_file_path)
        os.unlink(pid_file_path)
        if ret == -1:
            os.execv(sys.executable, ['python'] + sys.argv)
        else:
            return ret

    except KeyboardInterrupt:
        print("\n\nInterrupted")
        return ERR_CODE
Exemple #35
0
def main(argv=None):
    global homedir
    # Step One: Parse The Command Line
    try:
        parser = argparse.ArgumentParser(description='Sopel IRC Bot',
                                         usage='%(prog)s [options]')
        parser.add_argument('-c', '--config', metavar='filename',
                            help='use a specific configuration file')
        parser.add_argument("-d", '--fork', action="store_true",
                            dest="daemonize", help="Daemonize sopel")
        parser.add_argument("-q", '--quit', action="store_true", dest="quit",
                            help="Gracefully quit Sopel")
        parser.add_argument("-k", '--kill', action="store_true", dest="kill",
                            help="Kill Sopel")
        parser.add_argument("-l", '--list', action="store_true",
                            dest="list_configs",
                            help="List all config files found")
        parser.add_argument("-m", '--migrate', action="store_true",
                            dest="migrate_configs",
                            help="Migrate config files to the new format")
        parser.add_argument('--quiet', action="store_true", dest="quiet",
                            help="Supress all output")
        parser.add_argument('-w', '--configure-all', action='store_true',
                            dest='wizard', help='Run the configuration wizard.')
        parser.add_argument('--configure-modules', action='store_true',
                            dest='mod_wizard', help=(
                                'Run the configuration wizard, but only for the '
                                'module configuration options.'))
        parser.add_argument('-v', '--version', action="store_true",
                            dest="version", help="Show version number and exit")
        opts = parser.parse_args()

        # Step Two: "Do not run as root" checks.
        try:
            # Linux/Mac
            if os.getuid() == 0 or os.geteuid() == 0:
                stderr('Error: Do not run Sopel with root privileges.')
                sys.exit(1)
        except AttributeError:
            # Windows
            if os.environ.get("USERNAME") == "Administrator":
                stderr('Error: Do not run Sopel as Administrator.')
                sys.exit(1)

        if opts.version:
            py_ver = '%s.%s.%s' % (sys.version_info.major,
                                   sys.version_info.minor,
                                   sys.version_info.micro)
            print('Sopel %s (running on python %s)' % (__version__, py_ver))
            print('http://sopel.chat/')
            return
        elif opts.wizard:
            _wizard('all', opts.config)
            return
        elif opts.mod_wizard:
            _wizard('mod', opts.config)
            return

        if opts.list_configs:
            configs = enumerate_configs()
            print('Config files in ~/.sopel:')
            if len(configs) is 0:
                print('\tNone found')
            else:
                for config in configs:
                    print('\t%s' % config)
            print('-------------------------')
            return

        config_name = opts.config or 'default'

        configpath = find_config(config_name)
        if not os.path.isfile(configpath):
            print("Welcome to Sopel!\nI can't seem to find the configuration file, so let's generate it!\n")
            if not configpath.endswith('.cfg'):
                configpath = configpath + '.cfg'
            _create_config(configpath)
            configpath = find_config(config_name)
        try:
            config_module = Config(configpath)
        except ConfigurationError as e:
            stderr(e)
            sys.exit(2)

        if config_module.core.not_configured:
            stderr('Bot is not configured, can\'t start')
            # exit with code 2 to prevent auto restart on fail by systemd
            sys.exit(2)

        logfile = os.path.os.path.join(config_module.core.logdir, 'stdio.log')

        config_module._is_daemonized = opts.daemonize

        sys.stderr = tools.OutputRedirect(logfile, True, opts.quiet)
        sys.stdout = tools.OutputRedirect(logfile, False, opts.quiet)

        # Handle --quit, --kill and saving the PID to file
        pid_dir = config_module.core.pid_dir
        if opts.config is None:
            pid_file_path = os.path.join(pid_dir, 'sopel.pid')
        else:
            basename = os.path.basename(opts.config)
            if basename.endswith('.cfg'):
                basename = basename[:-4]
            pid_file_path = os.path.join(pid_dir, 'sopel-%s.pid' % basename)
        if os.path.isfile(pid_file_path):
            with open(pid_file_path, 'r') as pid_file:
                try:
                    old_pid = int(pid_file.read())
                except ValueError:
                    old_pid = None
            if old_pid is not None and tools.check_pid(old_pid):
                if not opts.quit and not opts.kill:
                    stderr('There\'s already a Sopel instance running with this config file')
                    stderr('Try using the --quit or the --kill options')
                    sys.exit(1)
                elif opts.kill:
                    stderr('Killing the sopel')
                    os.kill(old_pid, signal.SIGKILL)
                    sys.exit(0)
                elif opts.quit:
                    stderr('Signaling Sopel to stop gracefully')
                    if hasattr(signal, 'SIGUSR1'):
                        os.kill(old_pid, signal.SIGUSR1)
                    else:
                        os.kill(old_pid, signal.SIGTERM)
                    sys.exit(0)
            elif old_pid is None or (not tools.check_pid(old_pid)
                                     and (opts.kill or opts.quit)):
                stderr('Sopel is not running!')
                sys.exit(1)
        elif opts.quit or opts.kill:
            stderr('Sopel is not running!')
            sys.exit(1)
        if opts.daemonize:
            child_pid = os.fork()
            if child_pid is not 0:
                sys.exit()
        with open(pid_file_path, 'w') as pid_file:
            pid_file.write(str(os.getpid()))

        # Step Five: Initialise And Run sopel
        run(config_module, pid_file_path)
    except KeyboardInterrupt:
        print("\n\nInterrupted")
        os._exit(1)
Exemple #36
0
def command_legacy(opts):
    """Legacy Sopel run script

    The ``legacy`` command manages the old-style ``sopel`` command line tool.
    Most of its features are replaced by the following commands:

    * ``sopel start`` replaces the default behavior (run the bot)
    * ``sopel stop`` replaces the ``--quit/--kill`` options
    * ``sopel restart`` replaces the ``--restart`` option
    * ``sopel configure`` replaces the
      ``-w/--configure-all/--configure-modules`` options

    The ``-v`` option for "version" is deprecated, ``-V/--version`` should be
    used instead.

    .. seealso::

       The github issue `#1471`__ tracks various changes requested for future
       versions of Sopel, some of them related to this legacy command.

       .. __: https://github.com/sopel-irc/sopel/issues/1471

    """
    # Step One: Handle "No config needed" options
    if opts.version:
        print_version()
        return
    elif opts.version_legacy:
        tools.stderr('WARNING: option -v is deprecated; '
                     'use `sopel -V/--version` instead')
        print_version()
        return

    if opts.wizard:
        tools.stderr('WARNING: option -w/--configure-all is deprecated; '
                     'use `sopel configure` instead')
        _wizard('all', opts.config)
        return

    if opts.mod_wizard:
        tools.stderr('WARNING: option --configure-modules is deprecated; '
                     'use `sopel configure --modules` instead')
        _wizard('mod', opts.config)
        return

    if opts.list_configs:
        print_config()
        return

    # Step Two: Get the configuration file and prepare to run
    try:
        config_module = get_configuration(opts)
    except ConfigurationError as e:
        tools.stderr(e)
        return ERR_CODE_NO_RESTART

    if config_module.core.not_configured:
        tools.stderr('Bot is not configured, can\'t start')
        return ERR_CODE_NO_RESTART

    # Step Three: Manage logfile, stdout and stderr
    utils.redirect_outputs(config_module, opts.quiet)

    # Step Four: Handle process-lifecycle options and manage the PID file
    pid_dir = config_module.core.pid_dir
    pid_file_path = get_pid_filename(opts, pid_dir)
    old_pid = get_running_pid(pid_file_path)

    if old_pid is not None and tools.check_pid(old_pid):
        if not opts.quit and not opts.kill and not opts.restart:
            tools.stderr(
                'There\'s already a Sopel instance running with this config file'
            )
            tools.stderr(
                'Try using either the `sopel stop` command or the `sopel restart` command'
            )
            return ERR_CODE
        elif opts.kill:
            tools.stderr('WARNING: option -k/--kill is deprecated; '
                         'use `sopel stop --kill` instead')
            tools.stderr('Killing the Sopel')
            os.kill(old_pid, signal.SIGKILL)
            return
        elif opts.quit:
            tools.stderr('WARNING: options -q/--quit is deprecated; '
                         'use `sopel stop` instead')
            tools.stderr('Signaling Sopel to stop gracefully')
            if hasattr(signal, 'SIGUSR1'):
                os.kill(old_pid, signal.SIGUSR1)
            else:
                # Windows will not generate SIGTERM itself
                # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal
                os.kill(old_pid, signal.SIGTERM)
            return
        elif opts.restart:
            tools.stderr('WARNING: options --restart is deprecated; '
                         'use `sopel restart` instead')
            tools.stderr('Asking Sopel to restart')
            if hasattr(signal, 'SIGUSR2'):
                os.kill(old_pid, signal.SIGUSR2)
            else:
                # Windows will not generate SIGILL itself
                # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/signal
                os.kill(old_pid, signal.SIGILL)
            return
    elif opts.kill or opts.quit or opts.restart:
        tools.stderr('Sopel is not running!')
        return ERR_CODE

    if opts.daemonize:
        child_pid = os.fork()
        if child_pid is not 0:
            return
    with open(pid_file_path, 'w') as pid_file:
        pid_file.write(str(os.getpid()))

    # Step Five: Initialize and run Sopel
    ret = run(config_module, pid_file_path)
    os.unlink(pid_file_path)
    if ret == -1:
        os.execv(sys.executable, ['python'] + sys.argv)
    else:
        return ret
Exemple #37
0
    def handle_connect(self):
        """
        Connect to IRC server, handle TLS and authenticate
        user if an account exists.
        """
        # handle potential TLS connection
        if self.config.core.use_ssl and has_ssl:
            if not self.config.core.verify_ssl:
                self.ssl = ssl.wrap_socket(self.socket,
                                           do_handshake_on_connect=True,
                                           suppress_ragged_eofs=True)
            else:
                self.ssl = ssl.wrap_socket(self.socket,
                                           do_handshake_on_connect=True,
                                           suppress_ragged_eofs=True,
                                           cert_reqs=ssl.CERT_REQUIRED,
                                           ca_certs=self.ca_certs)
                # connect to host specified in config first
                try:
                    ssl.match_hostname(self.ssl.getpeercert(), self.config.core.host)
                except ssl.CertificateError:
                    # the host in config and certificate don't match
                    LOGGER.error("hostname mismatch between configuration and certificate")
                    # check (via exception) if a CNAME matches as a fallback
                    has_matched = False
                    for hostname in self._get_cnames(self.config.core.host):
                        try:
                            ssl.match_hostname(self.ssl.getpeercert(), hostname)
                            LOGGER.warning("using {0} instead of {1} for TLS connection"
                                           .format(hostname, self.config.core.host))
                            has_matched = True
                            break
                        except ssl.CertificateError:
                            pass
                    if not has_matched:
                        # everything is broken
                        stderr("Invalid certificate, hostname mismatch!")
                        LOGGER.error("invalid certificate, no hostname matches")
                        if hasattr(self.config.core, 'pid_file_path'):
                            os.unlink(self.config.core.pid_file_path)
                            os._exit(1)
            self.set_socket(self.ssl)

        # Request list of server capabilities. IRCv3 servers will respond with
        # CAP * LS (which we handle in coretasks). v2 servers will respond with
        # 421 Unknown command, which we'll ignore
        self.write(('CAP', 'LS', '302'))

        # authenticate account if needed
        if self.config.core.auth_method == 'server':
            password = self.config.core.auth_password
            self.write(('PASS', password))
        self.write(('NICK', self.nick))
        self.write(('USER', self.user, '+iw', self.nick), self.name)

        # maintain connection
        stderr('Connected.')
        self.last_ping_time = datetime.now()
        timeout_check_thread = threading.Thread(target=self._timeout_check)
        timeout_check_thread.daemon = True
        timeout_check_thread.start()
        ping_thread = threading.Thread(target=self._send_ping)
        ping_thread.daemon = True
        ping_thread.start()
Exemple #38
0
def check_python_version():
    if sys.version_info < (2, 7):
        stderr(u'Error: You need at least Python 2.7!')
        sys.exit(1)
Exemple #39
0
 def signal_handler(sig, frame):
     if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT:
         stderr('Got quit signal, shutting down.')
         p.quit('Closing')
Exemple #40
0
def parse_event_001(bot, trigger):
    if 'RPL_Welcome' not in bot.memory:
        bot.memory['RPL_Welcome'] = trigger.args[1]
    stderr("\n" + trigger.event + "    " + str(trigger.args) + "\n")
def setup_thread(bot):

    bot.memory['Sopel-CommandsQuery'] = dict()
    for comtype in ['module', 'nickname', 'rule']:
        bot.memory['Sopel-CommandsQuery'][comtype + "_commands"] = dict()
        bot.memory['Sopel-CommandsQuery'][comtype + "_commands_count"] = 0

    filepathlisting = []

    # main Modules directory
    main_dir = os.path.dirname(os.path.abspath(sopel.__file__))
    modules_dir = os.path.join(main_dir, 'modules')
    filepathlisting.append(modules_dir)

    # Home Directory
    home_modules_dir = os.path.join(bot.config.homedir, 'modules')
    if os.path.isdir(home_modules_dir):
        filepathlisting.append(home_modules_dir)

    # pypi installed
    try:
        import sopel_modules
        pypi_modules = os.path.dirname(os.path.abspath(sopel_modules.__file__))
        pypi_modules_dir = os.path.join(pypi_modules, 'modules')
        filepathlisting.append(pypi_modules_dir)
    except Exception:
        pass

    # Extra directories
    filepathlist = []
    for directory in bot.config.core.extra:
        filepathlisting.append(directory)

    for directory in filepathlisting:
        for pathname in os.listdir(directory):
            path = os.path.join(directory, pathname)
            if (os.path.isfile(path) and path.endswith('.py')
                    and not path.startswith('_')):
                filepathlist.append(str(path))

    # CoreTasks
    ct_path = os.path.join(main_dir, 'coretasks.py')
    filepathlist.append(ct_path)

    for modulefile in filepathlist:
        module_file_lines = []
        module_file = open(modulefile, 'r')
        lines = module_file.readlines()
        for line in lines:
            module_file_lines.append(line)
        module_file.close()

        dict_from_file = dict()
        filelinelist = []

        for line in module_file_lines:

            if str(line).startswith("@"):
                line = str(line)[1:]

                # Commands
                if str(line).startswith(
                        tuple([
                            "commands", "module.commands",
                            "sopel.module.commands"
                        ])):
                    comtype = "module"
                    line = str(line).split("commands(")[-1]
                    line = str("(" + line)
                    validcoms = eval(str(line))
                    if isinstance(validcoms, tuple):
                        validcoms = list(validcoms)
                    else:
                        validcoms = [validcoms]
                    validcomdict = {"comtype": comtype, "validcoms": validcoms}
                    filelinelist.append(validcomdict)
                elif str(line).startswith(
                        tuple([
                            "nickname_commands", "module.nickname_commands",
                            "sopel.module.nickname_commands"
                        ])):
                    comtype = "nickname"
                    line = str(line).split("commands(")[-1]
                    line = str("(" + line)
                    validcoms = eval(str(line))
                    if isinstance(validcoms, tuple):
                        validcoms = list(validcoms)
                    else:
                        validcoms = [validcoms]
                    nickified = []
                    for nickcom in validcoms:
                        nickified.append(str(bot.nick) + " " + nickcom)
                    validcomdict = {"comtype": comtype, "validcoms": nickified}
                    filelinelist.append(validcomdict)
                elif str(line).startswith(
                        tuple(["rule", "module.rule", "sopel.module.rule"])):
                    comtype = "rule"
                    line = str(line).split("rule(")[-1]
                    validcoms = [str("(" + line)]
                    validcomdict = {"comtype": comtype, "validcoms": validcoms}
                    filelinelist.append(validcomdict)

        for atlinefound in filelinelist:

            comtype = atlinefound["comtype"]
            validcoms = atlinefound["validcoms"]

            comtypedict = str(comtype + "_commands")

            bot.memory['Sopel-CommandsQuery'][comtypedict + "_count"] += 1

            # default command to filename
            if "validcoms" not in dict_from_file.keys():
                dict_from_file["validcoms"] = validcoms

            maincom = dict_from_file["validcoms"][0]
            if len(dict_from_file["validcoms"]) > 1:
                comaliases = spicemanip.main(dict_from_file["validcoms"], '2+',
                                             'list')
            else:
                comaliases = []

            bot.memory['Sopel-CommandsQuery'][comtypedict][
                maincom] = dict_from_file
            for comalias in comaliases:
                if comalias not in bot.memory['Sopel-CommandsQuery'][
                        comtypedict].keys():
                    bot.memory['Sopel-CommandsQuery'][comtypedict][
                        comalias] = {
                            "aliasfor": maincom
                        }

    for comtype in ['module_commands', 'nickname_commands', 'rule_commands']:
        stderr("[Sopel-CommandsQuery] Found " +
               str(len(bot.memory['Sopel-CommandsQuery'][comtype].keys())) +
               " " + comtype + " commands.")

    if botevents_installed:
        set_bot_event(bot, "Sopel-CommandsQuery")
def setup(bot):
    stderr("[Sopel-CommandsQuery] Evaluating Core Commands List")

    threading.Thread(target=setup_thread, args=(bot, )).start()
def query_detection(bot, trigger):

    while "Sopel-CommandsQuery" not in bot.memory:
        pass

    # command must start with
    if not str(trigger).startswith(tuple(['?'])):
        return
    stderr(trigger.args)

    commands_list = dict()
    for commandstype in bot.memory['Sopel-CommandsQuery'].keys():
        if not commandstype.endswith("_count"):
            for com in bot.memory['Sopel-CommandsQuery'][commandstype].keys():
                if com not in commands_list.keys():
                    commands_list[com] = bot.memory['Sopel-CommandsQuery'][
                        commandstype][com]

    triggerargsarray = spicemanip.main(trigger, 'create')

    # command issued, check if valid
    querycommand = spicemanip.main(triggerargsarray, 1).lower()[1:]
    if len(querycommand) == 1:
        commandlist = []
        for command in commands_list.keys():
            if command.lower().startswith(querycommand):
                commandlist.append(command)
        if commandlist == []:
            bot.notice("No commands match " + str(querycommand) + ".",
                       trigger.nick)
            return
        else:
            bot.notice(
                "The following commands match " + str(querycommand) + ": " +
                spicemanip.main(commandlist, 'andlist') + ".", trigger.nick)
            return

    elif querycommand.endswith(tuple(["+"])):
        querycommand = querycommand[:-1]
        if querycommand not in commands_list.keys():
            bot.notice("The " + str(querycommand) +
                       " does not appear to be valid.")
            return
        realcom = querycommand
        if "aliasfor" in commands_list[querycommand].keys():
            realcom = commands_list[querycommand]["aliasfor"]
        validcomlist = commands_list[realcom]["validcoms"]
        bot.notice(
            "The following commands match " + str(querycommand) + ": " +
            spicemanip.main(validcomlist, 'andlist') + ".", trigger.nick)
        return

    elif querycommand.endswith(tuple(['?'])):
        querycommand = querycommand[:-1]
        sim_com, sim_num = [], []
        for com in commands_list.keys():
            similarlevel = SequenceMatcher(None, querycommand.lower(),
                                           com.lower()).ratio()
            sim_com.append(com)
            sim_num.append(similarlevel)
        sim_num, sim_com = (list(x) for x in zip(
            *sorted(zip(sim_num, sim_com), key=itemgetter(0))))
        closestmatch = spicemanip.main(sim_com, 'reverse', "list")
        listnumb, relist = 1, []
        for item in closestmatch:
            if listnumb <= 10:
                relist.append(str(item))
            listnumb += 1
        bot.notice(
            "The following commands may match " + str(querycommand) + ": " +
            spicemanip.main(relist, 'andlist') + ".", trigger.nick)
        return

    elif querycommand in commands_list.keys():
        bot.notice(
            "The following commands match " + str(querycommand) + ": " +
            str(querycommand) + ".", trigger.nick)
        return

    elif not querycommand:
        return

    else:
        commandlist = []
        for command in commands_list.keys():
            if command.lower().startswith(querycommand):
                commandlist.append(command)
        if commandlist == []:
            bot.notice("No commands match " + str(querycommand) + ".",
                       trigger.nick)
            return
        else:
            bot.notice(
                "The following commands match " + str(querycommand) + ": " +
                spicemanip.main(commandlist, 'andlist') + ".", trigger.nick)
            return
Exemple #44
0
def run(settings, pid_file, daemon=False):
    delay = 20

    # Acts as a welcome message, showing the program and platform version at start
    print_version()

    if not settings.core.ca_certs:
        tools.stderr(
            'Could not open CA certificates file. SSL will not work properly!')

    def signal_handler(sig, frame):
        if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT:
            LOGGER.warning('Got quit signal, shutting down.')
            p.quit('Closing')
        elif sig == signal.SIGUSR2 or sig == signal.SIGILL:
            LOGGER.warning('Got restart signal, shutting down and restarting.')
            p.restart('Restarting')

    # Define empty variable `p` for bot
    p = None
    while True:
        if p and p.hasquit:  # Check if `hasquit` was set for bot during disconnected phase
            break
        try:
            p = bot.Sopel(settings, daemon=daemon)
            if hasattr(signal, 'SIGUSR1'):
                signal.signal(signal.SIGUSR1, signal_handler)
            if hasattr(signal, 'SIGTERM'):
                signal.signal(signal.SIGTERM, signal_handler)
            if hasattr(signal, 'SIGINT'):
                signal.signal(signal.SIGINT, signal_handler)
            if hasattr(signal, 'SIGUSR2'):
                signal.signal(signal.SIGUSR2, signal_handler)
            if hasattr(signal, 'SIGILL'):
                signal.signal(signal.SIGILL, signal_handler)
            p.setup()
        except KeyboardInterrupt:
            break
        except Exception:
            # In that case, there is nothing we can do.
            # If the bot can't setup itself, then it won't run.
            # This is a critical case scenario, where the user should have
            # direct access to the exception traceback right in the console.
            # Besides, we can't know if logging has been set up or not, so
            # we can't rely on that here.
            tools.stderr('Unexpected error in bot setup')
            raise

        try:
            p.run(settings.core.host, int(settings.core.port))
        except KeyboardInterrupt:
            break
        except Exception:
            err_log = logging.getLogger('sopel.exceptions')
            err_log.exception('Critical exception in core')
            err_log.error('----------------------------------------')
            # TODO: This should be handled by command_start
            # All we should need here is a return value, but replacing the
            # os._exit() call below (at the end) broke ^C.
            # This one is much harder to test, so until that one's sorted it
            # isn't worth the risk of trying to remove this one.
            os.unlink(pid_file)
            os._exit(1)

        if not isinstance(delay, int):
            break
        if p.wantsrestart:
            return -1
        if p.hasquit:
            break
        LOGGER.warning('Disconnected. Reconnecting in %s seconds...', delay)
        time.sleep(delay)
    # TODO: This should be handled by command_start
    # All we should need here is a return value, but making this
    # a return makes Sopel hang on ^C after it says "Closed!"
    os.unlink(pid_file)
    os._exit(0)
Exemple #45
0
def setup(bot):
    stderr("[SpiceBot_DatabaseCache] Setting up Database Cache.")
    bot.memory['SpiceBot_DatabaseCache'] = dict()
Exemple #46
0
"""
from __future__ import unicode_literals, absolute_import, print_function, division

import argparse
import logging
import os
import platform
import signal
import sys
import time

from sopel import bot, config, logger, tools, __version__
from . import utils

if sys.version_info < (2, 7):
    tools.stderr('Error: Requires Python 2.7 or later. Try python2.7 sopel')
    sys.exit(1)
if sys.version_info.major == 2:
    if time.time() >= 1577836800:  # 2020-01-01 00:00:00 UTC
        state = 'is near end of life'
    else:
        state = 'has reached end of life and will receive no further updates'
    tools.stderr(
        'Warning: Python 2.x %s. Sopel will drop support in version 8.0.' %
        state)
if sys.version_info.major == 3 and sys.version_info.minor < 3:
    tools.stderr('Error: When running on Python 3, Python 3.3 is required.')
    sys.exit(1)

LOGGER = logging.getLogger(__name__)
Exemple #47
0
def run(settings, pid_file, daemon=False):
    """Run the bot with these ``settings``.

    :param settings: settings with which to run the bot
    :type settings: :class:`sopel.config.Config`
    :param str pid_file: path to the bot's PID file
    :param bool daemon: tell if the bot should be run as a daemon
    """
    delay = 20

    # Acts as a welcome message, showing the program and platform version at start
    print_version()
    # Also show the location of the config file used to load settings
    print("\nLoaded config file: {}".format(settings.filename))

    if not settings.core.ca_certs:
        tools.stderr(
            'Could not open CA certificates file. SSL will not work properly!')

    # Define empty variable `p` for bot
    p = None
    while True:
        if p and p.hasquit:  # Check if `hasquit` was set for bot during disconnected phase
            break
        try:
            p = bot.Sopel(settings, daemon=daemon)
            p.setup()
            p.set_signal_handlers()
        except KeyboardInterrupt:
            tools.stderr('Bot setup interrupted')
            break
        except Exception:
            # In that case, there is nothing we can do.
            # If the bot can't setup itself, then it won't run.
            # This is a critical case scenario, where the user should have
            # direct access to the exception traceback right in the console.
            # Besides, we can't know if logging has been set up or not, so
            # we can't rely on that here.
            tools.stderr('Unexpected error in bot setup')
            raise

        try:
            p.run(settings.core.host, int(settings.core.port))
        except KeyboardInterrupt:
            break
        except Exception:
            err_log = logging.getLogger('sopel.exceptions')
            err_log.exception('Critical exception in core')
            err_log.error('----------------------------------------')
            # TODO: This should be handled by command_start
            # All we should need here is a return value, but replacing the
            # os._exit() call below (at the end) broke ^C.
            # This one is much harder to test, so until that one's sorted it
            # isn't worth the risk of trying to remove this one.
            os.unlink(pid_file)
            os._exit(1)

        if not isinstance(delay, int):
            break
        if p.wantsrestart:
            return -1
        if p.hasquit:
            break
        LOGGER.warning('Disconnected. Reconnecting in %s seconds...', delay)
        time.sleep(delay)
    # TODO: This should be handled by command_start
    # All we should need here is a return value, but making this
    # a return makes Sopel hang on ^C after it says "Closed!"
    os.unlink(pid_file)
    os._exit(0)
Exemple #48
0
def main(argv=None):
    global homedir
    # Step One: Parse The Command Line
    try:
        parser = optparse.OptionParser('%prog [options]')
        parser.add_option('-c',
                          '--config',
                          metavar='filename',
                          help='use a specific configuration file')
        parser.add_option("-d",
                          '--fork',
                          action="store_true",
                          dest="deamonize",
                          help="Deamonize sopel")
        parser.add_option("-q",
                          '--quit',
                          action="store_true",
                          dest="quit",
                          help="Gracefully quit Sopel")
        parser.add_option("-k",
                          '--kill',
                          action="store_true",
                          dest="kill",
                          help="Kill Sopel")
        parser.add_option(
            '--exit-on-error',
            action="store_true",
            dest="exit_on_error",
            help="Exit immediately on every error instead of trying to recover"
        )
        parser.add_option("-l",
                          '--list',
                          action="store_true",
                          dest="list_configs",
                          help="List all config files found")
        parser.add_option("-m",
                          '--migrate',
                          action="store_true",
                          dest="migrate_configs",
                          help="Migrate config files to the new format")
        parser.add_option('--quiet',
                          action="store_true",
                          dest="quiet",
                          help="Supress all output")
        parser.add_option('-w',
                          '--configure-all',
                          action='store_true',
                          dest='wizard',
                          help='Run the configuration wizard.')
        parser.add_option(
            '--configure-modules',
            action='store_true',
            dest='mod_wizard',
            help=
            'Run the configuration wizard, but only for the module configuration options.'
        )
        parser.add_option(
            '--configure-database',
            action='store_true',
            dest='db_wizard',
            help=
            'Run the configuration wizard, but only for the database configuration options.'
        )
        opts, args = parser.parse_args(argv)

        if opts.wizard:
            wizard('all', opts.config)
            return
        elif opts.mod_wizard:
            wizard('mod', opts.config)
            return
        elif opts.db_wizard:
            wizard('db', opts.config)
            return

        check_python_version()
        if opts.list_configs is not None:
            configs = enumerate_configs()
            print 'Archives de configuation:'
            if len(configs[0]) is 0:
                print u'\tJe n\'ai trouvé pas rien'
            else:
                for config in configs:
                    print '\t%s' % config
            print '-------------------------'
            return

        config_name = opts.config or 'default'

        configpath = find_config(config_name)
        if not os.path.isfile(configpath):
            print u"Welcome to JoikervgBot configuration wizard! -- ¡Bienvenido al asistente de configuración de JoikervgBot! -- Bienvenue à l'assistant de configuration de JoikervgBot!\n"
            if not configpath.endswith('.cfg'):
                configpath = configpath + '.cfg'
            create_config(configpath)
            configpath = find_config(config_name)
        try:
            config_module = Config(configpath)
        except ConfigurationError as e:
            stderr(e)
            sys.exit(2)

        if config_module.core.not_configured:
            stderr(
                u'The bot is not configured. -- El bot no está configurado. -- Le bot n\'est pas configuré.'
            )
            # exit with code 2 to prevent auto restart on fail by systemd
            sys.exit(2)

        if not config_module.has_option('core', 'homedir'):
            config_module.dotdir = homedir
            config_module.homedir = homedir
        else:
            homedir = config_module.core.homedir
            config_module.dotdir = config_module.core.homedir

        if not config_module.core.logdir:
            config_module.core.logdir = os.path.join(homedir, 'logs')
        logfile = os.path.os.path.join(config_module.logdir, 'stdio.log')
        if not os.path.isdir(config_module.logdir):
            os.mkdir(config_module.logdir)

        if opts.exit_on_error:
            config_module.exit_on_error = True
        else:
            config_module.exit_on_error = False

        if opts.quiet is None:
            opts.quiet = False

        sys.stderr = tools.OutputRedirect(logfile, True, opts.quiet)
        sys.stdout = tools.OutputRedirect(logfile, False, opts.quiet)

        #Handle --quit, --kill and saving the PID to file
        pid_dir = config_module.core.pid_dir or homedir
        if opts.config is None:
            pid_file_path = os.path.join(pid_dir, 'sopel.pid')
        else:
            basename = os.path.basename(opts.config)
            if basename.endswith('.cfg'):
                basename = basename[:-4]
            pid_file_path = os.path.join(pid_dir, 'sopel-%s.pid' % basename)
        if os.path.isfile(pid_file_path):
            pid_file = open(pid_file_path, 'r')
            old_pid = int(pid_file.read())
            pid_file.close()
            if tools.check_pid(old_pid):
                if opts.quit is None and opts.kill is None:
                    stderr(
                        u'There is already a JoikervgBot running. -- Ya hay un bot ejecutándose. -- Il y a déjà un bot en fonctionnement.'
                    )
                    stderr(u'Try -- Intenta: --quit o --kill')
                    sys.exit(1)
                elif opts.kill:
                    stderr(
                        u'Killing JoikervgBot. -- Matando a JoikervgBot. -- Tuent JoikervgBot.'
                    )
                    os.kill(old_pid, signal.SIGKILL)
                    sys.exit(0)
                elif opts.quit:
                    stderr(
                        u'Quitting JoikervgBot. -- Desconectando a JoikervgBot. -- Déconnectant JoikervgBot.'
                    )
                    if hasattr(signal, 'SIGUSR1'):
                        os.kill(old_pid, signal.SIGUSR1)
                    else:
                        os.kill(old_pid, signal.SIGTERM)
                    sys.exit(0)
            elif not tools.check_pid(old_pid) and (opts.kill or opts.quit):
                stderr(
                    u'The bot is not running. -- El bot no se está ejecutando. -- Le bot n\'est pas en fonctionnement.'
                )
                sys.exit(1)
        elif opts.quit is not None or opts.kill is not None:
            stderr(
                u'The bot is not running. -- El bot no se está ejecutando. -- Le bot n\'est pas en fonctionnement.'
            )
            sys.exit(1)
        if opts.deamonize is not None:
            child_pid = os.fork()
            if child_pid is not 0:
                sys.exit()
        pid_file = open(pid_file_path, 'w')
        pid_file.write(str(os.getpid()))
        pid_file.close()
        config_module.pid_file_path = pid_file_path

        # Step Five: Initialise And Run sopel
        run(config_module)
    except KeyboardInterrupt:
        print "\n\nKeyboard Interrupt"
        os._exit(1)
Exemple #49
0
import logging
import os
import platform
import signal
import sys
import time

from sopel import bot, config, logger, tools, __version__
from . import utils

# This is in case someone somehow manages to install Sopel on an old version
# of pip (<9.0.0), which doesn't know about `python_requires`, or tries to run
# from source on an unsupported version of Python.
if sys.version_info < (2, 7) or (sys.version_info.major >= 3
                                 and sys.version_info < (3, 3)):
    tools.stderr('Error: Sopel requires Python 2.7+ or 3.3+.')
    sys.exit(1)
if sys.version_info.major == 2:
    now = time.time()
    state = 'has reached end of life'
    if now >= 1588291200:  # 2020-05-01 00:00:00 UTC
        state += ' and will receive no further updates'
    tools.stderr(
        'Warning: Python 2.x %s. Sopel 8.0 will drop support for it.' % state)

LOGGER = logging.getLogger(__name__)

ERR_CODE = 1
"""Error code: program exited with an error"""
ERR_CODE_NO_RESTART = 2
"""Error code: program exited with an error and should not be restarted
Exemple #50
0
 def run(self, host, port=6667):
     try:
         self.initiate_connect(host, port)
     except socket.error as e:
         stderr('Connection error: %s' % e)
         self.handle_close()
Exemple #51
0
def setup(bot):
    stderr("[Sopel-BotEvents] Starting Module Events Logging")

    threading.Thread(target=setup_thread, args=(bot, )).start()
Exemple #52
0
import argparse
import os
import platform
import signal
import sys
import time
import traceback

from sopel import bot, logger, tools, __version__
from sopel.config import (Config, _create_config, ConfigurationError,
                          ConfigurationNotFound, DEFAULT_HOMEDIR, _wizard)
from . import utils

if sys.version_info < (2, 7):
    tools.stderr('Error: Requires Python 2.7 or later. Try python2.7 sopel')
    sys.exit(1)
if sys.version_info.major == 2:
    tools.stderr(
        'Warning: Python 2.x is near end of life. Sopel support at that point is TBD.'
    )
if sys.version_info.major == 3 and sys.version_info.minor < 3:
    tools.stderr('Error: When running on Python 3, Python 3.3 is required.')
    sys.exit(1)

ERR_CODE = 1
"""Error code: program exited with an error"""
ERR_CODE_NO_RESTART = 2
"""Error code: program exited with an error and should not be restarted

This error code is used to prevent systemd from restarting the bot when it
def handle_disable(options):
    """Disable Sopel plugins.

    :param options: parsed arguments
    :type options: :class:`argparse.Namespace`
    :return: 0 if everything went fine;
             1 if the plugin doesn't exist,
             or if attempting to disable coretasks (required)
    """
    plugin_names = options.names
    force = options.force
    ensure_remove = options.remove
    settings = utils.load_settings(options)
    usable_plugins = plugins.get_usable_plugins(settings)
    actually_disabled = []

    # coretasks is sacred
    if 'coretasks' in plugin_names:
        tools.stderr('Plugin coretasks cannot be disabled.')
        return 1  # do nothing and return an error code

    unknown_plugins = [
        name
        for name in plugin_names
        if name not in usable_plugins
    ]
    if unknown_plugins:
        display_unknown_plugins(unknown_plugins)
        return 1  # do nothing and return an error code

    # remove from enabled if asked
    if ensure_remove:
        settings.core.enable = [
            name
            for name in settings.core.enable
            if name not in plugin_names
        ]
        settings.save()

    # disable plugin (when needed)
    actually_disabled = tuple(
        name
        for name in plugin_names
        if _handle_disable_plugin(settings, name, force)
    )

    # save if required
    if actually_disabled:
        settings.save()
    else:
        return 0  # nothing to disable or save, but not an error case

    # display plugins actually disabled by the command
    print(utils.get_many_text(
        actually_disabled,
        one='Plugin {item} disabled.',
        two='Plugins {first} and {second} disabled.',
        many='Plugins {left}, and {last} disabled.'
    ))

    return 0
Exemple #54
0
# coding=utf-8
"""
Sopel - An IRC Bot
Copyright 2008, Sean B. Palmer, inamidst.com
Copyright © 2012-2014, Elad Alfassa <*****@*****.**>
Licensed under the Eiffel Forum License 2.

http://sopel.chat
"""
from __future__ import unicode_literals, absolute_import, print_function, division

import sys
from sopel.tools import stderr

if sys.version_info < (2, 7):
    stderr('Error: Requires Python 2.7 or later. Try python2.7 sopel')
    sys.exit(1)
if sys.version_info.major == 3 and sys.version_info.minor < 3:
    stderr('Error: When running on Python 3, Python 3.3 is required.')
    sys.exit(1)

import os
import argparse
import signal

from sopel.__init__ import run, __version__
from sopel.config import Config, _create_config, ConfigurationError, _wizard
import sopel.tools as tools

homedir = os.path.join(os.path.expanduser('~'), '.sopel')
Exemple #55
0
    def setup(self):
        stderr("\nWelcome to Sopel. Loading modules...\n\n")

        modules = sopel.loader.enumerate_modules(self.config)

        error_count = 0
        success_count = 0
        for name in modules:
            path, type_ = modules[name]

            try:
                module, _ = sopel.loader.load_module(name, path, type_)
            except Exception as e:
                error_count = error_count + 1
                filename, lineno = tools.get_raising_file_and_line()
                rel_path = os.path.relpath(filename, os.path.dirname(__file__))
                raising_stmt = "%s:%d" % (rel_path, lineno)
                stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
            else:
                try:
                    if hasattr(module, 'setup'):
                        module.setup(self)
                    relevant_parts = sopel.loader.clean_module(
                        module, self.config)
                except Exception as e:
                    error_count = error_count + 1
                    filename, lineno = tools.get_raising_file_and_line()
                    rel_path = os.path.relpath(filename,
                                               os.path.dirname(__file__))
                    raising_stmt = "%s:%d" % (rel_path, lineno)
                    stderr("Error in %s setup procedure: %s (%s)" %
                           (name, e, raising_stmt))
                else:
                    self.register(*relevant_parts)
                    success_count += 1

        if len(modules) > 1:  # coretasks is counted
            stderr('\n\nRegistered %d modules,' % (success_count - 1))
            stderr('%d modules failed to load\n\n' % error_count)
        else:
            stderr("Warning: Couldn't load any modules")
Exemple #56
0
    def setup(self):
        """Set up the Sopel instance."""
        load_success = 0
        load_error = 0
        load_disabled = 0

        stderr("Welcome to Sopel. Loading modules...")
        usable_plugins = plugins.get_usable_plugins(self.config)
        for name, info in usable_plugins.items():
            plugin, is_enabled = info
            if not is_enabled:
                load_disabled = load_disabled + 1
                continue

            try:
                plugin.load()
            except Exception as e:
                load_error = load_error + 1
                filename, lineno = tools.get_raising_file_and_line()
                rel_path = os.path.relpath(filename, os.path.dirname(__file__))
                raising_stmt = "%s:%d" % (rel_path, lineno)
                stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
            else:
                try:
                    if plugin.has_setup():
                        plugin.setup(self)
                    plugin.register(self)
                except Exception as e:
                    load_error = load_error + 1
                    filename, lineno = tools.get_raising_file_and_line()
                    rel_path = os.path.relpath(filename,
                                               os.path.dirname(__file__))
                    raising_stmt = "%s:%d" % (rel_path, lineno)
                    stderr("Error in %s setup procedure: %s (%s)" %
                           (name, e, raising_stmt))
                else:
                    load_success = load_success + 1
                    print('Loaded: %s' % name)

        total = sum([load_success, load_error, load_disabled])
        if total and load_success:
            stderr('Registered %d modules' % (load_success - 1))
            stderr('%d modules failed to load' % load_error)
            stderr('%d modules disabled' % load_disabled)
        else:
            stderr("Warning: Couldn't load any modules")
Exemple #57
0
import logging
import os
import platform
import signal
import sys
import time

from sopel import __version__, bot, config, logger, tools
from . import utils

# This is in case someone somehow manages to install Sopel on an old version
# of pip (<9.0.0), which doesn't know about `python_requires`, or tries to run
# from source on an unsupported version of Python.
if sys.version_info < (2, 7) or (sys.version_info.major >= 3
                                 and sys.version_info < (3, 3)):
    tools.stderr('Error: Sopel requires Python 2.7+ or 3.3+.')
    sys.exit(1)
if sys.version_info.major == 2:
    tools.stderr(
        'Warning: Python 2.x has reached end of life and will receive '
        'no further updates. Sopel 8.0 will drop support for it.')

LOGGER = logging.getLogger(__name__)

ERR_CODE = 1
"""Error code: program exited with an error"""
ERR_CODE_NO_RESTART = 2
"""Error code: program exited with an error and should not be restarted

This error code is used to prevent systemd from restarting the bot when it
encounters such an error case.
Exemple #58
0
def run(config, pid_file, daemon=False):
    import sopel.bot as bot
    import sopel.logger
    from sopel.tools import stderr
    delay = 20
    # Inject ca_certs from config to web for SSL validation of web requests
    if not config.core.ca_certs:
        stderr('Could not open CA certificates file. SSL will not '
               'work properly.')

    def signal_handler(sig, frame):
        if sig == signal.SIGUSR1 or sig == signal.SIGTERM or sig == signal.SIGINT:
            stderr('Got quit signal, shutting down.')
            p.quit('Closing')
        elif sig == signal.SIGUSR2 or sig == signal.SIGILL:
            stderr('Got restart signal.')
            p.restart('Restarting')

    while True:
        try:
            p = bot.Sopel(config, daemon=daemon)
            if hasattr(signal, 'SIGUSR1'):
                signal.signal(signal.SIGUSR1, signal_handler)
            if hasattr(signal, 'SIGTERM'):
                signal.signal(signal.SIGTERM, signal_handler)
            if hasattr(signal, 'SIGINT'):
                signal.signal(signal.SIGINT, signal_handler)
            if hasattr(signal, 'SIGUSR2'):
                signal.signal(signal.SIGUSR2, signal_handler)
            if hasattr(signal, 'SIGILL'):
                signal.signal(signal.SIGILL, signal_handler)
            sopel.logger.setup_logging(p)
            p.run(config.core.host, int(config.core.port))
        except KeyboardInterrupt:
            break
        except Exception:  # TODO: Be specific
            trace = traceback.format_exc()
            try:
                stderr(trace)
            except Exception:  # TODO: Be specific
                pass
            logfile = open(os.path.join(config.core.logdir, 'exceptions.log'),
                           'a')
            logfile.write('Critical exception in core')
            logfile.write(trace)
            logfile.write('----------------------------------------\n\n')
            logfile.close()
            # TODO: This should be handled in run_script
            # All we should need here is a return value, but replacing the
            # os._exit() call below (at the end) broke ^C.
            # This one is much harder to test, so until that one's sorted it
            # isn't worth the risk of trying to remove this one.
            os.unlink(pid_file)
            os._exit(1)

        if not isinstance(delay, int):
            break
        if p.wantsrestart:
            return -1
        if p.hasquit:
            break
        stderr('Warning: Disconnected. Reconnecting in %s seconds...' % delay)
        time.sleep(delay)
    # TODO: This should be handled in run_script
    # All we should need here is a return value, but making this
    # a return makes Sopel hang on ^C after it says "Closed!"
    os.unlink(pid_file)
    os._exit(0)
Exemple #59
0
def setup(bot):
    stderr("[SpiceBot_Update] Initial Setup processing...")
    bot.config.define_section("SpiceBot_Update",
                              SpiceBot_Update_MainSection,
                              validate=False)