示例#1
0
文件: auth.py 项目: aobolensk/walbot
 def _resetpass(self, update: Update, context: CallbackContext):
     log_command(update)
     if not check_auth(update):
         return
     bc.config.telegram["passphrase"] = uuid.uuid4().hex
     log.warning("New passphrase: " + bc.config.telegram["passphrase"])
     reply(update, 'Passphrase has been reset!')
示例#2
0
def optional_numba_jit(func) -> Any:
    try:
        numba = importlib.import_module("numba")
        return numba.njit(fastmath=True)(func)
    except ImportError:
        log.warning(f"Function {func.__name__} is missing numba package for better performance!")
        return func
示例#3
0
    def _run(self, args) -> None:
        while True:
            if bc is None or bc.secret_config is None:
                time.sleep(2)
            else:
                break
        if bc.secret_config.telegram["token"] is None:
            log.warning("Telegram backend is not configured. Missing token in secret config")
            return
        log.info("Starting Telegram instance...")
        updater = Updater(bc.secret_config.telegram["token"])
        builtin_cmds = BuiltinCommands()
        builtin_cmds.add_handlers(updater.dispatcher)
        reminder_cmds = ReminderCommands()
        reminder_cmds.add_handlers(updater.dispatcher)
        auth_cmds = AuthCommands()
        auth_cmds.add_handlers(updater.dispatcher)
        updater.dispatcher.add_handler(
            MessageHandler(
                Filters.text & ~Filters.command & Filters.entity(MessageEntity.MENTION), self._handle_mentions))
        updater.dispatcher.add_handler(
            MessageHandler(
                Filters.text & ~Filters.command & ~Filters.entity(MessageEntity.MENTION), self._handle_messages))

        log.info("Telegram instance is started!")
        updater.start_polling()
        while True:
            time.sleep(1)
            if self._is_stopping:
                log.info("Stopping Telegram instance...")
                updater.stop()
                log.info("Telegram instance is stopped!")
                break
示例#4
0
def check_updates(context: AutoUpdateContext) -> bool:
    """Function that performs updates check. It is called periodically"""
    old_sha = context.repo.head.object.hexsha
    try:
        context.repo.remotes.origin.fetch()
    except Exception as e:
        return log.error(
            f"Fetch failed: {e}. Skipping this cycle, will try to update on the next one"
        )
    new_sha = context.repo.remotes.origin.refs['master'].object.name_rev.split(
    )[0]
    log.debug(f"{old_sha} {new_sha}")
    if old_sha == new_sha:
        return log.debug("No new updates")
    bot_cache = importlib.import_module("src.bot_cache").BotCache(True).parse()
    if bot_cache is None:
        return log.warning(
            "Could not read bot cache. Skipping this cycle, will try to update on the next one"
        )
    if "do_not_update" not in bot_cache.keys():
        return log.warning(
            "Could not find 'do_not_update' field in bot cache. "
            "Skipping this cycle, will try to update on the next one")
    if bot_cache["do_not_update"]:
        return log.debug(
            "Automatic update is not permitted. Skipping this cycle, will try to update on the next one"
        )
    context.repo.git.reset("--hard")
    try:
        g = git.cmd.Git(os.getcwd())
        g.pull()
    except git.exc.GitCommandError as e:
        if "Connection timed out" in e.stderr or "Could not resolve host" in e.stderr:
            log.warning(f"{e.command}: {e.stderr}")
        else:
            raise e
    subprocess.call(f"{sys.executable} -m pip install -r requirements.txt",
                    shell=True)
    minibot_response = "WalBot automatic update is in progress. Please, wait..."
    subprocess.call(
        f"{sys.executable} walbot.py startmini --message '{minibot_response}' --nohup &",
        shell=True)
    subprocess.call(f"{sys.executable} walbot.py stop", shell=True)
    if context.check_versions():
        subprocess.call(f"{sys.executable} walbot.py patch", shell=True)
    subprocess.call(f"{sys.executable} walbot.py start --fast_start --nohup &",
                    shell=True)
    while True:
        time.sleep(1)
        bot_cache = importlib.import_module("src.bot_cache").BotCache(
            True).parse()
        if bot_cache is not None and bot_cache["ready"]:
            subprocess.call(f"{sys.executable} walbot.py stopmini", shell=True)
            log.info("Bot is fully loaded. MiniWalBot is stopped.")
            break
        log.debug("Bot is not fully loaded yet. Waiting...")
    return True
示例#5
0
 def check_version(name, actual, expected, solutions=None, fatal=True):
     if actual == expected:
         return True
     if not fatal:
         log.warning(
             f"{name} versions mismatch. Expected: {expected}, but actual: {actual}"
         )
     else:
         log.error(
             f"{name} versions mismatch. Expected: {expected}, but actual: {actual}"
         )
     if solutions:
         log.info("Possible solutions:")
         for solution in solutions:
             log.info(f" - {solution}")
     return not fatal
示例#6
0
 def parse(self) -> Optional[Dict]:
     if not os.path.exists(self.path):
         return
     cache = None
     for _ in range(10):
         try:
             with open(self.path, 'r') as f:
                 cache = json.load(f)
             if cache is not None:
                 if "pid" not in cache or not psutil.pid_exists(int(cache["pid"])):
                     log.warning("Could validate pid from .bot_cache")
                     os.remove(self.path)
                     return
                 return cache
         except json.decoder.JSONDecodeError:
             time.sleep(0.5)
示例#7
0
def configure_iptables(init, args=None):
    """Sets iptables rules and ip forwarding according to KISS configuration."""

    if platform == "linux":  #platform is from scapy
        os.system("iptables --flush")
        if init:
            arps_activated = (args.A_ENABLED or args.N_PASSIVE_ARPS_EVERYONE)
            if arps_activated:
                os.system("echo 1 > /proc/sys/net/ipv4/ip_forward")
            if args.D_ENABLED:
                os.system("iptables -A FORWARD -p udp --dport 53 -j DROP")
                os.system(
                    "iptables -A FORWARD -p tcp --dport 80 -m string --string 'GET' --algo bm -j DROP"
                )
                #os.system("iptables -A FORWARD -p tcp --dport 80 -m string --string 'POST' --algo bm -m string --string 'GET' --algo bm -j DROP")

            if args.J_ENABLED:
                os.system(
                    "iptables -A FORWARD -p tcp --sport 80 -m string --string 'ype: text/html' --algo bm -j DROP"
                )

            log.info("CONFIG", "Iptables have been established.")
        else:
            os.system("echo 0 > /proc/sys/net/ipv4/ip_forward")
            log.info("CONFIG", "Iptables have been cleared.")
    else:
        if init:
            arps_activated = (args.A_ENABLED or args.N_PASSIVE_ARPS_EVERYONE)
            if arps_activated:
                log.warning(
                    None,
                    "Make sure Routing and Remote Access Service is activated."
                )
            if args.D_ENABLED:
                log.warning(
                    None,
                    "Windows is not supported due to the lack of iptables. DNS Spoofing will probably not work correctly."
                )
示例#8
0
 async def wrapped(*args, **kwargs):
     try:
         return await func(*args, **kwargs)
     except Exception as e:
         try:
             bot_info = bc.info.get_full_info(2)
         except Exception:
             log.warning("Failed to get bot info to attach to e-mail",
                         exc_info=True)
             bot_info = "ERROR: Failed to retrieve details, please refer to log file"
         if bc.secret_config.admin_email_list:
             mail = Mail(bc.secret_config)
             mail.send(
                 bc.secret_config.admin_email_list,
                 f"WalBot (instance: {bc.instance_name}) {func.__name__} failed",
                 f"{func.__name__} failed:\n"
                 f"{e}\n"
                 "\n"
                 f"Backtrace:\n"
                 f"{traceback.format_exc()}\n"
                 f"Details:\n"
                 f"{bot_info}")
         log.error(f"{func.__name__} failed", exc_info=True)
示例#9
0
    def check_args(self):
        """Checks missing, empty and illogical attributes, and disables a
        module if its config is not correct, logging a warning or an error.
        """
        if "D34D" in self.__dict__.values():
            #si alguno de los atributos de la clase args contiene D34D, es porque no se ha encontrado alguno de los args
            log.error(None,
                      "Missing conf parameters in config file. Leaving...")
            sys.exit()

        #PARTICULARES
        #NET
        if self.N_ENABLED and not (self.N_ACTIVE or self.N_PASSIVE):
            log.warning(
                None,
                "Net Analyzer is enabled, but mode was not chosen. Please enable active mode, passive mode or both. Disabling Net Analyzer..."
            )
            self.N_ENABLED = False
        if self.N_ENABLED and not packet_utilities.is_it_an_ip(
                self.N_GATEWAY_IP):
            log.warning(
                None,
                "Gateway IP Address of module Net Analyzer is not valid. Disabling Net Analyzer..."
            )
            self.N_ENABLED = False

        #SNIFF
        if self.S_ENABLED and self.S_ATTRIBUTES != "*":
            try:
                f = open(self.S_ATTRIBUTES)
                f.close()
            except FileNotFoundError:
                log.warning(None, "File", self.S_ATTRIBUTES,
                            "could not be found. Disabling Sniffer...")
                self.S_ENABLED = False

        #ARPS
        if self.A_ENABLED and (not self.A_TARGET_IP or not self.A_GATEWAY_IP
                               or not self.A_INTERVAL):
            #si algun valor esta vacio... solo TIME_SECS y DISCONNECT pueden estar vacios o ser None/False
            log.warning(
                None,
                "Missing ARPS parameters in config file. Disabling ARPS...")
            self.A_ENABLED = False
        if self.A_ENABLED and self.A_INTERVAL == 0:
            log.warning(None, "ARPS interval can't be 0. Setting to 1...")
            self.A_INTERVAL = 1
        if self.A_ENABLED and not packet_utilities.is_it_an_ip(
                self.A_GATEWAY_IP):
            log.warning(
                None,
                "Gateway IP Address of module ARP Spoofing is not valid. Disabling ARP Spoofing..."
            )
            self.A_ENABLED = False
        if self.A_ENABLED and (not packet_utilities.is_it_an_ip(
                self.A_TARGET_IP) and self.A_TARGET_IP != "everyone"):
            log.warning(
                None,
                "Target IP Address of module ARP Spoofing is not valid. Disabling ARP Spoofing..."
            )
            self.A_ENABLED = False

        #DNS
        if self.D_ENABLED and not self.D_FILE:
            log.warning(
                None,
                "Missing DNS parameters in config file. Disabling DNS Spoofer..."
            )
            self.D_ENABLED = False
        if self.D_ENABLED:
            try:
                f = open(self.D_FILE)
                f.close()
            except FileNotFoundError:
                log.warning(None, "File", self.D_FILE,
                            "could not be found. Disabling DNS Spoofer...")
                self.D_ENABLED = False

        #JS
        if self.J_ENABLED and (not self.J_FILE or not self.J_TARGET_IP):
            log.warning(
                None,
                "Missing JS parameters in config file. Disabling JS Injecter..."
            )
            self.J_ENABLED = False

        #NO PARTICULARES
        if self.D_ENABLED and not self.A_ENABLED:
            #dns sin arps esta feo
            log.warning(
                None,
                "DNS Spoofing can't be done without ARP Spoofing. Please enable ARPS. Disabling DNS..."
            )
            self.D_ENABLED = False

        if self.S_ENABLED and not self.A_ENABLED:
            log.warning(
                None,
                "ARP Spoofing is disabled! Only packets from the local machine will be sniffed. You can activate it in the config file."
            )

        if self.J_ENABLED and not self.A_ENABLED:
            #JS sin arps esta feo
            log.warning(
                None,
                "JS Injecting can't be done without ARP Spoofing. Please enable ARPS. Disabling JS..."
            )
            self.J_ENABLED = False

        if not self.N_ENABLED and not self.S_ENABLED and not self.A_ENABLED and not self.D_ENABLED and not self.U_ENABLED and not self.J_ENABLED:
            log.error(None, "No module is enabled. Leaving...")
            sys.exit()

        log.info("CONFIG", "Config file is OK!")
示例#10
0
def send_note(entity, argument, command_name, note_type, note_singular,
              note_plural, note_class, grammar_genre):
    if not entity:
        log.bug("entity non è un parametro valido: %r" % entity)
        return False

    # argument può essere una stringa vuota

    if not command_name:
        log.bug("command_name non è un parametro valido: %r" % command_name)
        return False

    if not note_type:
        log.bug("note_type non è un parametro valido: %r" % note_type)
        return False

    if not note_singular:
        log.bug("note_singular non è un parametro valido: %r" % note_singular)
        return False

    if not note_class:
        log.bug("note_class non è un parametro valido: %r" % note_class)
        return False

    if not grammar_genre:
        log.bug("grammar_genre non è un parametro valido: %r" % grammar_genre)
        return False

    # -------------------------------------------------------------------------

    if not argument:
        syntax = get_command_syntax(entity, command_name)
        entity.send_output(syntax, break_line=False)
        return False

    if not entity.IS_PLAYER:
        entity.send_output(
            "Solo i giocatori possono inviare %s." %
            (add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE,
                         GRAMMAR.SINGULAR)))
        return False

    if config.max_account_typos == 0:
        entity.send_output(
            "L'invio %s è stata disabilitata." %
            (add_article(note_plural, grammar_genre, GRAMMAR.INDETERMINATE,
                         GRAMMAR.PLURAL)))
        return False

    # Non dovrebbe mai capitare, ma non si sa mai, in teoria comunque non è un baco
    if not entity.account:
        entity.send_output(
            "La sessione del tuo account è scaduta, devi riaccedere nuovamente al sito per poter inviare %s"
            % (add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE,
                           GRAMMAR.SINGULAR)))
        return False

    # Anche questa non dovrebbe mai capitare
    if not entity.location:
        entity.send_output(
            "Ti è impossibile inviare %s da questo luogo." %
            (add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE,
                         GRAMMAR.SINGULAR)))
        log.bug(
            "entity.location non è valido (%r) per entity %s mentre stava cercando di inviare una nota %s"
            % (entity.location, entity.code, note_type))
        return False

    # Crea il codice per la nota
    code_to_check = entity.account.name + "#"
    sended_notes = getattr(entity.account, "sended_%ss" % note_type)
    code = code_to_check + str(sended_notes)

    # Se il codice già esiste nel database probabilmente c'è una dicrepanza
    # tra il dato di account e quelli delle note quindi il codice si
    # incrementa fino a trovare un numero libero
    counter = 1
    while code in database[note_type + "s"]:
        code = code_to_check + str(sended_notes + counter)
        setattr(entity.account, "sended_%ss" % note_type,
                sended_notes + counter + 1)
        counter += 1

    who = entity.code
    if entity.IS_PLAYER and entity.account:
        who += " dell'account %s" % entity.account.name
    if entity.location.IS_ROOM:
        where = "%s (%r)" % (entity.location.code,
                             entity.location.get_destination())
    else:
        where = "%s in %r  " % (entity.location.get_name(),
                                entity.get_in_room.get_destination())
    when = "%s (%s %s %s %s)" % (datetime.datetime.now(), calendar.minute,
                                 calendar.hour, calendar.day, calendar.month)

    note = note_class(code, who, where, when, argument)
    if not note:
        log.bug("note non è valido (%r) per la tipologia %s" %
                (note, note_type))
        entity.send_output(
            "Impossibile segnalare un%s nuov%s %s." %
            ("" if grammar_genre == GRAMMAR.MASCULINE else "a", "o"
             if grammar_genre == GRAMMAR.MASCULINE else "a", note_singular))
        return False

    # Evitare di inviare una nota subito dopo averne inviata un'altra,
    # così da evitare eventuali spammer
    last_note_sended_at = getattr(entity.account,
                                  "last_%s_sended_at" % note_type)
    if (datetime.datetime.now() - last_note_sended_at).days <= 0:
        # (TD) Python 2.7, così non servirà più il check su days
        #total_seconds = (datetime.datetime.now() - last_note_sended_at).total_seconds()
        total_seconds = (datetime.datetime.now() - last_note_sended_at).seconds
        if total_seconds < config.sending_interval:
            remaining_seconds = config.sending_interval - total_seconds
            random_id = random.random()
            entity.send_output(
                '''Potrai inviare %s solo tra <span id="%s">%d %s</span><script>parent.countdown("%s");</script>'''
                %
                (add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE,
                             GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE), random_id,
                 remaining_seconds, "secondo"
                 if remaining_seconds == 1 else "secondi", random_id))
            return False

    # Inserisce un tetto massimo di note ancora aperte inviabili contando se
    # effettivamente le note dell'account ancora aperte sono così tante
    counter = 0
    for note_code in database[note_type + "s"]:
        if note_code.startswith(code_to_check):
            counter += 1
    if counter >= getattr(config, "max_account_%ss" % note_type):
        entity.send_output(
            "Hai raggiunto il massimo di %s attualmente segnalabili, riprova tra qualche minuto."
            % (note_plural, config.game_name))
        log.monitor(
            "%s ha raggiunto il numero massimo di %s inviabili, controllare e chiudere quelle obsolete cosicché possa inviarne delle altre.",
            (entity.code, note_plural))
        return False

    # Controlla che le altre note non abbiano lo stesso testo, altrimenti ci
    # troviamo davanti ad un possibile spammer, il sistema quindi fa finta
    # di salvarsi la nota e intanto segnala agli Amministratori lo spammer
    for other_note in database[note_type + "s"].itervalues():
        if other_note.text == argument and other_note.code.startswith(
                code_to_check):
            entity.send_output("%s è stato salvato. Grazie!" % (add_article(
                note_singular, grammar_genre, GRAMMAR.DETERMINATE,
                GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE).capitalize()))
            log.warning(
                "Qualcuno per sbaglio invia due volte la stessa nota %s. Se continua esageratemente è un possibile spammer (%s)"
                % (note_singular, entity.get_conn().get_id()))
            return False

    database[note_type + "s"][note.code] = note

    # Le note sono uno di quei dati slegati dal gioco che possono essere
    # scritti subito sul disco vista la relativa rarità del suo invio e quindi
    # la bassa probabilità che vada ad inficiare sulle prestazioni globali
    note_path = "data/%ss/%s.dat" % (note_type, note.code)
    try:
        note_file = open(note_path, "w")
    except IOError:
        entity.send_output(
            "Per un errore interno al server è impossibile salvare %s." %
            (add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE,
                         GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE)))
        log.bug("Impossibile aprire il file %s in scrittura" % note_path)
        return False
    fwrite(note_file, note)
    note_file.close()

    # Poiché è stato salvata la nota bisogna anche salvare l'account che
    # contiene il numero di note inviate da sempre incrementato giusto qui
    # (TT) Bisogna fare attenzione al salvataggio facile dell'account,
    # potrebbero in futuro esservi informazioni duplicate o erroneamente
    # incrementate (dopo un crash) a seconda delle dipendenze tra i dati
    # nell'account ed altre tipologie di dati che non verrebbero salvate
    # se non alla chisura del Mud
    setattr(entity.account, "sended_%ss" % note_type, sended_notes + 1)
    setattr(entity.account, "last_%s_sended_at" % note_type,
            datetime.datetime.now())
    account_path = "persistence/accounts/%s.dat" % entity.account.name
    try:
        account_file = open(account_path, "w")
    except IOError:
        entity.send_output(
            "Per un errore interno al server è impossibile salvare %s." %
            (add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE,
                         GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE)))
        log.bug("Impossibile aprire il file %s in scrittura" % account_path)
        return False
    fwrite(account_file, entity.account)
    account_file.close()

    entity.send_output("Hai appena segnalato %s%d° %s. Grazie!" % (get_article(
        note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR,
        GRAMMAR.POSSESSIVE), sended_notes + 1, note_singular))

    # Invia la mail
    subject = "Invio %s" % note_singular
    text = "L'account %s ha appena segnalato %s%d° %s." % (
        entity.account.name,
        get_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE,
                    GRAMMAR.SINGULAR), sended_notes + 1, note_singular)
    text += "\nWho: %s" % note.who
    text += "\nWhere: %s" % note.where
    text += "\nWhen: %s" % note.when
    text += "\nText: %s" % note.text

    mail.send_to_admins(subject, text, show_players=False)
    return True
示例#11
0
 def start(self, args, main_bot=True):
     # Check whether bot is already running
     bot_cache = BotCache(main_bot).parse()
     if bot_cache is not None:
         pid = bot_cache["pid"]
         if pid is not None and psutil.pid_exists(pid):
             return log.error("Bot is already running!")
     # Some variable initializations
     config = None
     secret_config = None
     bc.restart_flag = False
     bc.args = args
     # Handle --nohup flag
     if sys.platform in ("linux", "darwin") and args.nohup:
         fd = os.open(const.NOHUP_FILE_PATH,
                      os.O_WRONLY | os.O_CREAT | os.O_APPEND)
         log.info(f"Output is redirected to {const.NOHUP_FILE_PATH}")
         os.dup2(fd, sys.stdout.fileno())
         os.dup2(sys.stdout.fileno(), sys.stderr.fileno())
         os.close(fd)
         signal.signal(signal.SIGHUP, signal.SIG_IGN)
     # Selecting YAML parser
     bc.yaml_loader, bc.yaml_dumper = Util.get_yaml(verbose=True)
     # Saving application pd in order to safely stop it later
     BotCache(main_bot).dump_to_file()
     # Executing patch tool if it is necessary
     if args.patch:
         cmd = f"'{sys.executable}' '{os.path.dirname(__file__) + '/../tools/patch.py'}' all"
         log.info("Executing patch tool: " + cmd)
         subprocess.call(cmd)
     # Read configuration files
     config = Util.read_config_file(const.CONFIG_PATH)
     if config is None:
         config = Config()
     secret_config = Util.read_config_file(const.SECRET_CONFIG_PATH)
     if secret_config is None:
         secret_config = SecretConfig()
     bc.markov = Util.read_config_file(const.MARKOV_PATH)
     if bc.markov is None and os.path.isdir("backup"):
         # Check available backups
         markov_backups = sorted([
             x for x in os.listdir("backup")
             if x.startswith("markov_") and x.endswith(".zip")
         ])
         if markov_backups:
             # Restore Markov model from backup
             with zipfile.ZipFile("backup/" + markov_backups[-1],
                                  'r') as zip_ref:
                 zip_ref.extractall(".")
             log.info(
                 f"Restoring Markov model from backup/{markov_backups[-1]}")
             shutil.move(markov_backups[-1][:-4], "markov.yaml")
             bc.markov = Util.read_config_file(const.MARKOV_PATH)
             if bc.markov is None:
                 bc.markov = Markov()
                 log.warning(
                     "Failed to restore Markov model from backup. Creating new Markov model..."
                 )
     if bc.markov is None:
         bc.markov = Markov()
         log.info("Created empty Markov model")
     # Check config versions
     ok = True
     ok &= Util.check_version(
         "discord.py",
         discord.__version__,
         const.DISCORD_LIB_VERSION,
         solutions=[
             "execute: python -m pip install -r requirements.txt",
         ])
     ok &= Util.check_version(
         "Config",
         config.version,
         const.CONFIG_VERSION,
         solutions=[
             "run patch tool",
             "remove config.yaml (settings will be lost!)",
         ])
     ok &= Util.check_version(
         "Markov config",
         bc.markov.version,
         const.MARKOV_CONFIG_VERSION,
         solutions=[
             "run patch tool",
             "remove markov.yaml (Markov model will be lost!)",
         ])
     ok &= Util.check_version(
         "Secret config",
         secret_config.version,
         const.SECRET_CONFIG_VERSION,
         solutions=[
             "run patch tool",
             "remove secret.yaml (your Discord authentication token will be lost!)",
         ])
     if main_bot and not ok:
         sys.exit(const.ExitStatus.CONFIG_FILE_ERROR)
     config.commands.update()
     # Checking authentication token
     if secret_config.token is None:
         secret_config = SecretConfig()
         if not FF.is_enabled("WALBOT_FEATURE_NEW_CONFIG"):
             secret_config.token = input("Enter your token: ")
     # Constructing bot instance
     if main_bot:
         intents = discord.Intents.all()
         walbot = WalBot(args.name, config, secret_config, intents=intents)
     else:
         walbot = importlib.import_module("src.minibot").MiniWalBot(
             args.name, config, secret_config, args.message)
     # Starting the bot
     try:
         walbot.run(secret_config.token)
     except discord.errors.PrivilegedIntentsRequired:
         log.error(
             "Privileged Gateway Intents are not enabled! Shutting down the bot..."
         )
     # After stopping the bot
     log.info("Bot is disconnected!")
     if main_bot:
         config.save(const.CONFIG_PATH,
                     const.MARKOV_PATH,
                     const.SECRET_CONFIG_PATH,
                     wait=True)
     BotCache(main_bot).remove()
     if bc.restart_flag:
         cmd = f"'{sys.executable}' '{os.path.dirname(os.path.dirname(__file__)) + '/walbot.py'}' start"
         log.info("Calling: " + cmd)
         if sys.platform in ("linux", "darwin"):
             fork = os.fork()
             if fork == 0:
                 subprocess.call(cmd)
             elif fork > 0:
                 log.info("Stopping current instance of the bot")
                 sys.exit(const.ExitStatus.NO_ERROR)
         else:
             subprocess.call(cmd)
示例#12
0
    def markov_yaml(self, config):
        """Update markov.yaml"""
        if config.version == "0.0.1":
            config.__dict__["min_chars"] = 1
            config.__dict__["min_words"] = 1
            self._bump_version(config, "0.0.2")
        if config.version == "0.0.2":
            config.__dict__["chains_generated"] = 0
            self._bump_version(config, "0.0.3")
        if config.version == "0.0.3":
            config.__dict__["max_chars"] = 2000
            config.__dict__["max_words"] = 500
            self._bump_version(config, "0.0.4")
        if config.version == "0.0.4":
            config.model[""].__dict__["word"] = None
            self._bump_version(config, "0.0.5")
        if config.version == "0.0.5":
            for i, _ in enumerate(config.filters):
                config.__dict__["filters"][i] = re.compile(config.filters[i].pattern, re.DOTALL)
            self._bump_version(config, "0.0.6")
        if config.version == "0.0.6":
            config.__dict__["ignored_prefixes"] = dict()
            self._bump_version(config, "0.0.7")
        if config.version == "0.0.7":
            if FF.is_enabled("WALBOT_FEATURE_NEW_CONFIG") == "1":
                db = WalbotDatabase()

                def preprocess_key(key: str):
                    return key.replace("$", "<__markov_dollar>").replace(".", "<__markov_dot>")

                markov_model = dict()
                for key, value in config.model.items():
                    if key is None or key == "":
                        key = "__markov_null"
                    next_list = dict()
                    for k, v in value.next.items():
                        if k is None:
                            k = "__markov_terminate"
                        next_list[preprocess_key(k)] = v
                    markov_model[preprocess_key(key)] = {
                        "word": value.word,
                        "next": next_list,
                        "total_next": value.total_next,
                        "type": value.type,
                    }
                markov_ignored_prefixes = dict()
                for key, value in config.ignored_prefixes.items():
                    markov_ignored_prefixes[str(key)] = value
                db.markov.insert({
                    "chains_generated": config.chains_generated,
                    "end_node": {
                        "word": None,
                        "next": {
                            "__markov_null": 0,
                        },
                        "total_next": 0,
                        "type": 2,
                    },
                    "filters": config.filters,
                    "ignored_prefixes": markov_ignored_prefixes,
                    "max_chars": config.max_chars,
                    "max_words": config.max_words,
                    "min_chars": config.min_chars,
                    "min_words": config.min_words,
                    "model": markov_model,
                    "version": "0.1.0",
                })
                self._bump_version(config, "0.1.0")
                log.warning("Markov model has been moved to MongoDB!")
            else:
                log.info(f"Version of {self.config_name} is up to date!")
        if config.version == "0.1.0":
            log.info(f"Version of {self.config_name} is up to date!")
        else:
            log.error(f"Unknown version {config.version} for {self.config_name}!")
示例#13
0
文件: note.py 项目: Carlovan/aaritmud
def send_note(entity, argument, command_name, note_type, note_singular, note_plural, note_class, grammar_genre):
    if not entity:
        log.bug("entity non è un parametro valido: %r" % entity)
        return False

    # argument può essere una stringa vuota

    if not command_name:
        log.bug("command_name non è un parametro valido: %r" % command_name)
        return False

    if not note_type:
        log.bug("note_type non è un parametro valido: %r" % note_type)
        return False

    if not note_singular:
        log.bug("note_singular non è un parametro valido: %r" % note_singular)
        return False

    if not note_class:
        log.bug("note_class non è un parametro valido: %r" % note_class)
        return False

    if not grammar_genre:
        log.bug("grammar_genre non è un parametro valido: %r" % grammar_genre)
        return False

    # -------------------------------------------------------------------------

    if not argument:
        syntax = get_command_syntax(entity, command_name)
        entity.send_output(syntax, break_line=False)
        return False

    if not entity.IS_PLAYER:
        entity.send_output("Solo i giocatori possono inviare %s." % (
            add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR)))
        return False

    if config.max_account_typos == 0:
        entity.send_output("L'invio %s è stata disabilitata." % (
            add_article(note_plural, grammar_genre, GRAMMAR.INDETERMINATE, GRAMMAR.PLURAL)))
        return False

    # Non dovrebbe mai capitare, ma non si sa mai, in teoria comunque non è un baco
    if not entity.account:
        entity.send_output("La sessione del tuo account è scaduta, devi riaccedere nuovamente al sito per poter inviare %s" % (
            add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR)))
        return False

    # Anche questa non dovrebbe mai capitare
    if not entity.location:
        entity.send_output("Ti è impossibile inviare %s da questo luogo." % (
            add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR)))
        log.bug("entity.location non è valido (%r) per entity %s mentre stava cercando di inviare una nota %s" % (
            entity.location, entity.code, note_type))
        return False

    # Crea il codice per la nota
    code_to_check = entity.account.name + "#"
    sended_notes = getattr(entity.account, "sended_%ss" % note_type)
    code = code_to_check + str(sended_notes)

    # Se il codice già esiste nel database probabilmente c'è una dicrepanza
    # tra il dato di account e quelli delle note quindi il codice si
    # incrementa fino a trovare un numero libero
    counter = 1
    while code in database[note_type + "s"]:
        code = code_to_check + str(sended_notes + counter)
        setattr(entity.account, "sended_%ss" % note_type, sended_notes + counter + 1)
        counter += 1

    who = entity.code
    if entity.IS_PLAYER and entity.account:
        who += " dell'account %s" % entity.account.name
    if entity.location.IS_ROOM:
        where = "%s (%r)" % (entity.location.code, entity.location.get_destination())
    else:
        where = "%s in %r  " % (entity.location.get_name(), entity.get_in_room.get_destination())
    when = "%s (%s %s %s %s)" % (datetime.datetime.now(), calendar.minute, calendar.hour, calendar.day, calendar.month)

    note = note_class(code, who, where, when, argument)
    if not note:
        log.bug("note non è valido (%r) per la tipologia %s" % (note, note_type))
        entity.send_output("Impossibile segnalare un%s nuov%s %s." % (
            "" if grammar_genre == GRAMMAR.MASCULINE else "a", "o" if grammar_genre == GRAMMAR.MASCULINE else "a", note_singular))
        return False

    # Evitare di inviare una nota subito dopo averne inviata un'altra,
    # così da evitare eventuali spammer
    last_note_sended_at = getattr(entity.account, "last_%s_sended_at" % note_type)
    if (datetime.datetime.now() - last_note_sended_at).days <= 0:
        # (TD) Python 2.7, così non servirà più il check su days
        #total_seconds = (datetime.datetime.now() - last_note_sended_at).total_seconds()
        total_seconds = (datetime.datetime.now() - last_note_sended_at).seconds
        if total_seconds < config.sending_interval:
            remaining_seconds = config.sending_interval - total_seconds
            random_id = random.random()
            entity.send_output('''Potrai inviare %s solo tra <span id="%s">%d %s</span><script>parent.countdown("%s");</script>''' % (
                add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE),
                random_id, remaining_seconds, "secondo" if remaining_seconds == 1 else "secondi", random_id))
            return False

    # Inserisce un tetto massimo di note ancora aperte inviabili contando se
    # effettivamente le note dell'account ancora aperte sono così tante
    counter = 0
    for note_code in database[note_type + "s"]:
        if note_code.startswith(code_to_check):
            counter += 1
    if counter >= getattr(config, "max_account_%ss" % note_type):
        entity.send_output("Hai raggiunto il massimo di %s attualmente segnalabili, riprova tra qualche minuto." % (note_plural, config.game_name))
        log.monitor("%s ha raggiunto il numero massimo di %s inviabili, controllare e chiudere quelle obsolete cosicché possa inviarne delle altre.", (
            entity.code, note_plural))
        return False

    # Controlla che le altre note non abbiano lo stesso testo, altrimenti ci
    # troviamo davanti ad un possibile spammer, il sistema quindi fa finta
    # di salvarsi la nota e intanto segnala agli Amministratori lo spammer
    for other_note in database[note_type + "s"].itervalues():
        if other_note.text == argument and other_note.code.startswith(code_to_check):
            entity.send_output("%s è stato salvato. Grazie!" % (
                add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE).capitalize()))
            log.warning("Qualcuno per sbaglio invia due volte la stessa nota %s. Se continua esageratemente è un possibile spammer (%s)" % (
                note_singular, entity.get_conn().get_id()))
            return False

    database[note_type + "s"][note.code] = note

    # Le note sono uno di quei dati slegati dal gioco che possono essere
    # scritti subito sul disco vista la relativa rarità del suo invio e quindi
    # la bassa probabilità che vada ad inficiare sulle prestazioni globali
    note_path = "data/%ss/%s.dat" % (note_type, note.code)
    try:
        note_file = open(note_path, "w")
    except IOError:
        entity.send_output("Per un errore interno al server è impossibile salvare %s." % (
            add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE)))
        log.bug("Impossibile aprire il file %s in scrittura" % note_path)
        return False
    fwrite(note_file, note)
    note_file.close()

    # Poiché è stato salvata la nota bisogna anche salvare l'account che
    # contiene il numero di note inviate da sempre incrementato giusto qui
    # (TT) Bisogna fare attenzione al salvataggio facile dell'account,
    # potrebbero in futuro esservi informazioni duplicate o erroneamente
    # incrementate (dopo un crash) a seconda delle dipendenze tra i dati
    # nell'account ed altre tipologie di dati che non verrebbero salvate
    # se non alla chisura del Mud
    setattr(entity.account, "sended_%ss" % note_type, sended_notes + 1)
    setattr(entity.account, "last_%s_sended_at" % note_type, datetime.datetime.now())
    account_path = "persistence/accounts/%s.dat" % entity.account.name
    try:
        account_file = open(account_path, "w")
    except IOError:
        entity.send_output("Per un errore interno al server è impossibile salvare %s." % (
            add_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE)))
        log.bug("Impossibile aprire il file %s in scrittura" % account_path)
        return False
    fwrite(account_file, entity.account)
    account_file.close()

    entity.send_output("Hai appena segnalato %s%d° %s. Grazie!" % (
        get_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR, GRAMMAR.POSSESSIVE),
        sended_notes+1,
        note_singular))

    # Invia la mail
    subject = "Invio %s" % note_singular
    text = "L'account %s ha appena segnalato %s%d° %s." % (
        entity.account.name,
        get_article(note_singular, grammar_genre, GRAMMAR.DETERMINATE, GRAMMAR.SINGULAR),
        sended_notes+1,
        note_singular)
    text += "\nWho: %s"   % note.who
    text += "\nWhere: %s" % note.where
    text += "\nWhen: %s"  % note.when
    text += "\nText: %s"  % note.text

    mail.send_to_admins(subject, text, show_players=False)
    return True