diff --git a/bootstrap.py b/bootstrap.py index f8727b0..690994a 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -17,7 +17,7 @@ from core.upgrade import run_upgrades def get_config_from_env(): config_obj = DictObject() for k, v in os.environ.items(): - if k.startswith("TYRBOT_"): + if k.startswith("IGNCORE_"): keys = k[7:].lower().split("_") temp_config = config_obj for key in keys[:-1]: @@ -26,7 +26,7 @@ def get_config_from_env(): if key not in temp_config: temp_config[key] = DictObject() temp_config = temp_config.get(key) - logger.debug("overriding config value from env var '%s'" % k) + logger.debug(f"overriding config value from env var '{k}'") if v.lower() == "true": v = True elif v.lower() == "false": @@ -46,7 +46,7 @@ try: print("Unknown bot specified. Please edit your ##botname##.start.sh accordingly to the instructions found in the README.") exit(0) logger = Logger("core.bootstrap") - logger.info("Starting Tyrbot...") + logger.info("Starting Bot...") mod = __import__(f'conf.{bot}', fromlist=['BotConfig']) config: BotConfig = getattr(mod, 'BotConfig') if sys.version_info < (3, 9): @@ -72,7 +72,7 @@ try: config.server.dimension = int(config.server.dimension) if platform.system() == "Windows": - os.system("title %s.%d" % (config.character, config.server.dimension)) + os.system(f"title {config.character}.{config.server.dimension:d}") # paths to search for instances: core + module_paths paths = ["core"] diff --git a/core/aochat/bot.py b/core/aochat/bot.py index 3117e43..3a61b07 100644 --- a/core/aochat/bot.py +++ b/core/aochat/bot.py @@ -41,10 +41,10 @@ class Bot: # read character list character_list_packet: LoginCharacterList = self.read_packet() if isinstance(character_list_packet, LoginError): - self.logger.error("Error logging in: %s" % character_list_packet.message) + self.logger.error(f"Error logging in: {character_list_packet.message}") return False if character not in character_list_packet.names: - self.logger.error("Character '%s' does not exist on this account" % character) + self.logger.error(f"Character '{character}' does not exist on this account") return False index = character_list_packet.names.index(character) diff --git a/core/chat_blob.py b/core/chat_blob.py index bc74683..5544e23 100644 --- a/core/chat_blob.py +++ b/core/chat_blob.py @@ -1,9 +1,9 @@ class ChatBlob: - def __init__(self, title, msg): + def __init__(self, title, msg, prefix="", suffix=""): self.title = title self.msg = msg.strip("\n") - self.page_prefix = "" - self.page_postfix = "" + self.page_prefix = prefix + self.page_postfix = suffix def __str__(self): return f"ChatBlob('{self.title}', '{self.msg}')" diff --git a/core/command_service.py b/core/command_service.py index 134c005..9324800 100644 --- a/core/command_service.py +++ b/core/command_service.py @@ -86,7 +86,7 @@ class CommandService: al = access_levels.get(command_key, None) if al is not None and al != access_level.lower(): print(handler) - raise Exception("Different access levels specified for forms of command '%s'" % command_key) + raise Exception(f"Different access levels specified for forms of command '{command_key}'") access_levels[command_key] = access_level self.register(handler, cmd_name, params, access_level, description, inst.module_name, help_text, @@ -182,10 +182,10 @@ class CommandService: """ if value in self.channels: - self.logger.error("Could not register command channel '%s': command channel already registered" % value) + self.logger.error(f"Could not register command channel '{value}': command channel already registered") return - self.logger.debug("Registering command channel '%s'" % value) + self.logger.debug(f"Registering command channel '{value}'") self.channels[value] = label def is_command_channel(self, channel): @@ -228,11 +228,11 @@ class CommandService: command_alias = self.command_alias_service.check_for_alias(command_str) if alias_depth_count > 20: - raise Exception("Command alias infinite recursion detected for command '%s'" % message) + raise Exception(f"Command alias infinite recursion detected for command '{message}'") cmd_configs = self.get_command_configs(command_str, channel) access_level = self.access_service.get_access_level(char_id) - sender = SenderObj(char_id, self.character_service.resolve_char_to_name(char_id, "Unknown(%d)" % char_id), + sender = SenderObj(char_id, self.character_service.resolve_char_to_name(char_id, f"Unknown({char_id:d})"), access_level) if cmd_configs: # given a list of cmd_configs that are enabled, see if one has regex that matches incoming command_str @@ -245,7 +245,7 @@ class CommandService: if response is not None: reply(response) except Exception as e: - self.logger.error("error processing command: %s" % message, e) + self.logger.error(f"error processing command: {message}", e) self.relay_hub_service.send_message("access_denied_logger", sender, f"[ERROR] {sender.name}: {message}", f"[ERROR] {sender.name}: {message}") @@ -262,16 +262,17 @@ class CommandService: reply(self.format_help_text(command_str, help_text)) else: # the command is known, but no help is returned, therefore user does not have access to command - if access_level['label'] != "all": - self.relay_hub_service.send_message("access_denied_logger", sender, - f"[DENIED] {sender.name}: {message}", - f"[DENIED] {sender.name}: {message}") - self.bot.send_mass_message(char_id, self.getresp("global", "access_denied")) + self.access_denied_response(message, sender, cmd_config, reply) + # if access_level['label'] != "all": + # self.relay_hub_service.send_message("access_denied_logger", sender, + # f"[DENIED] {sender.name}: {message}", + # f"[DENIED] {sender.name}: {message}") + # self.bot.send_mass_message(char_id, self.getresp("global", "access_denied")) else: self.handle_unknown_command(command_str, command_args, channel, sender, reply) except Exception as e: - self.logger.error("error processing command: %s" % message, e) - sender = SenderObj(char_id, self.character_service.resolve_char_to_name(char_id, "Unknown(%d)" % char_id), + self.logger.error(f"error processing command: {message}", e) + sender = SenderObj(char_id, self.character_service.resolve_char_to_name(char_id, f"Unknown({char_id:d})"), 0) self.relay_hub_service.send_message("access_denied_logger", sender, f"[ERROR] {sender.name}: {message}", f"[ERROR] {sender.name}: {message}") @@ -426,7 +427,7 @@ class CommandService: False) t: Thread = Thread(target=i, daemon=True) - t.run() + t.start() def handle_private_channel_message(self, conn: Conn, packet: server_packets.PrivateChannelMessage): if not self.setting_service.get("accept_commands_from_slave_bots").get_value() and conn.id != "main": @@ -449,7 +450,7 @@ class CommandService: lambda msg: self.bot.send_private_channel_message(msg, private_channel_id=conn.char_id, conn_id=conn.id), conn, - False), daemon=True).run() + False), daemon=True).start() def handle_public_channel_message(self, conn: Conn, packet: server_packets.PublicChannelMessage): if not self.setting_service.get("accept_commands_from_slave_bots").get_value() and conn.id != "main": @@ -472,7 +473,7 @@ class CommandService: packet.char_id, lambda msg: self.bot.send_org_message(msg, conn_id=conn.id), conn, - False), daemon=True).run() + False), daemon=True).start() def trim_command_symbol(self, s): symbol = self.setting_service.get("symbol").get_value() diff --git a/core/db.py b/core/db.py index 0f14b2f..66673e9 100644 --- a/core/db.py +++ b/core/db.py @@ -75,13 +75,12 @@ class DB: if string.__contains__("UPDATE ") or string.__contains__("INSERT "): conn.commit() except Exception as e: - raise SqlException("SQL Error: '%s' for '%s' [%s]" % ( - str(e), sql, ", ".join(map(lambda x: str(x), params)))) from e + raise SqlException( f"SQL Error: '{str(e)}' for '{sql}' " + f"[{', '.join(map(lambda x: str(x), params))}]") from e elapsed = time.time() - start_time result = callback(cur) if elapsed > 5: - self.logger.warning("slow query (%fs) '%s' for params: %s" % (elapsed, sql, str(params))) - + self.logger.warning(f"slow query ({elapsed:f}s) '{sql}' for params: {str(params)}") return result def query_single(self, sql, params=None, extended_like=False) -> DictObject: diff --git a/core/event_service.py b/core/event_service.py index e4fdf87..53b246e 100644 --- a/core/event_service.py +++ b/core/event_service.py @@ -72,7 +72,7 @@ class EventService: """ if len(inspect.signature(handler).parameters) != 2: raise Exception( - "Incorrect number of arguments for handler '%s.%s()'" % (handler.__module__, handler.__name__)) + f"Incorrect number of arguments for handler '{handler.__module__}.{handler.__name__}()'") event_base_type, event_sub_type = self.get_event_type_parts(event_type) module = module.lower() @@ -80,12 +80,12 @@ class EventService: is_hidden = 1 if is_hidden else 0 is_enabled = 1 if is_enabled else 0 if event_base_type not in self.event_types: - self.logger.error("Could not register handler '%s' for event type '%s': event type does not exist" % ( - handler_name, event_type)) + self.logger.error( + f"Could not register handler '{handler_name}' for event type '{event_type}': event type does not exist") return if not description: - self.logger.warning("No description for event_type '%s' and handler '%s'" % (event_type, handler_name)) + self.logger.warning(f"No description for event_type '{event_type}' and handler '{handler_name}'") row = self.db.query_single("SELECT 1 FROM event_config WHERE event_type = ? AND handler = ?", [event_base_type, handler_name]) @@ -117,28 +117,34 @@ class EventService: event_base_type, event_sub_type = self.get_event_type_parts(event_type) if event_base_type not in self.event_types: - self.logger.error("Could not fire event type '%s': event type does not exist" % event_type) + self.logger.error(f"Could not fire event type '{event_type}': event type does not exist") return data = self.get_handlers(event_base_type, event_sub_type) - for row in data: - if event_type != "connect": - t = Thread(target=self.call_handler, args=(row.handler, event_type, event_data), daemon=True) - t.run() - else: - self.call_handler(row.handler, event_type, event_data) + + # We dont want to spawn a new Thread for each event handler, but only per event type. + def i(): + for row in data: + if event_type != "connect": + self.call_handler(row.handler, event_type, event_data) + else: + self.call_handler(row.handler, event_type, event_data) + if event_type != "connect": + Thread(target=i, daemon=True).start() + else: + i() def call_handler(self, handler_method, event_type, event_data): handler = self.handlers.get(handler_method, None) if not handler: self.logger.error( - "Could not find handler callback for event type '%s' and handler '%s'" % (event_type, handler_method)) + f"Could not find handler callback for event type '{event_type}' and handler '{handler_method}'") return try: handler(event_type, event_data) except Exception as e: - self.logger.error("error processing event '%s'" % event_type, e) + self.logger.error(f"error processing event '{event_type}'", e) def get_event_type_parts(self, event_type): parts = event_type.lower().split(":", 1) diff --git a/core/lookup/character_history_service.py b/core/lookup/character_history_service.py index 84c6c70..62543bb 100644 --- a/core/lookup/character_history_service.py +++ b/core/lookup/character_history_service.py @@ -41,13 +41,13 @@ class CharacterHistoryService: # with TorRequests() as tor_request: # with tor_request.get_session(1) as session: # r = session.get(url, timeout=5) - r = requests.get(url, timeout=5, headers={"User-Agent": self.bot.major_version}) - result = r.json() + # r = requests.get(url, timeout=5, headers={"User-Agent": self.bot.major_version}) + result = json.loads(r) except ReadTimeout: - self.logger.warning("Timeout while requesting '%s'" % url) + self.logger.warning(f"Timeout while requesting '{url}'") result = None except Exception as e: - self.logger.error("Error requesting history for url '%s'" % url, e) + self.logger.error(f"Error requesting history for url '{url}'", e) result = None if result: diff --git a/core/private_channel_service.py b/core/private_channel_service.py index 1955d73..4eb4f4d 100644 --- a/core/private_channel_service.py +++ b/core/private_channel_service.py @@ -33,7 +33,7 @@ class PrivateChannelService: self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message, priority=30) - self.access_service.register_access_level("guest", 95, self.in_private_channel) + self.access_service.register_access_level("guest", 94, self.in_private_channel) def handle_private_channel_message(self, conn: Conn, packet: server_packets.PrivateChannelMessage): if conn.id != "main": diff --git a/core/text.py b/core/text.py index 2f64690..842b1c7 100644 --- a/core/text.py +++ b/core/text.py @@ -207,7 +207,7 @@ class Text: label2 = self.format_message(label) else: label2 = self.format_message(label) + " (Page " + str(index) + " / " + str(num_pages) + ")" - return chatblob.page_prefix + self.format_page(label2, page) + chatblob.page_postfix + return self.format_message(chatblob.page_prefix) + self.format_page(label2, page) + self.format_message(chatblob.page_postfix) return list(map(mapper, zip(pages, range(1, num_pages + 1)))) diff --git a/core/tyrbot.py b/core/tyrbot.py index 4707a31..74edcc4 100644 --- a/core/tyrbot.py +++ b/core/tyrbot.py @@ -42,7 +42,7 @@ class Tyrbot: self.last_timer_event = 0 self.start_time = int(time.time()) self.major_version = "IGNCore v2.5" - self.minor_version = "1" + self.minor_version = "2" self.incoming_queue = FifoQueue() self.mass_message_queue = None self.conns = DictObject() diff --git a/modules/core/accounting/services/access_service.py b/modules/core/accounting/services/access_service.py index 5ec078f..15a27a6 100644 --- a/modules/core/accounting/services/access_service.py +++ b/modules/core/accounting/services/access_service.py @@ -2,6 +2,7 @@ import inspect from typing import List from core.decorators import instance +from core.dict_object import DictObject from core.logger import Logger @@ -100,14 +101,17 @@ class AccessService: for access_level in self.access_levels: if access_level["level"] == level: return access_level - return False + self.logger.warning(f"Access to undefined Accesslevel detected: {level} - answering with rank 'None'") + return DictObject({"label": "none", "level": 0, "handler": self.no_access}) def get_access_level_by_label(self, label) -> dict or bool: label = label.lower() for access_level in self.access_levels: if access_level["label"] == label: return access_level - return False + + self.logger.warning(f"Access to undefined Accesslevel detected: {label} - answering with rank 'None'") + return DictObject({"label": "none", "level": 0, "handler": self.no_access}) def check_access(self, char, access_level_label) -> bool: char_id = self.character_service.resolve_char_to_id(char) diff --git a/modules/core/accounting/services/account_service.py b/modules/core/accounting/services/account_service.py index 3319808..9b099f0 100644 --- a/modules/core/accounting/services/account_service.py +++ b/modules/core/accounting/services/account_service.py @@ -122,16 +122,27 @@ class AccountService: "Is this bot used as an alliancebot") self.setting_service.register_new(self.module_name, "alt_verification", False, BooleanSettingType(), "alts require admin verification") + # Default preferences + self.setting_service.register_new(self.module_name, "pref_autoinvite", False, BooleanSettingType(), + "Default Value for the auto invite preference") + self.setting_service.register_new(self.module_name, "pref_raidinvite", True, BooleanSettingType(), + "Default Value for the raid invite (Massinvite) preference") + self.setting_service.register_new(self.module_name, "pref_raidspam", True, BooleanSettingType(), + "Default Value for the raid spam (Mass Message) preference") + self.setting_service.register_new(self.module_name, "pref_newsspam", True, BooleanSettingType(), + "Default Value for the news spam (News on logon) preference") + v = self.setting_service.get_value("pref_raidspam") + # Ranks if self.setting_service.get_value("is_alliance_bot") == "1": self.access_service.register_access_level("officer", 80, self.check_officer) self.access_service.register_access_level("general", 70, self.check_general) self.access_service.register_access_level("president", 60, self.check_president) self.access_service.register_access_level("council", 40, self.check_council) - self.access_service.register_access_level("moderator", 30, self.check_moderator) self.access_service.register_access_level("member", 90, self.check_member) - self.access_service.register_access_level("admin", 20, self.check_admin) self.access_service.register_access_level("leader", 50, self.check_leader) + self.access_service.register_access_level("moderator", 30, self.check_moderator) + self.access_service.register_access_level("admin", 20, self.check_admin) self.access_service.register_access_level("superadmin", 10, self.check_superadmin) def get_main(self, char_id) -> DictObject: @@ -300,19 +311,28 @@ class AccountService: return [self.bot.public_channel_service.org_id] def create_users(self, users, disable=False) -> int: + # Default preferences + pref_autoinvite = self.setting_service.get_value("pref_autoinvite") + pref_raidinvite = self.setting_service.get_value("pref_raidinvite") + pref_raidspam = self.setting_service.get_value("pref_raidspam") + pref_newsspam = self.setting_service.get_value("pref_newsspam") if type(users) == list and len(users) > 0: with self.db.pool.get_connection() as conn: with conn.cursor() as cur: if disable: cur.executemany( - "INSERT IGNORE INTO account(char_id, main, member, disabled, last_updated, created) " - "VALUES(?, ?, ?, 1, ?, ?) ON DUPLICATE KEY UPDATE " + "INSERT IGNORE INTO account(char_id, main, member, disabled, last_updated, created, " + "auto_invite, raid_invite, raid_spam, news_spam) " + f"VALUES(?, ?, ?, 1, ?, ?, {pref_autoinvite}, {pref_raidinvite}, " + f"{pref_raidspam}, {pref_newsspam}) ON DUPLICATE KEY UPDATE " "member=VALUE(member), last_updated=VALUE(last_updated), disabled=1", users) return cur.rowcount cur.executemany( - "INSERT IGNORE INTO account(char_id, main, member, last_updated, created) " - "VALUES(?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE " + "INSERT IGNORE INTO account(char_id, main, member, last_updated, " + "created, auto_invite, raid_invite, raid_spam, news_spam) " + f"VALUES(?, ?, ?, ?, ?, {pref_autoinvite}, {pref_raidinvite}, " + f"{pref_raidspam}, {pref_newsspam}) ON DUPLICATE KEY UPDATE " "member=VALUE(member), last_updated=VALUE(last_updated)", users) return cur.rowcount diff --git a/modules/core/system/system_controller.py b/modules/core/system/system_controller.py index d887fac..503bbcc 100644 --- a/modules/core/system/system_controller.py +++ b/modules/core/system/system_controller.py @@ -49,7 +49,7 @@ class SystemController: NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]), "Maximum size of blobs in private channel") - self.setting_service.register_new("core.system", "accept_commands_from_slave_bots", False, BooleanSettingType(), + self.setting_service.register_new("core.system", "accept_commands_from_slave_bots", True, BooleanSettingType(), "Accept and respond to commands sent to slave bots (only applies if you have " "added slave bots in the config)") diff --git a/modules/onlinebot/accounting/account_service.py b/modules/onlinebot/accounting/account_service.py new file mode 100644 index 0000000..0432350 --- /dev/null +++ b/modules/onlinebot/accounting/account_service.py @@ -0,0 +1,95 @@ +from core.command_service import CommandService +from core.decorators import instance, command +from modules.core.accounting.preference_controller import PreferenceController +from modules.core.accounting.services.account_service import AccountService + + +@instance(name="account_service", override=True) +class AccountServiceOnlineBot(AccountService): + registered = [] + + def inject(self, registry): + super().inject(registry) + self.preference_controller: PreferenceController = registry.get_instance("preference_controller") + + def pre_start(self): + super().pre_start() + self.db.exec("CREATE TABLE IF NOT EXISTS registered (char_id int primary key)") + self.registered = [x.char_id for x in self.db.query("SELECT * from registered")] + self.access_service.register_access_level("unregistered", 95, self.check_unregistered) + + @command(command="opt-in", params=[], + description="Register yourself in the bot", + access_level="unregistered") + def cmd_opt_in(self, request): + player = self.pork.get_character_info(request.sender.name) + if player.org_id in self.get_orgs(): + if self.is_registered((main := self.get_main(request.sender.char_id)).char_id): + return "You're already registered." + self.db.exec("INSERT IGNORE INTO registered VALUES (?)", [main.char_id]) + pref_autoinvite = int(self.setting_service.get_value("pref_autoinvite")) + pref_raidinvite = int(self.setting_service.get_value("pref_raidinvite")) + pref_raidspam = int(self.setting_service.get_value("pref_raidspam")) + pref_newsspam = int(self.setting_service.get_value("pref_newsspam")) + self.registered.append(main.char_id) + + self.preference_controller.set_pref(main.char_id, "news", pref_newsspam) + self.preference_controller.set_pref(main.char_id, "raidspam", pref_raidspam) + self.preference_controller.set_pref(main.char_id, "autoinvite", pref_autoinvite) + + # This one is of no use in Onlinebots, + # as the command taking advantage of it + # is only implemented in the raidbot modules. + # raidspam is used instead. + self.preference_controller.set_pref(main.char_id, "raidinvite", pref_raidinvite) + + return "You've been registered successfully, and your preferences have been set to the defaults." + + @command(command="opt-out", params=[], + description="Unregister yourself from the bot", + access_level="member") + def cmd_opt_out(self, request): + main = self.get_main(request.sender.char_id) + if self.is_registered(main.char_id): + self.db.exec("DELETE FROM registered where char_id = ?", [main.char_id]) + self.registered.remove(main.char_id) + self.db.exec("UPDATE account SET disabled=1 where char_id=?", [main.char_id]) + return "You've been unregistered successfully. For re-registering, " \ + "you may need to contact an Administrator, " \ + "as you may only register once as a spam protection. " \ + "Further tells will get ignored." + else: + return "You're not registered, and thus cannot unregister yourself." + + def is_registered(self, char_id): + return char_id in self.registered + + def simple_checks(self, account): + main = account.get("main", -1) + if main and main in self.registered: + return super().simple_checks(account) + return False + + def check_unregistered(self, char_id) -> bool: + account = self.get_account(char_id) or {} + return super().simple_checks(account) + + def check_member(self, char_id) -> bool: + account = self.get_account(char_id) or {} + if self.simple_checks(account): + return True + return False + + +@instance(name="command_service", override=True) +class AccountServiceOnlineBot(CommandService): + def access_denied_response(self, message, sender, cmd_config, reply): + self.relay_hub_service.send_message("access_denied_logger", sender, f"[DENIED] {sender.name}: {message}", + f"[DENIED] {sender.name}: {message}") + + if sender.access_level["label"] != "all": + if sender.access_level['label'] == "unregistered": + self.bot.send_mass_message(sender.char_id, + "You are not registered yet. To gain access to my commands, you'll need to use opt-in") + return + self.bot.send_mass_message(sender.char_id, self.getresp("global", "access_denied")) diff --git a/modules/onlinebot/online/org_controller.py b/modules/onlinebot/online/org_controller.py index 28ca3e8..e62970e 100644 --- a/modules/onlinebot/online/org_controller.py +++ b/modules/onlinebot/online/org_controller.py @@ -76,23 +76,23 @@ class OrgController: orgs = orgs[0] if self.db.exec("REPLACE INTO orgs(org_id) VALUES(?)", [orgs.org_id]) == 1: - sender.reply("Adding the organisation %s to the roster..." % orgs.org_name) + sender.reply(f"Adding the organisation {orgs.org_name} to the roster...") org_adder = Thread(name=orgs.org_id, target=self.fetch_single, args=(orgs.org_id, orgs.org_name, sender)) self.threads[orgs.org_id] = org_adder org_adder.start() else: - return "The organisation %s is in the roster already." % orgs.org_name + return f"The organisation {orgs.org_name} is in the roster already." elif len(orgs) == 0: - return "No org with the name %s was found on PoRK." % search + return f"No org with the name {search} was found on PoRK." else: blob = "Your search had multiple results; please pick an org:
" for org in orgs: - blob += "[%s][%s] %s (%s) <%s>%s [%s " \ - "members]
" \ - % (self.text.make_chatcmd("Add", "/tell orgs add %s" % org.org_id), - self.text.make_chatcmd("More", "/tell org info %s" % org.org_id), - org.org_name, org.org_id, org.faction.lower(), org.faction, org.member_count) + blob += f'[{self.text.make_chatcmd("Add", f"/tell orgs add {org.org_id}")}]' \ + f'[{self.text.make_chatcmd("More", f"/tell org info {org.org_id}")}]' \ + f' {org.org_name} ({org.org_id}) ' \ + f'<{org.faction.lower()}>{org.faction} [{org.member_count} members]' \ + f'
' return ChatBlob("Pick an Org", blob) @command(command="orgs", params=[Const("rem"), Any("Organisation")], access_level="admin", @@ -109,19 +109,19 @@ class OrgController: org_remover = Thread(name=orgs.org_id, target=self.remove_single, args=(orgs.org_id, orgs.org_name)) self.threads[orgs.org_id] = org_remover org_remover.start() - return "Removed the organisation %s from the roster." % orgs.org_name + return f"Removed the organisation {orgs.org_name} from the roster." else: - return "The organisation %s is not on the roster list." % orgs.org_name + return f"The organisation {orgs.org_name} is not on the roster list." elif len(orgs) == 0: - return "The organisation %s is not on the roster list." % search + return f"The organisation {search} is not on the roster list." else: blob = "Your search had multiple results; please pick an org:
" for org in orgs: - blob += "[%s][%s] %s (%s) <%s>%s [%s " \ - "members]
" \ - % (self.text.make_chatcmd("Remove", "/tell orgs remove %s" % org.org_id), - self.text.make_chatcmd("More", "/tell org info %s" % org.org_id), - org.org_name, org.org_id, org.faction.lower(), org.faction, org.member_count) + blob += f'[{self.text.make_chatcmd("Remove", f"/tell orgs remove {org.org_id}")}]' \ + f'[{self.text.make_chatcmd("More", f"/tell org info {org.org_id}")}]' \ + f' {org.org_name} ({org.org_id}) ' \ + f'<{org.faction.lower()}>{org.faction} [{org.member_count} members]' \ + f'
' return ChatBlob("Pick an Org", blob) @command(command="orgs", params=[Const("list", is_optional=True)], access_level="member", @@ -169,7 +169,7 @@ class OrgController: continue data.append((int(match[0]), match[1], int(match[2]), match[4], start)) count += 1 - self.logger.info("Batch %s done!" % letter) + self.logger.info(f"Batch {letter} done!") with self.db.pool.get_connection() as conn: with conn.cursor() as cur: @@ -183,7 +183,7 @@ class OrgController: "last_seen=VALUE(last_seen)", data) self.db.exec("DELETE FROM all_orgs where last_seen < ?", [time.time() - 2 * 24 * 60 * 60]) - self.logger.info("Successfully fetched %d orgs in %d seconds." % (count, time.time() - start)) + self.logger.info(f"Successfully fetched {count} orgs in {time.time() - start:.2f} seconds.") self.threads.pop('orgdiscover', None) if "orgdiscover" not in self.threads.keys(): @@ -229,7 +229,7 @@ class OrgController: "level": char_info["LEVELX"], "ai_level": char_info["ALIENLEVEL"], "ranks": 0})) - self.logger.info("Organisation %s has been updated." % org.org_name) + self.logger.info(f"Organisation {org.org_name} has been updated.") self.account_service.create_users(accounts) if len(data) > 1: with self.db.lock: @@ -286,8 +286,9 @@ class OrgController: "ranks": count})) self.account_service.remove_members(accounts) self.log(output, time.time() - start) - self.logger.info("Successfully fetched %d players from %d orgs in %d seconds. - " % ( - len(data), len(ours), time.time() - start)) + self.logger.info( + f"Successfully fetched {len(data)} players from {len(ours)} orgs " + f"in {time.time() - start:.2f} seconds. - ") del self.threads['roster'] if "roster" not in self.threads.keys(): @@ -330,11 +331,11 @@ class OrgController: "pvp_rating, pvp_title, source, last_updated) VALUES " "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", data) self.account_service.create_users(accounts) - self.logger.info("Organisation %s added!" % org_name) + self.logger.info(f"Organisation {org_name} added!") sender.reply(f"{org_name} has been added to the roster. " f"Runtime: {time.time() - start:.2f} seconds.") - self.logger.info("Successfully fetched %d players in %d seconds." % (count, time.time() - start)) + self.logger.info(f"Successfully fetched {count} players in {time.time() - start} seconds.") del self.threads[org_id] def remove_single(self, org_id, org_name): @@ -344,7 +345,7 @@ class OrgController: self.db.exec("UPDATE account set member=-1 where char_id in" " (SELECT char_id from player where org_id=?)", [org_id]) self.db.exec("DELETE FROM online where char_id in (SELECT char_id from player where org_id=?)", [org_id]) - self.logger.info("Organisation %s removed!" % org_name) + self.logger.info(f"Organisation {org_name} removed!") del self.threads[org_id] def log(self, blob, duration): diff --git a/modules/onlinebot/raids/raid_controller.py b/modules/onlinebot/raids/raid_controller.py index 32126b8..0094006 100644 --- a/modules/onlinebot/raids/raid_controller.py +++ b/modules/onlinebot/raids/raid_controller.py @@ -15,6 +15,7 @@ from core.setting_service import SettingService from core.text import Text from core.tyrbot import Tyrbot from core.util import Util +from modules.core.accounting.services.account_service import AccountService from modules.core.ban.ban_service import BanService @@ -62,6 +63,7 @@ class RaidController: self.setting_service: SettingService = registry.get_instance("setting_service") self.ban: BanService = registry.get_instance("ban_service") self.private_channel_service: PrivateChannelService = registry.get_instance("private_channel_service") + self.account_service: AccountService = registry.get_instance("account_service") @command(command="raid", params=[Const("desc"), Any("<description>")], description="Change the title of the Raid", access_level="leader") @@ -119,7 +121,9 @@ class RaidController: if self.raid: return "There's already a raid running." self.raid = Raid(request.sender, request.sender.access_level["level"]) - self.raid.bot = self.bot.get_char_name() + # after I saw myself sending raid invites with the wrong bot reference by accident atleast once, + # the default is being set to allianceraid again. + self.raid.bot = "allianceraid" # self.bot.get_char_name() self.raid.last_action = time.time() spam = f""" ________________ @@ -146,7 +150,7 @@ class RaidController: return "There's no raid running." def blob(label, msg): - return "%s" % (textwrap.dedent(msg), textwrap.dedent(label)) + return f"{textwrap.dedent(label)}" last = time.time() if self.raid.last_spam + 5 * 60 > last: @@ -178,7 +182,7 @@ class RaidController: """ subtile = f"""[{request.sender.name}]: Raid Starting: {self.raid.raid_desc} -- use /tell {self.raid.bot} join to participate or {click_here} """ # noinspection SqlAggregates - players = self.db.query("SELECT p.*, a.subtile_spam, a.raid_invite, a.raid_spam from online o " + players = self.db.query("SELECT p.*, a.subtile_spam, a.raid_invite, a.raid_spam, a.main, a.disabled, a.member from online o " "left join player p on o.char_id = p.char_id " "left join account a on a.char_id=(select main from account where char_id=o.char_id) " "where p.level >= ? AND ((a.raid_invite=1 or a.raid_spam = 1) " @@ -189,18 +193,20 @@ class RaidController: "AND o.bot=? group by o.char_id " "ORDER BY p.profession, p.name, p.level, p.org_rank_id;", [self.raid.min_level, self.bot.get_char_id()]) - self.bot.send_mass_message(request.sender.char_id, f"Sending invites to {len(players)} Players...") + self.bot.send_mass_message(request.sender.char_id, f"Attempting to send {len(players)} invites...") info = "Sent invites to:" prof = "" + count = len(players) for i in players: + if not self.account_service.simple_checks(i): + count -= 1 + continue if prof != i.profession: info += "\n\n" - info += "\n" + "%s\n" % i.profession + info += "\n" + f"{i.profession}\n" info += "" prof = i.profession - if self.ban.get_ban(i.char_id): - continue + if i.raid_spam == 1: self.bot.send_mass_message(i.char_id, spam if i.subtile_spam == 0 else subtile) # if i.raid_invite == 1: @@ -211,7 +217,7 @@ class RaidController: self.raid.last_action = time.time() self.raid.last_spam = time.time() self.bot.send_mass_message(request.sender.char_id, - f"Successfully sent {len(players)} invites! [{self.text.format_page('More', info)}]") + f"Successfully sent {count} invite{'s' if count != 1 else ''}! [{self.text.format_page('More', info)}]") @command(command="raid", params=[Const("settings")], description="Change the title of the Raid", access_level="leader") diff --git a/modules/standard/helpbot/random_controller.py b/modules/standard/helpbot/random_controller.py index 548b0be..7a2341c 100644 --- a/modules/standard/helpbot/random_controller.py +++ b/modules/standard/helpbot/random_controller.py @@ -25,7 +25,7 @@ class RandomController: self.command_alias_service.add_alias("verify", "roll verify") self.command_alias_service.add_alias("lootorder", "random") - @command(command="random", params=[Any("items")], access_level="all", + @command(command="random", params=[Any("items")], access_level="member", description="Randomly order a list of elements", extended_description="Enter a space-delimited list of items to randomize.") def random_command(self, _, items): @@ -33,19 +33,19 @@ class RandomController: random.shuffle(items) return " ".join(items) - @command(command="roll", params=[Const("verify"), Int("roll_id")], access_level="all", + @command(command="roll", params=[Const("verify"), Int("roll_id")], access_level="member", description="Verify a roll that happened") def roll_verify_command(self, _, _1, roll_id): row = self.db.query_single("SELECT * FROM roll WHERE id = ?", [roll_id]) if not row: - return "Could not find roll with id %d." % roll_id + return f"Could not find roll with id {roll_id:d}." else: time_string = self.util.time_to_readable(int(time.time()) - row.created_at) name = self.character_service.resolve_char_to_name(row.char_id) - return "%s rolled by %s %s ago. Possible options: %s." % ( - row.result, name, time_string, row.options) + return f"{row.result} rolled by {name} " \ + f"{time_string} ago. Possible options: {row.options}." - @command(command="roll", params=[Int("start_value", is_optional=True), Int("end_value")], access_level="all", + @command(command="roll", params=[Int("start_value", is_optional=True), Int("end_value")], access_level="member", description="Roll a number between 1 and a number", extended_description="The given numbers are included in the roll.") def roll_number_command(self, request, start_value, end_value): @@ -64,7 +64,7 @@ class RandomController: f"To verify do /tell verify {self.db.last_insert_id():d}" # Keep this method at the bottom of file otherwise it will precede over all other commands - @command(command="roll", params=[Any("items")], access_level="all", + @command(command="roll", params=[Any("items")], access_level="member", description="Roll a random value", extended_description="Enter a space-delimited list of values to roll") def roll_text_variables_command(self, request, items): diff --git a/modules/standard/helpbot/whompah_controller.py b/modules/standard/helpbot/whompah_controller.py index d36c962..bbb5941 100644 --- a/modules/standard/helpbot/whompah_controller.py +++ b/modules/standard/helpbot/whompah_controller.py @@ -17,7 +17,7 @@ class WhompahController: self.db.create_view("whompah_cities") self.db.create_view("whompah_cities_rel") - @command(command="whompah", params=[], access_level="all", + @command(command="whompah", params=[], access_level="member", description="Show list of whompah cities") def whompah_list_cmd(self, request): cities = self.db.query("SELECT id, city_name, zone, faction, short_name FROM whompah_cities ORDER BY city_name") @@ -29,7 +29,7 @@ class WhompahController: return ChatBlob("Whompah Cities", blob) - @command(command="whompah", params=[Any("city1"), Any("city2")], access_level="all", + @command(command="whompah", params=[Any("city1"), Any("city2")], access_level="member", description="Show whompah route between two cities") def whompah_travel_cmd(self, request, city_name1, city_name2): city1 = self.get_whompah_city(city_name1) @@ -53,7 +53,7 @@ class WhompahController: path = self.format_path(self.find_path(cities, city1.id, city2.id)) return " -> ".join(path) - @command(command="whompah", params=[Any("city")], access_level="all", + @command(command="whompah", params=[Any("city")], access_level="member", description="Show whompah destinations for a city") def whompah_city_cmd(self, request, city_name): city = self.get_whompah_city(city_name) diff --git a/modules/standard/loot/loot_lists_controller.py b/modules/standard/loot/loot_lists_controller.py index 7c16571..5cc1f0d 100644 --- a/modules/standard/loot/loot_lists_controller.py +++ b/modules/standard/loot/loot_lists_controller.py @@ -265,7 +265,7 @@ class LootListsController: @command(command="totwh", params=[Options( ["binyacht", "guardian", "summoner", "loremaster", "nematet", "aegis", "lien", "gartua", "aztur", "khalum", "uklesh", "gen", "armor"])], - description="Get list of items from Temple of Three Winds", access_level="all") + description="Get list of items from Temple of Three Winds", access_level="member") def totwh_loot_cmd(self, _, category_name): category = self.get_real_category_name(category_name) items = self.get_items("Temple of Three Winds (HL)", category) @@ -274,7 +274,7 @@ class LootListsController: else: return "No loot registered for %s." % category_name - @command(command="totwh", params=[], description="Get list of items from Temple of Three Winds", access_level="all") + @command(command="totwh", params=[], description="Get list of items from Temple of Three Winds", access_level="member") def totwh_tables_cmd(self, _): return ChatBlob("Temple of Three Winds (HL) loot tables", self.build_overview("Temple of Three Winds (HL)", "totwh")) diff --git a/modules/standard/news/mail_controller.py b/modules/standard/news/mail_controller.py index 288c759..a4f6c50 100644 --- a/modules/standard/news/mail_controller.py +++ b/modules/standard/news/mail_controller.py @@ -52,7 +52,7 @@ class MailController: def logon_event(self, _, data): if not self.bot.is_ready(): return - if data.account.disabled == 1: + if not self.account_service.simple_checks(data.account): return if "mail" in self.buddy_service.get_buddy(data.packet.char_id)["types"]: self.job_schedule.delayed_job(self.send_mail, 16, data.packet.char_id, self.get_mails(data.packet.char_id)) @@ -129,8 +129,10 @@ class MailController: recipient = self.account_service.get_account(receiver.char_id) if not recipient: return f"No account for {receiver.name} found." - if recipient.member == -1 or recipient.disabled == 1: - return f"The Character {recipient.name} has " \ + + # if recipient.member == -1 or recipient.disabled == 1: + if not self.account_service.simple_checks(recipient): + return f"The Character {recipient.name} has " \ f"no active account in ." self.db.exec("INSERT INTO mail(sender, recipient, text, sent_at) VALUES(?, ?, ?, ?)", [sender.sender.char_id, recipient.main, message, time.time()]) @@ -152,12 +154,12 @@ class MailController: group = self.account_service.get_group_tag(recipient) users = [] if group: - if group == "all": - if sender.sender.access_level['level'] > 20: - return "This group is unavailable." - users = self.account_service.get_all_members() - else: - users = self.account_service.get_by_group(group) + # if group == "all": + # if sender.sender.access_level['level'] > 20: + # return "This group is unavailable." + # users = self.account_service.get_all_members() + # else: + users = self.account_service.get_by_group(group) elif not group: return f"Sorry, but the group {recipient} does not exist. " \ diff --git a/modules/standard/news/news_controller.py b/modules/standard/news/news_controller.py index f78eecf..8ff661e 100644 --- a/modules/standard/news/news_controller.py +++ b/modules/standard/news/news_controller.py @@ -122,7 +122,8 @@ hh:mm - DD.MM.YYYY return if "member" in self.buddy_service.get_buddy(data.packet.char_id)["types"]: account = data.account - if account.disabled == 1: + # Apply standard checks. (User Banned, Account disabled, ...) + if not self.account_service.simple_checks(data.account): return if self.db.query_single("SELECT * from org_bots where char_id=?", [data.packet.char_id]): return @@ -138,6 +139,8 @@ hh:mm - DD.MM.YYYY self.job_schedule.delayed_job(self.send_news, 15, user, self.account_service.get_alts(account.main), discord, self.preferences.get_pref_view_small(account)) + + # This one is kinda redudant now that the simple checks get done above, will remove it in the future. if account.last_seen == 0 and self.setting_service.get_value('is_alliance_bot') == "1": self.bot.send_mass_message(data.packet.char_id, self.INFO) diff --git a/modules/standard/online/online_display.py b/modules/standard/online/online_display.py index f0ff75d..3ad99b6 100644 --- a/modules/standard/online/online_display.py +++ b/modules/standard/online/online_display.py @@ -35,13 +35,12 @@ class OnlineDisplay: blob, org, priv, notify = online postfix = [] if org > 0: - postfix.append(f"Org: {org}") + postfix.append(f"Org: {org}") if priv > 0: - postfix.append(f"Priv: {priv}") + postfix.append(f"Priv: {priv}") if notify > 0: - postfix.append(f"Buddylist: {notify}") - blob = ChatBlob(title, blob) - blob.page_postfix = f" ({f', '.join(postfix)})" + postfix.append(f"Buddylist: {notify}") + blob = ChatBlob(title, blob, suffix=f" ({f', '.join(postfix)})") return blob def format_by_channel_main(self, query, params): diff --git a/modules/standard/quote/quote_controller.py b/modules/standard/quote/quote_controller.py index cc3af4f..946fa31 100644 --- a/modules/standard/quote/quote_controller.py +++ b/modules/standard/quote/quote_controller.py @@ -20,7 +20,7 @@ class QuoteController: "created_at INT NOT NULL, " "content VARCHAR(4096) NOT NULL)") - @command(command="quote", params=[], access_level="all", + @command(command="quote", params=[], access_level="member", description="Show a random quote") def quote_command(self, request): quote = self.get_quote_info() @@ -30,7 +30,7 @@ class QuoteController: else: return "There are no quotes to display." - @command(command="quote", params=[Int("quote_id")], access_level="all", + @command(command="quote", params=[Int("quote_id")], access_level="member", description="Show a specific quote") def quote_view_command(self, _, quote_id): quote = self.get_quote_info(quote_id) @@ -40,7 +40,7 @@ class QuoteController: else: return f"Could not find quote with ID {quote_id:d}." - @command(command="quote", params=[Const("add"), Any("quote")], access_level="all", + @command(command="quote", params=[Const("add"), Any("quote")], access_level="member", description="Show a specific quote") def quote_add_command(self, request, _, quote): if len(quote) > 4096: diff --git a/modules/standard/recipe/recipe_controller.py b/modules/standard/recipe/recipe_controller.py index c27fb6d..184df96 100644 --- a/modules/standard/recipe/recipe_controller.py +++ b/modules/standard/recipe/recipe_controller.py @@ -50,7 +50,7 @@ class RecipeController: recipe = self.find_recipe(recipe_id, recipes) if recipe: recipes.remove(recipe) - if recipe.dt == dt: + if recipe.dt >= dt: continue self.update_recipe(recipe_dir, recipe_id, file_type, dt)