Ejemplo n.º 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
Ejemplo n.º 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()
Ejemplo n.º 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)
Ejemplo n.º 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')
Ejemplo n.º 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))
Ejemplo n.º 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)
Ejemplo n.º 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()
Ejemplo n.º 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
Ejemplo n.º 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
Ejemplo n.º 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)
Ejemplo n.º 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")
Ejemplo n.º 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))
Ejemplo n.º 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))
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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()
Ejemplo n.º 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
Ejemplo n.º 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
Ejemplo n.º 22
0
Archivo: bot.py Proyecto: yoosi/sopel
    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))
Ejemplo n.º 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
Ejemplo n.º 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)
Ejemplo n.º 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
Ejemplo n.º 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()
Ejemplo n.º 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()
Ejemplo n.º 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 = []
Ejemplo n.º 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))
Ejemplo n.º 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
Ejemplo n.º 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
Ejemplo n.º 32
0
Archivo: bot.py Proyecto: nsnw/sopel
    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 = []
Ejemplo n.º 33
0
def startup_reconnect(bot, trigger):

    # Startup
    stderr("[Sopel-startupmonologue] " + bot.nick + " has reconnected.")
    bot.osd(" has reconnected.", bot.channels.keys(), 'ACTION')
Ejemplo n.º 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
Ejemplo n.º 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)
Ejemplo n.º 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
Ejemplo n.º 37
0
Archivo: irc.py Proyecto: maninax/sopel
    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()
Ejemplo n.º 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)
Ejemplo n.º 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')
Ejemplo n.º 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
Ejemplo n.º 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)
Ejemplo n.º 45
0
def setup(bot):
    stderr("[SpiceBot_DatabaseCache] Setting up Database Cache.")
    bot.memory['SpiceBot_DatabaseCache'] = dict()
Ejemplo n.º 46
0
Archivo: run.py Proyecto: kwaaak/sopel
"""
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__)
Ejemplo n.º 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)
Ejemplo n.º 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)
Ejemplo n.º 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
Ejemplo n.º 50
0
Archivo: irc.py Proyecto: maninax/sopel
 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()
Ejemplo n.º 51
0
def setup(bot):
    stderr("[Sopel-BotEvents] Starting Module Events Logging")

    threading.Thread(target=setup_thread, args=(bot, )).start()
Ejemplo n.º 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
Ejemplo n.º 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')
Ejemplo n.º 55
0
Archivo: bot.py Proyecto: zeamp/sopel
    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")
Ejemplo n.º 56
0
Archivo: bot.py Proyecto: nsnw/sopel
    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")
Ejemplo n.º 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.
Ejemplo n.º 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)
Ejemplo n.º 59
0
def setup(bot):
    stderr("[SpiceBot_Update] Initial Setup processing...")
    bot.config.define_section("SpiceBot_Update",
                              SpiceBot_Update_MainSection,
                              validate=False)