class BanController: def inject(self, registry): self.text = registry.get_instance("text") self.util = registry.get_instance("util") self.ban_service = registry.get_instance("ban_service") self.command_alias_service = registry.get_instance("command_alias_service") def start(self): self.command_alias_service.add_alias("unban", "ban rem") @command(command="ban", params=[Const("list", is_optional=True)], access_level="moderator", description="Show the ban list") def ban_list_cmd(self, request, _): t = int(time.time()) data = self.ban_service.get_ban_list() blob = "" for row in data: ends = "never" if row.finished_at == -1 else self.util.format_timestamp(row.finished_at) time_left = "" if row.finished_at == -1 else " (%s left)" % self.util.time_to_readable(row.finished_at - t) blob += "<pagebreak>Name: <highlight>%s<end>\n" % row.name blob += "Added: <highlight>%s<end>\n" % self.util.format_timestamp(row.created_at) blob += "By: <highlight>%s<end>\n" % row.sender_name blob += "Ends: <highlight>%s<end>%s\n" % (ends, time_left) blob += "Reason: <highlight>%s<end>\n\n" % row.reason return ChatBlob("Ban List (%d)" % len(data), blob) @command(command="ban", params=[Options(["rem", "remove"]), Character("character")], access_level="moderator", description="Remove a character from the ban list") def ban_remove_cmd(self, request, _, char): if not char.char_id: return "Could not find <highlight>%s<end>." % char.name elif not self.ban_service.get_ban(char.char_id): return "<highlight>%s<end> is not banned." % char.name else: self.ban_service.remove_ban(char.char_id) return "<highlight>%s<end> has been removed from the ban list." % char.name @command(command="ban", params=[Const("add", is_optional=True), Character("character"), Time("duration", is_optional=True), Any("reason", is_optional=True)], access_level="moderator", description="Add a character to the ban list") def ban_add_cmd(self, request, _, char, duration, reason): reason = reason or "" if not char.char_id: return "Could not find <highlight>%s<end>." % char.name elif self.ban_service.get_ban(char.char_id): return "<highlight>%s<end> is already banned." % char.name else: self.ban_service.add_ban(char.char_id, request.sender.char_id, duration, reason) return "<highlight>%s<end> has been added to the ban list." % char.name
class RunasController: def inject(self, registry): self.bot = registry.get_instance("bot") self.command_service = registry.get_instance("command_service") self.setting_service = registry.get_instance("setting_service") self.access_service = registry.get_instance("access_service") self.getresp = registry.get_instance( "translation_service").get_response @command(command="runas", params=[Character("character"), Any("command")], access_level="superadmin", description="Run a command as another character") def shutdown_cmd(self, request, char, command_str): if command_str[0] == self.setting_service.get("symbol").get_value(): command_str = command_str[1:] if not char.char_id: return self.getresp("global", "char_not_found", {"char": char.name}) elif not self.access_service.has_sufficient_access_level( request.sender.char_id, char.char_id): return self.getresp("module/system", "runas_fail", {"char": char.name}) else: self.command_service.process_command(command_str, request.channel, char.char_id, request.reply)
def start(self): self.db.exec( "CREATE TABLE IF NOT EXISTS name_history (char_id INT NOT NULL, name VARCHAR(20) NOT NULL, created_at INT NOT NULL, PRIMARY KEY (char_id, name))" ) self.command_alias_service.add_alias("w", "whois") self.command_alias_service.add_alias("lookup", "whois") self.discord_controller.register_discord_command_handler( self.whois_discord_cmd, "whois", [Character("character")])
class LastSeenController: def __init__(self): self.logger = Logger(__name__) def inject(self, registry): self.db = registry.get_instance("db") self.util = registry.get_instance("util") def start(self): self.db.exec( "CREATE TABLE IF NOT EXISTS last_seen (char_id INT NOT NULL PRIMARY KEY, " "dt INT NOT NULL DEFAULT 0)") @command( command="lastseen", params=[Character("character")], access_level="org_member", description="Show the last time an org member was online (on any alt)") def lastseen_cmd(self, request, char): sql = "SELECT p.*, a.group_id, a.status, l.dt FROM player p " \ "LEFT JOIN alts a ON p.char_id = a.char_id " \ "LEFT JOIN last_seen l ON p.char_id = l.char_id " \ "WHERE p.char_id = ? OR a.group_id = (SELECT group_id FROM alts WHERE char_id = ?) " \ "ORDER BY a.status DESC, p.level DESC, p.name ASC" data = self.db.query(sql, [char.char_id, char.char_id]) blob = "" if len(data) == 0: blob += f"Note: <highlight>{char.name}</highlight> must be on the buddylist in order for <highlight>lastseen</highlight> data to be recorded." else: for row in data: blob += f"<highlight>{row.name}</highlight>" if row.dt: blob += " last seen at " + self.util.format_datetime( row.dt) else: blob += " unknown" blob += "\n\n" return ChatBlob("Last Seen for %s (%d)" % (char.name, len(data)), blob) @event(event_type=BuddyService.BUDDY_LOGON_EVENT, description="Record last seen info") def handle_buddy_logon_event(self, event_type, event_data): self.update_last_seen(event_data.char_id) def update_last_seen(self, char_id): t = int(time.time()) if self.db.exec("UPDATE last_seen SET dt = ? WHERE char_id = ?", [t, char_id]) == 0: self.db.exec( "INSERT IGNORE INTO last_seen (char_id, dt) VALUES (?, ?)", [char_id, t]) def get_last_seen(self, char_id): return self.db.query_single( "SELECT dt FROM last_seen WHERE char_id = ?", [char_id])
def start(self): self.db.exec( "CREATE TABLE IF NOT EXISTS discord_char_link (discord_id BIGINT NOT NULL, char_id INT NOT NULL)" ) self.message_hub_service.register_message_destination( self.MESSAGE_SOURCE, self.handle_incoming_relay_message, [ "private_channel", "org_channel", "websocket_relay", "tell_relay", "shutdown_notice" ], [self.MESSAGE_SOURCE]) self.register_discord_command_handler( self.discord_link_cmd, "discord", [Const("link"), Character("ao_character")]) self.register_discord_command_handler(self.discord_unlink_cmd, "discord", [Const("unlink")]) self.ts.register_translation("module/discord", self.load_discord_msg) self.setting_service.register(self.module_name, "discord_enabled", False, BooleanSettingType(), "Enable the Discord relay") self.setting_service.register(self.module_name, "discord_bot_token", "", HiddenSettingType(allow_empty=True), "Discord bot token") self.setting_service.register( self.module_name, "discord_channel_id", "", TextSettingType(allow_empty=True), "Discord channel id for relaying messages to and from", "You can get the Discord channel ID by right-clicking on a channel name in Discord and then clicking \"Copy ID\"" ) self.setting_service.register(self.module_name, "discord_embed_color", "#00FF00", ColorSettingType(), "Discord embedded message color") self.setting_service.register( self.module_name, "relay_color_prefix", "#FCA712", ColorSettingType(), "Set the prefix color for messages coming from Discord") self.setting_service.register( self.module_name, "relay_color_name", "#808080", ColorSettingType(), "Set the color of the name for messages coming from Discord") self.setting_service.register( self.module_name, "relay_color_message", "#00DE42", ColorSettingType(), "Set the color of the content for messages coming from Discord") self.setting_service.register_change_listener( "discord_channel_id", self.update_discord_channel) self.setting_service.register_change_listener( "discord_enabled", self.update_discord_state)
class SendMessageController: def inject(self, registry): self.bot = registry.get_instance("bot") self.command_service = registry.get_instance("command_service") self.getresp = registry.get_instance("translation_service").get_response @command(command="sendtell", params=[Character("character"), Any("message")], access_level="superadmin", description="Send a tell to another character from the bot") def sendtell_cmd(self, request, char, message): if char.char_id: self.bot.send_private_message(char.char_id, message, add_color=False, conn=request.conn) return self.getresp("module/system", "msg_sent") else: return self.getresp("global", "char_not_found", {"char": char.name})
class CharacterHistoryController: def __init__(self): pass def inject(self, registry): self.bot = registry.get_instance("bot") self.util = registry.get_instance("util") self.character_history_service = registry.get_instance( "character_history_service") self.command_alias_service = registry.get_instance( "command_alias_service") def start(self): self.command_alias_service.add_alias("h", "history") @command( command="history", params=[Character("character"), Int("server_num", is_optional=True)], access_level="all", description="Get history of character") def handle_history_cmd1(self, request, char, server_num): server_num = server_num or self.bot.dimension data = self.character_history_service.get_character_history( char.name, server_num) if not data: return "Could not find history for <highlight>%s<end> on server <highlight>%d<end>." % ( char.name, server_num) return ChatBlob("History of %s (RK%d)" % (char.name, server_num), self.format_character_history(char.name, data)) def format_character_history(self, name, history): blob = "Date Level AI Faction Breed Guild (rank)\n" blob += "________________________________________________ \n" for row in history: if row.guild_name: org = "%s (%s)" % (row.guild_name, row.guild_rank_name) else: org = "" last_changed = self.util.format_date(int(float(row.last_changed))) blob += "%s | %s | <green>%s<end> | %s | %s | %s\n" % \ (last_changed, row.level, row.defender_rank or 0, row.faction, row.breed, org) blob += "\nHistory provided by Auno.org, Chrisax, and Athen Paladins" return blob
class LastSeenController: def __init__(self): self.logger = Logger(__name__) def inject(self, registry): self.db = registry.get_instance("db") self.util = registry.get_instance("util") self.character_service = registry.get_instance("character_service") self.public_channel_service = registry.get_instance( "public_channel_service") @command( command="lastseen", params=[Character("character")], access_level="admin", description="Show the last time an org member was online (on any alt)") def lastseen_cmd(self, request, char): if not self.public_channel_service.get_org_id: return "Error! Bot must be in an org in order to use this command." sql = """ SELECT p.*, o.last_seen FROM player p LEFT JOIN alts a ON p.char_id = a.char_id JOIN org_member o ON p.char_id = o.char_id WHERE o.last_seen != 0 AND ( p.char_id = ? OR a.group_id = (SELECT group_id FROM alts WHERE char_id = ?) ) ORDER BY o.last_seen DESC, p.name ASC, p.level DESC """ data = self.db.query(sql, [char.char_id, char.char_id]) blob = "" if len(data) == 0: blob += "Note: <highlight>%s<end> must be in the same organization as the bot in order to track <highlight>lastseen<end> data." % char.name for row in data: blob += "<highlight>%s<end> last seen at %s\n" % ( row.name, self.util.format_datetime(row.last_seen)) return ChatBlob("Last Seen for %s (%d)" % (char.name, len(data)), blob)
class IsOnlineController: BUDDY_IS_ONLINE_TYPE = "is" def __init__(self): self.waiting_for_update = {} def inject(self, registry): self.bot = registry.get_instance("bot") self.buddy_service = registry.get_instance("buddy_service") @command(command="is", params=[Character("character")], access_level="all", description="Show online status for a character") def is_cmd(self, request, char): if not char.char_id: return "Could not find <highlight>%s<end>." % char.name else: online_status = self.buddy_service.is_online(char.char_id) if online_status is None: self.bot.add_packet_handler(BuddyAdded.id, self.handle_buddy_status) self.waiting_for_update[char.char_id] = DictObject({ "char_id": char.char_id, "name": char.name, "reply": request.reply }) self.buddy_service.add_buddy(char.char_id, self.BUDDY_IS_ONLINE_TYPE) else: return self.format_message(char.name, online_status) def handle_buddy_status(self, packet): char = self.waiting_for_update.get(packet.char_id) if char: char.reply(self.format_message(char.name, packet.online)) self.buddy_service.remove_buddy(packet.char_id, self.BUDDY_IS_ONLINE_TYPE) del self.waiting_for_update[packet.char_id] if not self.waiting_for_update: self.bot.remove_packet_handler(BuddyAdded.id, self.handle_buddy_status) def format_message(self, char_name, online_status): return "%s is %s." % (char_name, "<green>online<end>" if online_status else "<red>offline<end>")
class SendMessageController: def inject(self, registry): self.bot = registry.get_instance("bot") self.command_service = registry.get_instance("command_service") @command(command="sendtell", params=[Character("character"), Any("message")], access_level="superadmin", description="Send a tell to another character from the bot") def sendtell_cmd(self, request, char, message): if char.char_id: self.bot.send_private_message(char.char_id, message) return "Your message has been sent." else: return "Could not find character <highlight>%s<end>." % char.name
class LeaderController: def __init__(self): self.leader = None def inject(self, registry): self.db: DB = registry.get_instance("db") self.text: Text = registry.get_instance("text") self.access_service = registry.get_instance("access_service") self.character_service = registry.get_instance("character_service") @command(command="leader", params=[], access_level="all", description="Show the current raidleader") def leader_show_command(self, request): if self.leader: return "The current raidleader is <highlight>%s<end>." % self.character_service.resolve_char_to_name(self.leader) else: return "There is no current raidleader." @command(command="leader", params=[Const("set")], access_level="all", description="Set yourself as raidleader") def leader_set_self_command(self, request, _): if self.leader == request.sender.char_id: self.leader = None return "You have been removed as raidleader." elif not self.leader: self.leader = request.sender.char_id return "You have been set as raidleader." elif self.access_service.has_sufficient_access_level(request.sender.char_id, self.leader): self.leader = request.sender.char_id return "You have taken leader from <highlight>%s<end>." % self.character_service.resolve_char_to_name(self.leader) else: return "You do not have a high enough access level to take raidleader from <highlight>%s<end>." % self.character_service.resolve_char_to_name(self.leader) @command(command="leader", params=[Const("set", is_optional=True), Character("character")], access_level="all", description="Set another character as raidleader") def leader_set_other_command(self, request, _, char): if not char.char_id: return "Could not find <highlight>%s<end>." % char.name if not self.leader or self.access_service.has_sufficient_access_level(request.sender.char_id, self.leader): self.leader = char.char_id return "<highlight>%s<end> has been set as raidleader." % char.name else: return "You do not have a high enough access level to take raidleader from <highlight>%s<end>." % self.character_service.resolve_char_to_name(self.leader)
class SendMessageController: def inject(self, registry): self.bot = registry.get_instance("bot") self.command_service = registry.get_instance("command_service") self.getresp = registry.get_instance( "translation_service").get_response @command(command="send", params=[Const("tell"), Character("character"), Any("message")], access_level="superadmin", description="Send a message to a character from the bot") def send_tell_cmd(self, request, _, char, message): if char.char_id: self.bot.send_private_message(char.char_id, message, add_color=False, conn=request.conn) return self.getresp("module/system", "msg_sent") else: return self.getresp("global", "char_not_found", {"char": char.name}) @command(command="send", params=[Const("org"), Any("message")], access_level="superadmin", description="Send a message to the org channel from the bot") def send_org_cmd(self, request, _, message): for _id, conn in self.bot.get_conns(lambda x: x.is_main): self.bot.send_org_message(message, add_color=False, conn=conn) return self.getresp("module/system", "msg_sent") @command(command="send", params=[Const("priv"), Any("message")], access_level="superadmin", description="Send a message to the private channel from the bot") def send_priv_cmd(self, request, _, message): self.bot.send_private_channel_message(message, add_color=False, conn=self.bot.get_primary_conn()) return self.getresp("module/system", "msg_sent")
class RunasController: def inject(self, registry): self.bot = registry.get_instance("bot") self.command_service = registry.get_instance("command_service") self.setting_service = registry.get_instance("setting_service") @command(command="runas", params=[Character("character"), Any("command")], access_level="superadmin", description="Run a command as another character") def shutdown_cmd(self, request, char, command_str): if command_str[0] == self.setting_service.get("symbol").get_value(): command_str = command_str[1:] if not char.char_id: return "Could not find character <highlight>%s<end>" % char.name else: self.command_service.process_command(command_str, request.channel, char.char_id, request.reply)
class RunasController: def inject(self, registry): self.command_service = registry.get_instance("command_service") self.access_service = registry.get_instance("access_service") @command(command="runas", params=[Character("character"), Any("command")], access_level="superadmin", description="Run a command as another character") def runas_cmd(self, request, char, command_str): if not char.char_id: return StandardMessage.char_not_found(char.name) elif not self.access_service.has_sufficient_access_level( request.sender.char_id, char.char_id): return f"Error! You must have a higher access level than <highlight>{char.name}</highlight>." else: command_str = self.command_service.trim_command_symbol(command_str) self.command_service.process_command(command_str, request.channel, char.char_id, request.reply, request.conn)
class CharacterHistoryController: def __init__(self): pass def inject(self, registry): self.bot = registry.get_instance("bot") self.text = registry.get_instance("text") self.util = registry.get_instance("util") self.character_history_service = registry.get_instance( "character_history_service") self.command_alias_service = registry.get_instance( "command_alias_service") def start(self): self.command_alias_service.add_alias("h", "history") @command( command="history", params=[Character("character"), Int("server_num", is_optional=True)], access_level="guest", description="Get history of character", extended_description= "Use server_num 6 for RK2019 and server_num 5 for live") def handle_history_cmd1(self, request, char, server_num): server_num = server_num or self.bot.dimension data = self.character_history_service.get_character_history( char.name, server_num) if not data: return "Could not find history for <highlight>%s</highlight> on server <highlight>%d</highlight>." % ( char.name, server_num) return ChatBlob( "History of %s (RK%d)" % (char.name, server_num), self.format_character_history(char.name, server_num, data)) def format_character_history(self, name, server_num, history): col_separator = " | " rows = [[ "Name", "Date", "Lvl", "AI", "Side", "Breed", "CharId", "Org (Rank)" ]] uniques = set() for row in history: if row.nickname and row.nickname != name: uniques.add(row.nickname) if row.char_id and row.char_id != name: uniques.add(row.char_id) if row.guild_name: org = "%s (%s)" % (row.guild_name, row.guild_rank_name) else: org = "" last_changed = self.util.format_date(int(float(row.last_changed))) current_row = [row.nickname, last_changed] if row.deleted == "1": # This value is output as string current_row.append("<red>DELETED</red>") else: current_row.extend([ row.level, "<green>%s</green>" % (row.defender_rank or "0"), row.faction, row.breed, row.char_id, org ]) rows.append(current_row) rows = self.text.pad_table(rows) blob = " ".join( map( lambda x: "[" + self.text.make_tellcmd( f"History {x}", f"history {x} {server_num}") + "]", uniques)) + "\n\n" blob += col_separator.join(rows[0]) + "\n" blob += "__________________________________________________________\n" for columns in rows[1:]: blob += col_separator.join(columns) + "\n" blob += "\nHistory provided by Auno.org, Chrisax, and Athen Paladins" return blob
class MemberController: MEMBER_BUDDY_TYPE = "member" MEMBER_LOGON_EVENT = "member_logon_event" MEMBER_LOGOFF_EVENT = "member_logoff_event" def inject(self, registry): self.db = registry.get_instance("db") self.private_channel_service = registry.get_instance("private_channel_service") self.buddy_service = registry.get_instance("buddy_service") self.bot = registry.get_instance("bot") self.access_service = registry.get_instance("access_service") self.command_alias_service = registry.get_instance("command_alias_service") self.event_service = registry.get_instance("event_service") def pre_start(self): self.access_service.register_access_level("member", 90, self.check_member) self.event_service.register_event_type(self.MEMBER_LOGON_EVENT) self.event_service.register_event_type(self.MEMBER_LOGOFF_EVENT) self.bot.add_packet_handler(BuddyAdded.id, self.handle_member_logon) def start(self): self.db.exec("CREATE TABLE IF NOT EXISTS members (char_id INT NOT NULL PRIMARY KEY, auto_invite INT DEFAULT 0);") self.command_alias_service.add_alias("adduser", "member add") self.command_alias_service.add_alias("remuser", "member rem") self.command_alias_service.add_alias("members", "member") @event(event_type="connect", description="Add members as buddies of the bot on startup") def handle_connect_event(self, event_type, event_data): for row in self.get_all_members(): if row.auto_invite == 1: self.buddy_service.add_buddy(row.char_id, self.MEMBER_BUDDY_TYPE) @command(command="member", params=[Const("add"), Character("character")], access_level=OrgMemberController.ORG_ACCESS_LEVEL, description="Add a member") def member_add_cmd(self, request, _, char): if char.char_id: if self.get_member(char.char_id): return "<highlight>%s<end> is already a member." % char.name else: self.add_member(char.char_id) return "<highlight>%s<end> has been added as a member." % char.name else: return "Could not find character <highlight>%s<end>." % char.name @command(command="member", params=[Options(["rem", "remove"]), Character("character")], access_level=OrgMemberController.ORG_ACCESS_LEVEL, description="Remove a member") def member_remove_cmd(self, request, _, char): if char.char_id: if self.get_member(char.char_id): self.remove_member(char.char_id) return "<highlight>%s<end> has been removed as a member." % char.name else: return "<highlight>%s<end> is not a member." % char.name else: return "Could not find character <highlight>%s<end>." % char.name @command(command="member", params=[Const("list", is_optional=True)], access_level=OrgMemberController.ORG_ACCESS_LEVEL, description="List members") def member_list_cmd(self, request, _): data = self.get_all_members() count = len(data) blob = "" for row in data: blob += str(row.name) + "\n" return ChatBlob("Members (%d)" % count, blob) @command(command="autoinvite", params=[Options(["on", "off"])], access_level="all", description="Set your auto invite preference") def autoinvite_cmd(self, request, pref): pref = pref.lower() member = self.get_member(request.sender.char_id) if member: self.update_auto_invite(request.sender.char_id, 1 if pref == "on" else 0) return "Your auto invite preference has been set to <highlight>%s<end>." % pref else: return "You must be a member of this bot to set your auto invite preference." @event(event_type=MEMBER_LOGON_EVENT, description="Auto invite members to the private channel when they logon") def handle_buddy_logon(self, event_type, event_data): if event_data.auto_invite == 1: self.bot.send_private_message(event_data.char_id, "You have been auto-invited to the private channel.") self.private_channel_service.invite(event_data.char_id) @event(event_type=BanService.BAN_ADDED_EVENT, description="Remove characters as members when they are banned") def ban_added_event(self, event_type, event_data): self.remove_member(event_data.char_id) def handle_member_logon(self, packet: BuddyAdded): member = self.get_member(packet.char_id) if member: if packet.online: self.event_service.fire_event(self.MEMBER_LOGON_EVENT, member) else: self.event_service.fire_event(self.MEMBER_LOGOFF_EVENT, member) def add_member(self, char_id, auto_invite=1): self.buddy_service.add_buddy(char_id, self.MEMBER_BUDDY_TYPE) if not self.get_member(char_id): self.db.exec("INSERT INTO members (char_id, auto_invite) VALUES (?, ?)", [char_id, auto_invite]) def remove_member(self, char_id): self.buddy_service.remove_buddy(char_id, self.MEMBER_BUDDY_TYPE) self.db.exec("DELETE FROM members WHERE char_id = ?", [char_id]) def update_auto_invite(self, char_id, auto_invite): self.db.exec("UPDATE members SET auto_invite = ? WHERE char_id = ?", [auto_invite, char_id]) def get_member(self, char_id): return self.db.query_single("SELECT char_id, auto_invite FROM members WHERE char_id = ?", [char_id]) def get_all_members(self): return self.db.query("SELECT COALESCE(p.name, m.char_id) AS name, m.char_id, m.auto_invite FROM members m LEFT JOIN player p ON m.char_id = p.char_id ORDER BY p.name ASC") def check_member(self, char_id): return self.get_member(char_id) is not None
class AdminController: def __init__(self): pass def inject(self, registry): self.bot = registry.get_instance("bot") self.admin_service = registry.get_instance("admin_service") self.pork_service = registry.get_instance("pork_service") self.command_alias_service = registry.get_instance( "command_alias_service") self.buddy_service = registry.get_instance("buddy_service") def start(self): self.command_alias_service.add_alias("adminlist", "admin") self.command_alias_service.add_alias("admins", "admin") @command(command="admin", params=[], access_level="all", description="Show the admin list") def admin_list_cmd(self, request): admins = self.admin_service.get_all() blob = "" current_access_level = "" for row in admins: if row.access_level != current_access_level: blob += "\n<header2>%s<end>\n" % row.access_level.capitalize() current_access_level = row.access_level blob += row.name if self.buddy_service.is_online(row.char_id): blob += " [<green>Online<end>]" blob += "\n" return ChatBlob("Admin List (%d)" % len(admins), blob) @command(command="admin", params=[Const("add"), Character("character")], access_level="superadmin", description="Add an admin", sub_command="modify") def admin_add_cmd(self, request, _, char): if not char.char_id: return "Could not find character <highlight>%s<end>." % char.name if self.admin_service.add(char.char_id, AdminService.ADMIN): return "Character <highlight>%s<end> added as <highlight>%s<end> successfully." % ( char.name, AdminService.ADMIN) else: return "Could not add character <highlight>%s<end> as <highlight>%s<end>." % ( char.name, AdminService.ADMIN) @command(command="admin", params=[Options(["remove", "rem"]), Character("character")], access_level="superadmin", description="Remove an admin", sub_command="modify") def admin_remove_cmd(self, request, _, char): if not char.char_id: return "Could not find character <highlight>%s<end>." % char.name if self.admin_service.remove(char.char_id): return "Character <highlight>%s<end> removed as <highlight>%s<end> successfully." % ( char.name, AdminService.ADMIN) else: return "Could not remove character <highlight>%s<end> as <highlight>%s<end>." % ( char.name, AdminService.ADMIN) @command(command="moderator", params=[Const("add"), Character("character")], access_level="admin", description="Add a moderator", sub_command="modify") def moderator_add_cmd(self, request, _, char): if not char.char_id: return "Could not find character <highlight>%s<end>." % char.name if self.admin_service.add(char.char_id, AdminService.MODERATOR): return "Character <highlight>%s<end> added as <highlight>%s<end> successfully." % ( char.name, AdminService.MODERATOR) else: return "Could not add character <highlight>%s<end> as <highlight>%s<end>." % ( char.name, AdminService.MODERATOR) @command(command="moderator", params=[Options(["remove", "rem"]), Character("character")], access_level="admin", description="Remove a moderator", sub_command="modify") def moderator_remove_cmd(self, request, _, char): if not char.char_id: return "Could not find character <highlight>%s<end>." % char.name if self.admin_service.remove(char.char_id): return "Character <highlight>%s<end> removed as <highlight>%s<end> successfully." % ( char.name, AdminService.MODERATOR) else: return "Could not remove character <highlight>%s<end> as <highlight>%s<end>." % ( char.name, AdminService.MODERATOR)
class AdminController: def __init__(self): pass def inject(self, registry): self.bot = registry.get_instance("bot") self.admin_service = registry.get_instance("admin_service") self.pork_service = registry.get_instance("pork_service") self.command_alias_service = registry.get_instance( "command_alias_service") self.buddy_service = registry.get_instance("buddy_service") def start(self): self.command_alias_service.add_alias("adminlist", "admin") self.command_alias_service.add_alias("admins", "admin") @command(command="admin", params=[], access_level="all", description="Show the admin list") def admin_list_cmd(self, request): admins = self.admin_service.get_all() blob = "" current_access_level = "" for row in admins: if row.access_level != current_access_level: blob += "\n<header2>%s</header2>\n" % row.access_level.capitalize( ) current_access_level = row.access_level if row.name: blob += row.name else: blob += "Unknown(%d)" % row.char_id if self.buddy_service.is_online(row.char_id): blob += " [<green>Online</green>]" blob += "\n" return ChatBlob("Admin List (%d)" % len(admins), blob) @command(command="admin", params=[Const("add"), Character("character")], access_level="superadmin", description="Add an admin", sub_command="modify") def admin_add_cmd(self, request, _, char): if not char.char_id: return StandardMessage.char_not_found(char.name) if self.admin_service.add(char.char_id, AdminService.ADMIN): return f"Character <highlight>{char.name}</highlight> added as <highlight>{AdminService.ADMIN}</highlight> successfully." else: return f"Could not add character <highlight>{char.name}</highlight> as <highlight>{AdminService.ADMIN}</highlight>." @command(command="admin", params=[Options(["remove", "rem"]), Character("character")], access_level="superadmin", description="Remove an admin", sub_command="modify") def admin_remove_cmd(self, request, _, char): if not char.char_id: return StandardMessage.char_not_found(char.name) if self.admin_service.remove(char.char_id): return f"Character <highlight>{char.name}</highlight> removed as <highlight>{AdminService.ADMIN}</highlight> successfully." else: return f"Could not remove character <highlight>{char.name}</highlight> as <highlight>{AdminService.ADMIN}</highlight>." @command(command="moderator", params=[Const("add"), Character("character")], access_level="admin", description="Add a moderator", sub_command="modify") def moderator_add_cmd(self, request, _, char): if not char.char_id: return StandardMessage.char_not_found(char.name) if self.admin_service.add(char.char_id, AdminService.MODERATOR): return f"Character <highlight>{char.name}</highlight> added as <highlight>{AdminService.MODERATOR}</highlight> successfully." else: return f"Could not add character <highlight>{char.name}</highlight> as <highlight>{AdminService.MODERATOR}</highlight>." @command(command="moderator", params=[Options(["remove", "rem"]), Character("character")], access_level="admin", description="Remove a moderator", sub_command="modify") def moderator_remove_cmd(self, request, _, char): if not char.char_id: return StandardMessage.char_not_found(char.name) if self.admin_service.remove(char.char_id): return f"Character <highlight>{char.name}</highlight> removed as <highlight>{AdminService.MODERATOR}</highlight> successfully." else: return f"Could not remove character <highlight>{char.name}</highlight> as <highlight>{AdminService.MODERATOR}</highlight>." @event(event_type="connect", description="Add admins as buddies", is_system=True) def connect_event(self, event_type, event_data): for row in self.admin_service.get_all(): self.buddy_service.add_buddy(row.char_id, "admin")
class PointsController: def __init__(self): pass def inject(self, registry): self.bot: Tyrbot = registry.get_instance("bot") self.db: DB = registry.get_instance("db") self.text: Text = registry.get_instance("text") self.character_service: CharacterService = registry.get_instance( "character_service") self.util: Util = registry.get_instance("util") self.setting_service: SettingService = registry.get_instance( "setting_service") self.alts_service: AltsService = registry.get_instance("alts_service") def start(self): if self.db.query_single( "SELECT COUNT(*) AS count FROM points_presets").count < 1: # Populate with pre-made presets if empty presets = [ "s13", "s28", "s35", "s42", "zodiac", "zod", "tnh", "beast", "12m", "tara", "pvp", "towers", "wipe", "clanwipe", "clan", "omniwipe", "omni", "bonus", "early" ] sql = "INSERT INTO points_presets (name) VALUES (?)" for preset in presets: self.db.exec(sql, [preset]) @setting(name="initial_points_value", value="0", description="How many points new accounts start with") def initial_points_value(self): return NumberSettingType() @command(command="account", params=[Const("create"), Character("char")], access_level="moderator", description="Create a new account for given character name", sub_command="modify") def bank_create_cmd(self, request, _, char: SenderObj): alts_info = self.alts_service.get_alts(char.char_id) for alt in alts_info: sql = "SELECT char_id, disabled FROM points WHERE char_id = ? LIMIT 1" count = self.db.query_single(sql, [alt.char_id]) if count: was_disabled = False if count.disabled == 1: if self.db.exec( "UPDATE points SET disabled = 0 WHERE char_id = ?", [alt.char_id]): was_disabled = True if alt.char_id == char.char_id: if was_disabled: if self.add_log_entry( alt.char_id, request.sender.char_id, "Account was re-enabled by %s" % self.character_service.resolve_char_to_name( request.sender.char_id)): return "<highlight>%s<end>'s account has been re-enabled." % char.name else: return "<highlight>%s<end> has an account, but failed to re-enable it." % char.name else: return "<highlight>%s<end> already has an account." % char.name else: if was_disabled: if self.add_log_entry( alt.char_id, request.sender.char_id, "Account was re-enabled by %s" % self.character_service.resolve_char_to_name( request.sender.char_id)): return "<highlight>%s<end>'s (%s) account has been re-enabled." % ( char.name, self.character_service.resolve_char_to_name( alt.char_id)) else: return "<highlight>%s<end> (%s) has an account, but failed to re-enable it." % ( char.name, self.character_service.resolve_char_to_name( alt.char_id)) else: return "<highlight>%s<end> (%s) already has an account." % ( char.name, self.character_service.resolve_char_to_name( alt.char_id)) main_info = alts_info.pop(0) changed_to_main = main_info.char_id == char.char_id initial_points = self.setting_service.get( "initial_points_value").get_value() sql = "INSERT INTO points (char_id, points, created) VALUES (?,?,?)" if self.db.exec(sql, [main_info.char_id, initial_points, int(time.time())]) < 1: return "Failed to create an account for <highlight>%s<end>." % char.name if not self.add_log_entry( main_info.char_id, request.sender.char_id, "Account opened by %s" % request.sender.name): sql = "DELETE FROM points WHERE char_id = ?" self.db.exec(sql, [main_info.char_id]) return "Failed to create an account for <highlight>%s<end>." % char.name name_reference = "%s (%s)" % ( char.name, self.character_service.resolve_char_to_name( main_info.char_id)) if changed_to_main else char.name return "A new account has been created for <highlight>%s<end>." % name_reference @command(command="account", params=[Const("close"), Character("char")], access_level="moderator", description="Close the account for given character name", sub_command="modify") def close_account_cmd(self, request, _, char: SenderObj): main_id = self.alts_service.get_main(char.char_id) sql = "UPDATE points SET disabled = 1 WHERE char_id = ?" if self.db.exec(sql, [main_id.char_id]) > 0: reason = "Account was closed by %s" % self.character_service.resolve_char_to_name( request.sender.char_id) if self.add_log_entry(main_id.char_id, request.sender.char_id, reason): name_reference = "%s (%s)" % ( char.name, self.character_service.resolve_char_to_name( main_id.char_id) ) if main_id.char_id != char.char_id else char.name return "<highlight>%s<end> has had their account disabled. Logs have been preserved." % name_reference return "<highlight>%s<end> does not have an open account." % char.name @command(command="account", params=[], access_level="all", description="Look up your account") def account_self_cmd(self, request): return self.get_account_display(request.sender) @command(command="account", params=[Const("logentry"), Int("log_id")], access_level="moderator", description="Look up specific log entry", sub_command="modify") def account_log_entry_cmd(self, _1, _2, log_id: int): log_entry = self.db.query_single( "SELECT * FROM points_log WHERE log_id = ?", [log_id]) if log_entry: char_name = self.character_service.resolve_char_to_name( log_entry.char_id) leader_name = self.character_service.resolve_char_to_name( log_entry.leader_id) blob = "Log entry ID: <highlight>%d<end>\n" % log_id blob += "Affecting account: <highlight>%s<end>\n" % char_name blob += "Action by: <highlight>%s<end>\n" % leader_name blob += "Type: <highlight>%s<end>\n" % ( "Management" if log_entry.audit == 0 else "Altering of points") blob += "Reason: <highlight>%s<end>\n" % log_entry.reason action_links = None if log_entry.audit == 0: if "closed" in log_entry.reason: action_links = self.text.make_chatcmd( "Open the account", "/tell <myname> account create %s" % char_name) elif "re-enabled" in log_entry.reason: action_links = self.text.make_chatcmd( "Close the account", "/tell <myname> account close %s" % char_name) else: if log_entry.audit < 0: reason = "Points from event (%d) has been retracted, %d points have been added." \ % (log_id, (-1*log_entry.audit)) action_links = self.text.make_chatcmd( "Retract", "/tell <myname> bank give %d %s %s" % ((-1 * log_entry.audit), char_name, reason)) else: reason = "Points from event (%d) has been retracted, %d points have been deducted." \ % (log_id, log_entry.audit) action_links = self.text.make_chatcmd( "Retract", "/tell <myname> bank take %d %s %s" % (log_entry.audit, char_name, reason)) blob += "Actions available: [%s]\n" % (action_links if action_links is not None else "No actions available") return ChatBlob("Log entry (%d)" % log_id, blob) return "No log entry with given ID <highlight>%d<end>." % log_id @command(command="account", params=[ Options(["give", "take"]), Int("amount"), Character("char"), Any("reason") ], access_level="moderator", description="Give or take points from character account", sub_command="modify") def account_give_take_cmd(self, request, action: str, amount: int, char: SenderObj, reason: str): main_id = self.alts_service.get_main(char.char_id) sql = "SELECT * FROM points WHERE char_id = ?" points = self.db.query_single(sql, [main_id.char_id]) if points: if points.disabled == 1: return "<highlight>%s<end>'s account is disabled, altering the account is not possible." % char.name if points.points == 0 and action == "take": return "<highlight>%s<end> has 0 points - can't have less than 0 points." % char.name if amount > points.points and action == "take": amount = points.points new_points = amount if action == "give" else 0 - amount if not self.alter_points(points.points, main_id.char_id, new_points, request.sender.char_id, reason): return "Failed to alter <highlight>%s<end>'s account." % char.name action = "taken from" if action == "take" else "added to" return "<highlight>%s<end> has had <highlight>%d<end> points %s their account." % ( char.name, amount, action) return "<highlight>%s<end> does not have an account." % char.name @command(command="account", params=[Character("char")], access_level="moderator", description="Look up account of another char", sub_command="modify") def account_other_cmd(self, request, char: SenderObj): return self.get_account_display(char) @command(command="presets", params=[Const("add"), Any("name"), Int("points")], access_level="admin", description="Add new points preset") def presets_add_cmd(self, _1, _2, name: str, points: int): count = self.db.query_single( "SELECT COUNT(*) AS count FROM points_presets WHERE name = ?", [name]).count if count > 0: return "A preset already exists with the name <highlight>%s<end>." % name sql = "INSERT INTO points_presets (name, points) VALUES (?,?)" if self.db.exec(sql, [name, points]) > 0: return "A preset with the name <highlight>%s<end> was added, worth <green>%d<end> points." % ( name, points) return "Failed to insert new preset in DB." @command(command="presets", params=[Const("rem"), Int("preset_id")], access_level="admin", description="Delete preset") def presets_rem_cmd(self, _1, _2, preset_id: int): if self.db.exec("DELETE FROM points_presets WHERE preset_id = ?", [preset_id]) > 0: return "Successfully removed preset with ID <highlight>%d<end>." % preset_id return "No preset with given ID <highlight>%d<end>." % preset_id @command(command="presets", params=[Const("alter"), Int("preset_id"), Int("new_points")], access_level="admin", description="Alter the points dished out by given preset") def presets_alter_cmd(self, _1, _2, preset_id: int, new_points: int): preset = self.db.query_single( "SELECT * FROM points_presets WHERE preset_id = ?", [preset_id]) if preset: if self.db.exec( "UPDATE points_presets SET points = ? WHERE preset_id = ?", [new_points, preset_id]) > 0: return "Successfully updated the preset, <highlight>%s<end>, to dish out " \ "<green>%d<end> points instead of <red>%d<end>." % (preset.name, new_points, preset.points) return "Failed to update preset with ID <highlight>%d<end>." % preset_id @command(command="presets", params=[], access_level="admin", description="See list of points presets") def presets_cmd(self, _): return ChatBlob("Points presets", self.build_preset_list()) def build_preset_list(self): presets = self.db.query( "SELECT * FROM points_presets ORDER BY name ASC, points DESC") if presets: blob = "" for preset in presets: add_points_link = self.text.make_chatcmd( "Add pts", "/tell <myname> raid addpts %s" % preset.name) delete_link = self.text.make_chatcmd( "Delete", "/tell <myname> presets rem %d" % preset.preset_id) blob += "<highlight>%s<end> worth <green>%d<end> points [id: %d]\n | [%s] [%s]\n\n" \ % (preset.name, preset.points, preset.preset_id, add_points_link, delete_link) return blob return "No presets available. To add new presets use <highlight><symbol>presets add preset_name preset_points<end>." def add_log_entry(self, char_id: int, leader_id: int, reason: str, amount=0): sql = "INSERT INTO points_log (char_id, audit, leader_id, reason, time) VALUES (?,?,?,?,?)" return self.db.exec( sql, [char_id, amount, leader_id, reason, int(time.time())]) > 0 def alter_points(self, current_points: int, char_id: int, amount: int, leader_id: int, reason: str): sql = "UPDATE points SET points = points + ? WHERE char_id = ?" if self.db.exec(sql, [amount, char_id]) < 1: return False if not self.add_log_entry(char_id, leader_id, reason, amount): sql = "UPDATE points p SET p.points = ? WHERE p.char_id = ?" self.db.exec(sql, [current_points, char_id]) return False return True def get_account(self, main_id): sql = "SELECT p.char_id, p.points, p.disabled FROM points p WHERE p.char_id = ?" return self.db.query_single(sql, [main_id]) def get_account_display(self, char: SenderObj): main = self.alts_service.get_main(char.char_id) if not main: return "Could not find character <highlight>%s<end>." % char.name points_log = self.db.query( "SELECT * FROM points_log WHERE char_id = ? ORDER BY time DESC LIMIT 50", [main.char_id]) points = self.db.query_single( "SELECT points, disabled FROM points WHERE char_id = ?", [main.char_id]) if not points: return "Could not find raid account for <highlight>%s<end>." % char.name alts_link = self.text.make_chatcmd( "Alts", "/tell <myname> alts %s" % main.name) blob = "" blob += "Holder of account: %s [%s]\n" % (main.name, alts_link) blob += "Points: %d\n" % points.points blob += "Status: %s\n\n" % ("<green>Open<end>" if points.disabled == 0 else "<red>Disabled<end>") blob += "<header2>Account log<end>\n" if points_log is None: blob += "No entries in log." else: for entry in points_log: name_reference = "<highlight>%s<end>" % char.name if entry.audit == 0: # If points is 0, then it's a general case log blob += "<grey>[%s]<end> <orange>\"%s\"<end>" % ( self.util.format_datetime(entry.time), entry.reason) elif entry.audit > 0: pts = "<green>%d<end>" % entry.audit blob += "<grey>[%s]<end> %s points were added to %s account " \ "by <highlight>%s<end> with reason <orange>%s<end>" \ % (self.util.format_datetime(entry.time), pts, name_reference, self.character_service.resolve_char_to_name(entry.leader_id), entry.reason) elif entry.audit < 0: pts = "<red>%d<end>" % (-1 * entry.audit) blob += "<grey>[%s]<end> %s points were taken from %s account " \ "by <highlight>%s<end> with reason <orange>%s<end>" \ % (self.util.format_datetime(entry.time), pts, name_reference, self.character_service.resolve_char_to_name(entry.leader_id), entry.reason) log_entry_link = self.text.make_chatcmd( "%d" % entry.log_id, "/tell <myname> account logentry %d" % entry.log_id) blob += " [%s]\n" % log_entry_link return ChatBlob("%s Account" % char.name, blob)
class AltsController: def inject(self, registry): self.bot = registry.get_instance("bot") self.alts_service = registry.get_instance("alts_service") self.buddy_service = registry.get_instance("buddy_service") self.ts: TranslationService = registry.get_instance("translation_service") self.getresp = self.ts.get_response def start(self): self.ts.register_translation("module/alts", self.load_alts_msg) def load_alts_msg(self): with open("modules/core/alts/alts.msg", mode="r", encoding="UTF-8") as f: return hjson.load(f) @command(command="alts", params=[], access_level="all", description="Show your alts") def alts_list_cmd(self, request): alts = self.alts_service.get_alts(request.sender.char_id) blob = self.format_alt_list(alts) return ChatBlob(self.getresp("module/alts", "list", {"char": alts[0].name, "amount": len(alts)}), blob) def get_alt_status(self, status): if status == AltsService.MAIN: return " - [main]" else: return "" @command(command="alts", params=[Const("setmain")], access_level="all", description="Set a new main", extended_description="You must run this from the character you want to be your new main") def alts_setmain_cmd(self, request, _): msg, result = self.alts_service.set_as_main(request.sender.char_id) if result: return self.getresp("module/alts", "new_main", {"char":request.sender.name}) elif msg == "not_an_alt": return self.getresp("module/alts", "not_an_alt") elif msg == "already_main": return self.getresp("module/alts", "already_main") else: raise Exception("Unknown msg: " + msg) @command(command="alts", params=[Const("add"), Character("character")], access_level="all", description="Add an alt") def alts_add_cmd(self, request, _, alt_char): if not alt_char.char_id: return self.getresp("global", "char_not_found", {"char":alt_char.name}) elif alt_char.char_id == request.sender.char_id: return self.getresp("module/alts", "add_fail_self") msg, result = self.alts_service.add_alt(request.sender.char_id, alt_char.char_id) if result: self.bot.send_private_message(alt_char.char_id, self.getresp("module/alts", "add_success_target", {"char": request.sender.name})) return self.getresp("module/alts", "add_success_self", {"char": alt_char.name}) elif msg == "another_main": return self.getresp("module/alts", "add_fail_already", {"char": alt_char.name}) else: raise Exception("Unknown msg: " + msg) @command(command="alts", params=[Options(["rem", "remove"]), Character("character")], access_level="all", description="Remove an alt") def alts_remove_cmd(self, request, _, alt_char): if not alt_char.char_id: return self.getresp("global", "char_not_found", {"char":alt_char.name}) msg, result = self.alts_service.remove_alt(request.sender.char_id, alt_char.char_id) if result: return self.getresp("module/alts", "rem_success", {"char": alt_char.name}) elif msg == "not_alt": return self.getresp("module/alts", "rem_fail_not", {"char": alt_char.name}) elif msg == "remove_main": return self.getresp("module/alts", "rem_fail_main") else: raise Exception("Unknown msg: " + msg) @command(command="alts", params=[Character("character")], access_level="member", description="Show alts of another character", sub_command="show") def alts_list_other_cmd(self, request, char): if not char.char_id: return self.getresp("global", "char_not_found", {"char":char.name}) alts = self.alts_service.get_alts(char.char_id) blob = self.format_alt_list(alts) return ChatBlob(self.getresp("module/alts", "alts_list", {"char": alts[0].name, "amount": len(alts)}), blob) def format_alt_list(self, alts): blob = "" for alt in alts: blob += "<highlight>%s<end> (%d/<green>%d<end>) %s %s" % (alt.name, alt.level, alt.ai_level, alt.faction, alt.profession) if self.buddy_service.is_online(alt.char_id): blob += " [<green>Online<end>]" blob += "\n" return blob
class UtilController: def inject(self, registry): self.bot = registry.get_instance("bot") self.db = registry.get_instance("db") self.util = registry.get_instance("util") self.command_service = registry.get_instance("command_service") self.buddy_service = registry.get_instance("buddy_service") self.access_service = registry.get_instance("access_service") self.event_service = registry.get_instance("event_service") self.public_channel_service = registry.get_instance( "public_channel_service") @command(command="checkaccess", params=[Character("character", is_optional=True)], access_level="all", description="Check access level for a character") def checkaccess_cmd(self, request, char): char = char or request.sender if not char.char_id: return "Could not find character <highlight>%s<end>." % char.name return "Access level for <highlight>%s<end> is <highlight>%s<end> (%s)." % \ (char.name, char.access_level["label"], self.access_service.get_single_access_level(char.char_id)["label"]) @command(command="macro", params=[Any("command1|command2|command3...")], access_level="all", description="Execute multiple commands at once") def macro_cmd(self, request, commands): commands = commands.split("|") for command_str in commands: self.command_service.process_command(command_str, request.channel, request.sender.char_id, request.reply) @command(command="echo", params=[Any("message")], access_level="all", description="Echo back a message") def echo_cmd(self, request, message): return html.escape(message) @command(command="showcommand", params=[Character("character"), Any("message")], access_level="admin", description="Show command output to another character") def showcommand_cmd(self, request, char, command_str): if not char.char_id: return "Could not find <highlight>%s<end>." % char.name self.bot.send_private_message( char.char_id, "<highlight>%s<end> is showing you output for command <highlight>%s<end>:" % (request.sender.name, command_str)) self.command_service.process_command( command_str, request.channel, request.sender.char_id, lambda msg: self.bot.send_private_message(char.char_id, msg)) return "Command <highlight>%s<end> output has been sent to <highlight>%s<end>." % ( command_str, char.name) @command(command="system", params=[], access_level="admin", description="Show system information") def system_cmd(self, request): blob = "" blob += "Version: <highlight>Tyrbot %s<end>\n" % self.bot.version blob += "Name: <highlight><myname><end>\n" blob += "\n" blob += "OS: <highlight>%s %s<end>\n" % (platform.system(), platform.release()) blob += "Python: <highlight>%d.%d.%d %s<end>\n" % ( sys.version_info.major, sys.version_info.minor, sys.version_info.micro, sys.version_info.releaselevel) blob += "Database: <highlight>%s<end>\n" % self.db.type blob += "Memory Usage: <highlight>%s KB<end>\n" % self.util.format_number( psutil.Process(os.getpid()).memory_info().rss / 1024) blob += "\n" blob += "Superadmin: <highlight>%s<end>\n" % self.bot.superadmin blob += "Buddy List: <highlight>%d / %d<end>\n" % (len( self.buddy_service.buddy_list), self.buddy_service.buddy_list_size) blob += "Uptime: <highlight>%s<end>\n" % self.util.time_to_readable( int(time.time()) - self.bot.start_time, max_levels=None) blob += "Dimension: <highlight>%s<end>\n" % self.bot.dimension blob += "\n" blob += "Org Id: <highlight>%s<end>\n" % self.public_channel_service.org_id blob += "Org Name: <highlight>%s<end>\n" % self.public_channel_service.org_name blob += "\n<pagebreak><header2>Public Channels<end>\n" for channel_id, name in self.public_channel_service.get_all_public_channels( ).items(): blob += "%s - <highlight>%d<end>\n" % (name, channel_id) blob += "\n<pagebreak><header2>Event Types<end>\n" for event_type in self.event_service.get_event_types(): blob += "%s\n" % event_type blob += "\n<pagebreak><header2>Access Levels<end>\n" for access_level in self.access_service.get_access_levels(): blob += "%s (%d)\n" % (access_level["label"], access_level["level"]) return ChatBlob("System Info", blob) @command( command="htmldecode", params=[Any("command")], access_level="all", description= "Decode html entities from a command before passing to the bot for execution" ) def htmldecode_cmd(self, request, command_str): self.command_service.process_command(html.unescape(command_str), request.channel, request.sender.char_id, request.reply)
class PrivateChannelController: RELAY_HUB_SOURCE = "private_channel" def inject(self, registry): self.bot = registry.get_instance("bot") self.private_channel_service = registry.get_instance( "private_channel_service") self.character_service = registry.get_instance("character_service") self.job_scheduler = registry.get_instance("job_scheduler") self.access_service = registry.get_instance("access_service") self.relay_hub_service = registry.get_instance("relay_hub_service") self.ban_service = registry.get_instance("ban_service") self.log_controller = registry.get_instance("log_controller") self.online_controller = registry.get_instance("online_controller") self.relay_controller = registry.get_instance("relay_controller") self.text: Text = registry.get_instance("text") self.ts: TranslationService = registry.get_instance( "translation_service") self.getresp = self.ts.get_response self.setting_service: SettingService = registry.get_instance( "setting_service") def start(self): self.relay_hub_service.register_relay( self.RELAY_HUB_SOURCE, self.handle_incoming_relay_message) self.ts.register_translation("module/private_channel", self.load_private_channel_msg) def load_private_channel_msg(self): with open("modules/core/private_channel/private_channel.msg", mode="r", encoding="utf-8") as f: return hjson.load(f) def handle_incoming_relay_message(self, ctx): message = ctx.message self.bot.send_private_channel_message(message, fire_outgoing_event=False) @setting( name="prefix_org", value="true", description= "Should the prefix [Org Tag] be displayed in relayed messages", ) def prefix_priv(self): return BooleanSettingType() @command(command="join", params=[], access_level="all", description="Join the private channel") def join_cmd(self, request): self.private_channel_service.invite(request.sender.char_id) @command(command="leave", params=[], access_level="all", description="Leave the private channel") def leave_cmd(self, request): self.private_channel_service.kick(request.sender.char_id) @command(command="invite", params=[Character("character")], access_level="all", description="Invite a character to the private channel") def invite_cmd(self, request, char): if char.char_id: if self.private_channel_service.in_private_channel(char.char_id): return self.getresp("module/private_channel", "invite_fail", {"target": char.name}) else: self.bot.send_private_message( char.char_id, self.getresp("module/private_channel", "invite_success_target", {"inviter": request.sender.name})) self.private_channel_service.invite(char.char_id) return self.getresp("module/private_channel", "invite_success_self", {"target": char.name}) else: return self.getresp("global", "char_not_found", {"char": char.name}) @command(command="kick", params=[Character("character")], access_level="admin", description="Kick a character from the private channel") def kick_cmd(self, request, char): if char.char_id: if not self.private_channel_service.in_private_channel( char.char_id): return self.getresp("module/private_channel", "kick_fail_not_in_priv", {"target": char.name}) else: # TODO use request.sender.access_level and char.access_level if self.access_service.has_sufficient_access_level( request.sender.char_id, char.char_id): self.bot.send_private_message( char.char_id, self.getresp("module/private_channel", "kick_success_target", {"kicker": request.sender.name})) self.private_channel_service.kick(char.char_id) return self.getresp("module/private_channel", "kick_success_self", {"target": char.name}) else: return self.getresp("module/private_channel", "kick_fail", {"target": char.name}) else: return self.getresp("global", "char_not_found", {"char": char.name}) @command(command="kickall", params=[], access_level="admin", description="Kick all characters from the private channel") def kickall_cmd(self, request): self.bot.send_private_channel_message( self.getresp("module/private_channel", "kick_all", {"char": request.sender.name})) self.job_scheduler.delayed_job( lambda t: self.private_channel_service.kickall(), 10) @event( event_type=BanService.BAN_ADDED_EVENT, description="Kick characters from the private channel who are banned", is_hidden=True) def ban_added_event(self, event_type, event_data): self.private_channel_service.kick(event_data.char_id) @event( event_type=PrivateChannelService.PRIVATE_CHANNEL_MESSAGE_EVENT, description="Relay messages from the private channel to the relay hub", is_hidden=True) def handle_private_channel_message_event(self, event_type, event_data): if event_data.char_id == self.bot.char_id or self.ban_service.get_ban( event_data.char_id): return char_name = self.character_service.resolve_char_to_name( event_data.char_id) sender = DictObject({"char_id": event_data.char_id, "name": char_name}) org = ( "[" + self.relay_controller.get_org_channel_prefix() + "]") if self.setting_service.get_value("prefix_org") == "1" else "" priv = "[Private]" char = self.text.make_charlink(char_name) + ": " message = self.getresp("module/private_channel", "relay_from_priv", { "org": org, "priv": priv, "char": char, "message": event_data.message }) self.relay_hub_service.send_message(self.RELAY_HUB_SOURCE, sender, message) @event(event_type=PrivateChannelService.JOINED_PRIVATE_CHANNEL_EVENT, description="Notify when a character joins the private channel") def handle_private_channel_joined_event(self, event_type, event_data): msg = self.getresp( "module/private_channel", "join", { "char": self.online_controller.get_char_info_display( event_data.char_id), "logon": self.log_controller.get_logon(event_data.char_id) }) self.bot.send_private_channel_message(msg) @event(event_type=PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT, description="Notify when a character leaves the private channel") def handle_private_channel_left_event(self, event_type, event_data): char_name = self.character_service.resolve_char_to_name( event_data.char_id) msg = self.getresp( "module/private_channel", "leave", { "char": char_name, "logoff": self.log_controller.get_logon(event_data.char_id) }) self.bot.send_private_channel_message(msg) @event( event_type=Tyrbot.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT, description="Relay commands from the private channel to the relay hub") def outgoing_private_channel_message_event(self, event_type, event_data): org = ( "[" + self.relay_controller.get_org_channel_prefix() + "]") if self.setting_service.get_value("prefix_org") == "1" else "" priv = "[Private]" if isinstance(event_data.message, ChatBlob): pages = self.text.paginate( ChatBlob(event_data.message.title, event_data.message.msg), self.setting_service.get( "org_channel_max_page_length").get_value()) if len(pages) < 4: for page in pages: message = self.getresp("module/private_channel", "relay_from_priv", { "org": org, "priv": priv, "message": page, "char": "" }) #message = "[%s][Private] %s" % (self.relay_controller.get_org_channel_prefix(), page) self.relay_hub_service.send_message( self.RELAY_HUB_SOURCE, DictObject({ "name": self.bot.char_name, "char_id": self.bot.char_id }), message) else: message = self.getresp( "module/private_channel", "relay_from_priv", { "org": org, "priv": priv, "message": event_data.message.title, "char": "" }) self.relay_hub_service.send_message( self.RELAY_HUB_SOURCE, DictObject({ "name": self.bot.char_name, "char_id": self.bot.char_id }), message) else: message = self.getresp( "module/private_channel", "relay_from_priv", { "org": org, "priv": priv, "message": event_data.message, "char": "" }) #message = "[%s][Private] %s" % (self.relay_controller.get_org_channel_prefix(), event_data.message) self.relay_hub_service.send_message( self.RELAY_HUB_SOURCE, DictObject({ "name": self.bot.char_name, "char_id": self.bot.char_id }), message)
class PrivateChannelController: MESSAGE_SOURCE = "private_channel" MESSAGE_SOURCE_UPDATE = "private_channel_update" def __init__(self): self.logger = Logger(__name__) self.private_channel_conn = None def inject(self, registry): self.bot = registry.get_instance("bot") self.private_channel_service = registry.get_instance( "private_channel_service") self.character_service = registry.get_instance("character_service") self.job_scheduler = registry.get_instance("job_scheduler") self.access_service = registry.get_instance("access_service") self.message_hub_service = registry.get_instance("message_hub_service") self.ban_service = registry.get_instance("ban_service") self.log_controller = registry.get_instance( "log_controller", is_optional=True) # TODO core module depending on standard module self.online_controller = registry.get_instance( "online_controller", is_optional=True) # TODO core module depending on standard module self.text: Text = registry.get_instance("text") self.setting_service: SettingService = registry.get_instance( "setting_service") def pre_start(self): self.message_hub_service.register_message_source(self.MESSAGE_SOURCE) self.message_hub_service.register_message_source( self.MESSAGE_SOURCE_UPDATE) def start(self): self.setting_service.register( self.module_name, "private_channel_prefix", "[Priv]", TextSettingType(["[Priv]", "[Guest]"]), "The name to show for messages coming from the private channel") self.setting_service.register( self.module_name, "private_channel_conn", "", TextSettingType(allow_empty=True), "The conn id or name to use for the private channel", extended_description= "If empty, the bot will use the primary conn. You MUST restart the bot after changing this value for the change to take effect." ) self.message_hub_service.register_message_destination( self.MESSAGE_SOURCE, self.handle_incoming_relay_message, [ "org_channel", "org_channel_update", "discord", "websocket_relay", "broadcast", "raffle", "shutdown_notice", "raid", "timers", "alliance" ], [self.MESSAGE_SOURCE, self.MESSAGE_SOURCE_UPDATE]) def handle_incoming_relay_message(self, ctx): self.bot.send_private_channel_message(ctx.formatted_message, conn=self.get_conn(None)) @command(command="join", params=[], access_level="member", description="Join the private channel") def join_cmd(self, request): self.private_channel_service.invite(request.sender.char_id, self.get_conn(request.conn)) @command(command="leave", params=[], access_level="all", description="Leave the private channel") def leave_cmd(self, request): self.private_channel_service.kick(request.sender.char_id, self.get_conn(request.conn)) @command(command="invite", params=[Character("character")], access_level="all", description="Invite a character to the private channel") def invite_cmd(self, request, char): if char.char_id: conn = self.get_conn(request.conn) if char.char_id in conn.private_channel: return f"<highlight>{char.name}</highlight> is already in the private channel." else: self.bot.send_private_message( char.char_id, f"You have been invited to the private channel by <highlight>{request.sender.name}</highlight>.", conn=conn) self.private_channel_service.invite(char.char_id, conn) return f"You have invited <highlight>{char.name}</highlight> to the private channel." else: return StandardMessage.char_not_found(char.name) @command(command="kick", params=[Character("character")], access_level="moderator", description="Kick a character from the private channel") def kick_cmd(self, request, char): if char.char_id: conn = self.get_conn(request.conn) if char.char_id not in conn.private_channel: return f"<highlight>{char.name}</highlight> is not in the private channel." else: # TODO use request.sender.access_level and char.access_level if self.access_service.has_sufficient_access_level( request.sender.char_id, char.char_id): self.bot.send_private_message( char.char_id, f"You have been kicked from the private channel by <highlight>{request.sender.name}</highlight>.", conn=conn) self.private_channel_service.kick(char.char_id, conn) return f"You have kicked <highlight>{char.name}</highlight> from the private channel." else: return f"You do not have the required access level to kick <highlight>{char.name}</highlight>." else: return StandardMessage.char_not_found(char.name) @command(command="kickall", params=[], access_level="moderator", description="Kick all characters from the private channel") def kickall_cmd(self, request): conn = self.get_conn(request.conn) self.bot.send_private_channel_message( f"Everyone will be kicked from this channel in 10 seconds. [by <highlight>{request.sender.name}</highlight>]", conn=conn) self.job_scheduler.delayed_job( lambda t: self.private_channel_service.kickall(conn), 10) @event(event_type="connect", description= "Load the conn ids as choice for private_channel_conn setting", is_system=True) def load_conns_into_setting_choice(self, event_type, event_data): options = [] for _id, conn in self.bot.get_conns(lambda x: x.is_main == True): options.append(conn.char_name) setting = self.setting_service.get("private_channel_conn") setting.options = options @event( event_type=BanService.BAN_ADDED_EVENT, description="Kick characters from the private channel who are banned", is_system=True) def ban_added_event(self, event_type, event_data): self.private_channel_service.kick_from_all(event_data.char_id) @event( event_type=PrivateChannelService.PRIVATE_CHANNEL_MESSAGE_EVENT, description="Relay messages from the private channel to the relay hub", is_system=True) def handle_private_channel_message_event(self, event_type, event_data): if self.bot.get_conn_by_char_id( event_data.char_id) or self.ban_service.get_ban( event_data.char_id): return sender = DictObject({ "char_id": event_data.char_id, "name": event_data.name }) self.message_hub_service.send_message( self.MESSAGE_SOURCE, sender, self.get_private_channel_prefix(), event_data.message) @event(event_type=PrivateChannelService.JOINED_PRIVATE_CHANNEL_EVENT, description="Notify when a character joins the private channel") def handle_private_channel_joined_event(self, event_type, event_data): if self.online_controller: char_info = self.online_controller.get_char_info_display( event_data.char_id, event_data.conn) else: char_info = self.character_service.resolve_char_to_name( event_data.char_id) msg = f"{char_info} has joined the private channel." if self.log_controller: msg += " " + self.log_controller.get_logon(event_data.char_id) self.bot.send_private_channel_message(msg, conn=event_data.conn) self.message_hub_service.send_message( self.MESSAGE_SOURCE_UPDATE, None, self.get_private_channel_prefix(), msg) @event(event_type=PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT, description="Notify when a character leaves the private channel") def handle_private_channel_left_event(self, event_type, event_data): msg = f"<highlight>{event_data.name}</highlight> has left the private channel." if self.log_controller: msg += " " + self.log_controller.get_logoff(event_data.char_id) self.bot.send_private_channel_message(msg, conn=event_data.conn) self.message_hub_service.send_message( self.MESSAGE_SOURCE_UPDATE, None, self.get_private_channel_prefix(), msg) @event( event_type=PrivateChannelService.PRIVATE_CHANNEL_COMMAND_EVENT, description="Relay commands from the private channel to the relay hub", is_system=True) def outgoing_private_channel_message_event(self, event_type, event_data): sender = None if event_data.name: sender = DictObject({ "char_id": event_data.char_id, "name": event_data.name }) if isinstance(event_data.message, ChatBlob): pages = self.text.paginate( ChatBlob(event_data.message.title, event_data.message.msg), event_data.conn, self.setting_service.get( "org_channel_max_page_length").get_value()) if len(pages) < 4: for page in pages: self.message_hub_service.send_message( self.MESSAGE_SOURCE, sender, self.get_private_channel_prefix(), page) else: self.message_hub_service.send_message( self.MESSAGE_SOURCE, sender, self.get_private_channel_prefix(), event_data.message.title) else: self.message_hub_service.send_message( self.MESSAGE_SOURCE, sender, self.get_private_channel_prefix(), event_data.message) def get_conn(self, conn): if self.private_channel_conn: return self.private_channel_conn conn_id = self.setting_service.get_value("private_channel_conn") if conn_id: for _id, conn in self.bot.get_conns( lambda x: x.id == conn_id or x.char_name == conn_id): self.private_channel_conn = conn break if not self.private_channel_conn: self.logger.warning( f"Could not find conn with id '{conn_id}', defaulting to primary conn" ) self.private_channel_conn = self.bot.get_primary_conn() else: # use the primary conn if private_channel_conn is not set self.private_channel_conn = self.bot.get_primary_conn() return self.private_channel_conn def get_private_channel_prefix(self): return self.setting_service.get_value("private_channel_prefix")
class RaidController: MESSAGE_SOURCE = "raid" NO_RAID_RUNNING_RESPONSE = "No raid is running." def __init__(self): self.raid: Raid = None def inject(self, registry): self.bot: Tyrbot = registry.get_instance("bot") self.db: DB = registry.get_instance("db") self.text: Text = registry.get_instance("text") self.setting_service: SettingService = registry.get_instance("setting_service") self.alts_service: AltsService = registry.get_instance("alts_service") self.buddy_service = registry.get_instance("buddy_service") self.character_service: CharacterService = registry.get_instance("character_service") self.private_channel_service = registry.get_instance("private_channel_service") self.points_controller: PointsController = registry.get_instance("points_controller") self.util: Util = registry.get_instance("util") self.message_hub_service = registry.get_instance("message_hub_service") self.leader_controller = registry.get_instance("leader_controller") self.topic_controller = registry.get_instance("topic_controller") self.member_controller = registry.get_instance("member_controller") def pre_start(self): self.message_hub_service.register_message_source(self.MESSAGE_SOURCE) self.db.exec("CREATE TABLE IF NOT EXISTS raid_log (raid_id INT PRIMARY KEY AUTO_INCREMENT, raid_name VARCHAR(255) NOT NULL, " "started_by BIGINT NOT NULL, raid_start INT NOT NULL, raid_end INT NOT NULL)") self.db.exec("CREATE TABLE IF NOT EXISTS raid_log_participants (raid_id INT NOT NULL, raider_id BIGINT NOT NULL, " "accumulated_points INT DEFAULT 0, left_raid INT, was_kicked INT, was_kicked_reason VARCHAR(500))") self.db.load_sql_file(self.module_dir + "/" + "raid_loot.sql") @command(command="raid", params=[], access_level="member", description="Show the current raid status") def raid_cmd(self, request): if not self.raid: return self.NO_RAID_RUNNING_RESPONSE t = int(time.time()) blob = "" blob += "Name: <highlight>%s</highlight>\n" % self.raid.raid_name blob += "Started By: <highlight>%s</highlight>\n" % self.raid.started_by.name blob += "Started At: <highlight>%s</highlight> (%s ago)\n" % (self.util.format_datetime(self.raid.started_at), self.util.time_to_readable(t - self.raid.started_at)) blob += "Status: %s" % ("<green>Open</green>" if self.raid.is_open else "<red>Closed</red>") if self.raid.is_open: blob += " (%s)" % self.text.make_tellcmd("Join", "raid join") blob += "\n\n" topic = self.topic_controller.get_topic() if topic: time_str = self.util.time_to_readable(int(time.time()) - topic["created_at"]) blob += "<header2>Orders</header2>\n" blob += "%s\n- <highlight>%s</highlight> %s ago\n\n" % (topic["topic_message"], topic["created_by"]["name"], time_str) blob += "<header2>Raiders</header2>\n" for raider in self.raid.raiders: if raider.is_active: blob += self.text.format_char_info(raider.get_active_char()) + "\n" return ChatBlob("Raid Status", blob) @command(command="raid", params=[Const("start"), Any("raid_name")], description="Start new raid", access_level="moderator", sub_command="manage") def raid_start_cmd(self, request, _, raid_name: str): if self.raid: return f"The raid <highlight>{self.raid.raid_name}</highlight> is already running." # if a leader is already set, only start raid if sender can take leader from current leader msg = self.leader_controller.set_raid_leader(request.sender, request.sender, request.conn) request.reply(msg) leader = self.leader_controller.get_leader(request.conn) if leader and leader.char_id != request.sender.char_id: return None self.raid = Raid(raid_name, request.sender) sql = "INSERT INTO raid_log (raid_name, started_by, raid_start, raid_end) VALUES (?,?,?,?)" self.db.exec(sql, [self.raid.raid_name, self.raid.started_by.char_id, self.raid.started_at, 0]) self.raid.raid_id = self.db.last_insert_id() leader_alts = self.alts_service.get_alts(request.sender.char_id) self.raid.raiders.append(Raider(leader_alts, request.sender.char_id)) join_link = self.text.paginate_single(ChatBlob("Click here", self.get_raid_join_blob()), request.conn) msg = "\n<highlight>----------------------------------------</highlight>\n" msg += "<highlight>%s</highlight> has started the raid <highlight>%s</highlight>.\n" % (request.sender.name, raid_name) msg += "%s to join\n" % join_link msg += "<highlight>----------------------------------------</highlight>" self.send_message(msg, request.conn) @command(command="raid", params=[Const("cancel")], description="Cancel the raid without saving/logging", access_level="moderator", sub_command="manage") def raid_cancel_cmd(self, request, _): if self.raid is None: return self.NO_RAID_RUNNING_RESPONSE self.send_message("<highlight>%s</highlight> canceled the raid <highlight>%s</highlight>." % (request.sender.name, self.raid.raid_name), request.conn) self.raid = None self.topic_controller.clear_topic() @command(command="raid", params=[Const("join")], description="Join the ongoing raid", access_level="member") def raid_join_cmd(self, request, _): if not self.raid: return self.NO_RAID_RUNNING_RESPONSE main_id = self.alts_service.get_main(request.sender.char_id).char_id in_raid = self.is_in_raid(main_id) if in_raid is not None: if in_raid.active_id == request.sender.char_id: if in_raid.is_active: return "You are already participating in the raid." else: if not self.raid.is_open: return "Raid is closed." in_raid.is_active = True in_raid.was_kicked = None in_raid.was_kicked_reason = None in_raid.left_raid = None self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Joined raid {self.raid.raid_name}") self.send_message("%s returned to actively participating in the raid." % request.sender.name, request.conn) elif in_raid.is_active: former_active_name = self.character_service.resolve_char_to_name(in_raid.active_id) in_raid.active_id = request.sender.char_id self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Switched to alt {request.sender.name} ({request.sender.char_id} in raid {self.raid.raid_name}") self.send_message("<highlight>%s</highlight> joined the raid with a different alt, <highlight>%s</highlight>." % (former_active_name, request.sender.name), request.conn) elif not in_raid.is_active: if not self.raid.is_open: return "Raid is closed." self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Switched to alt {request.sender.name} in raid {self.raid.raid_name}") self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Joined raid {self.raid.raid_name}") former_active_name = self.character_service.resolve_char_to_name(in_raid.active_id) in_raid.active_id = request.sender.char_id in_raid.was_kicked = None in_raid.was_kicked_reason = None in_raid.left_raid = None self.send_message("%s returned to actively participate with a different alt, <highlight>%s</highlight>." % (former_active_name, request.sender.name), request.conn) elif self.raid.is_open: alts = self.alts_service.get_alts(request.sender.char_id) self.raid.raiders.append(Raider(alts, request.sender.char_id)) self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Joined raid {self.raid.raid_name}") self.send_message("<highlight>%s</highlight> joined the raid." % request.sender.name, request.conn) if request.sender.char_id not in self.bot.get_primary_conn().private_channel: self.private_channel_service.invite(request.sender.char_id, self.bot.get_primary_conn()) else: return "Raid is closed." @command(command="raid", params=[Const("leave")], description="Leave the ongoing raid", access_level="member") def raid_leave_cmd(self, request, _): main_id = self.alts_service.get_main(request.sender.char_id).char_id in_raid = self.is_in_raid(main_id) if in_raid: if not in_raid.is_active: return "You are not active in the raid." in_raid.is_active = False in_raid.left_raid = int(time.time()) self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Left raid {self.raid.raid_name}") self.send_message("<highlight>%s</highlight> left the raid." % request.sender.name, request.conn) else: return "You are not in the raid." @command(command="raid", params=[Const("addpts"), Any("name")], description="Add points to all active participants", access_level="moderator", sub_command="manage") def points_add_cmd(self, request, _, name: str): if not self.raid: return self.NO_RAID_RUNNING_RESPONSE preset = self.db.query_single("SELECT name, points FROM points_presets WHERE name = ?", [name]) if not preset: return ChatBlob("No such preset - see list of presets", self.points_controller.build_preset_list()) self.raid.added_points = True for raider in self.raid.raiders: account = self.points_controller.get_account(raider.main_id, request.conn) if raider.is_active: if account.disabled == 0: self.points_controller.alter_points(raider.main_id, request.sender.char_id, preset.name, preset.points) raider.accumulated_points += preset.points else: self.points_controller.add_log_entry(raider.main_id, request.sender.char_id, "Participated in raid with a disabled account, missed points from %s." % preset.name) else: self.points_controller.add_log_entry(raider.main_id, request.sender.char_id, "Was inactive during raid, %s, when points for %s were dished out." % (self.raid.raid_name, preset.name)) self.send_message("<highlight>%d</highlight> points added to all active raiders for <highlight>%s</highlight>." % (preset.points, preset.name), request.conn) @command(command="raid", params=[Const("active")], description="Get a list of raiders to do active check", access_level="moderator", sub_command="manage") def raid_active_cmd(self, request, _): if not self.raid: return self.NO_RAID_RUNNING_RESPONSE blob = "" count = 0 raider_names = [] for raider in self.raid.raiders: if count == 10: active_check_names = "/assist " active_check_names += "\\n /assist ".join(raider_names) blob += "\n[<a href='chatcmd://%s'>Active check</a>]\n\n" % active_check_names count = 0 raider_names.clear() raider_name = self.character_service.resolve_char_to_name(raider.active_id) akick_link = self.text.make_tellcmd("Active kick", "raid kick %s inactive" % raider_name) warn_link = self.text.make_chatcmd("Warn", "/tell %s You missed active check, please give notice." % raider_name) blob += "<highlight>%s</highlight> [%s] [%s]\n" % (raider_name, akick_link, warn_link) raider_names.append(raider_name) count += 1 if len(raider_names) > 0: active_check_names = "/assist " active_check_names += "\\n /assist ".join(raider_names) blob += "\n[<a href='chatcmd://%s'>Active check</a>]\n\n" % active_check_names raider_names.clear() return ChatBlob("Active check", blob) @command(command="raid", params=[Const("add"), Character("char")], description="Add a character to the raid", access_level="moderator", sub_command="manage") def raid_add_cmd(self, request, _, char): if self.raid is None: return self.NO_RAID_RUNNING_RESPONSE alts = self.alts_service.get_alts(char.char_id) main_id = alts[0].char_id in_raid = self.is_in_raid(main_id) if in_raid is None: self.raid.raiders.append(Raider(alts, char.char_id)) self.bot.send_private_message(char.char_id, f"You have been added to the raid <highlight>{self.raid.raid_name}</highlight>.", conn=request.conn) self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Added to raid {self.raid.raid_name}") if char.char_id not in self.bot.get_primary_conn().private_channel: self.private_channel_service.invite(char.char_id) return "<highlight>%s</highlight> has been added to the raid." % char.name else: if not in_raid.is_active: in_raid.is_active = True in_raid.was_kicked = None in_raid.was_kicked_reason = None in_raid.left_raid = None self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Added to raid {self.raid.raid_name}") self.bot.send_private_message(char.char_id, f"You have been set as active in the raid <highlight>{self.raid.raid_name}</highlight>.", conn=request.conn) return f"<highlight>{char.name}</highlight> has been set as active." else: return f"<highlight>{char.name}</highlight> is already in the raid." @command(command="raid", params=[Const("kick"), Character("char"), Any("reason")], description="Set raider as kicked with a reason", access_level="moderator", sub_command="manage") def raid_kick_cmd(self, request, _, char: SenderObj, reason: str): if self.raid is None: return self.NO_RAID_RUNNING_RESPONSE main_id = self.alts_service.get_main(char.char_id).char_id in_raid = self.is_in_raid(main_id) if in_raid is not None: if not in_raid.is_active: return "<highlight>%s</highlight> is already set as inactive." % char.name in_raid.is_active = False in_raid.was_kicked = int(time.time()) in_raid.was_kicked_reason = reason self.points_controller.add_log_entry(main_id, request.sender.char_id, f"Kicked from raid {self.raid.raid_name} with reason: {reason}") self.bot.send_private_message(char.char_id, f"You have been kicked from raid <highlight>{self.raid.raid_name}</highlight> with reason <highlight>{reason}</highlight>.", conn=request.conn) return "<highlight>%s</highlight> has been kicked from the raid with reason <highlight>%s</highlight>." % (char.name, reason) else: return "<highlight>%s</highlight> is not participating." % char.name @command(command="raid", params=[Options(["open", "unlock"])], description="Open raid for new participants", access_level="moderator", sub_command="manage") def raid_open_cmd(self, request, action): if not self.raid: return self.NO_RAID_RUNNING_RESPONSE if self.raid.is_open: return "Raid is already open." else: self.raid.is_open = True self.send_message("Raid has been opened by %s." % request.sender.name, request.conn) @command(command="raid", params=[Options(["close", "lock"])], description="Close raid for new participants", access_level="moderator", sub_command="manage") def raid_close_cmd(self, request, action): if not self.raid: return self.NO_RAID_RUNNING_RESPONSE if self.raid.is_open: self.raid.is_open = False self.send_message("Raid has been closed by %s." % request.sender.name, request.conn) else: return "Raid is already closed." @command(command="raid", params=[Options(["end", "save"]), NamedFlagParameters(["force"])], description="End raid, and log results", access_level="moderator", sub_command="manage") def raid_save_cmd(self, request, _, flag_params): if not self.raid: return self.NO_RAID_RUNNING_RESPONSE if not self.raid.added_points and not flag_params.force: blob = "You have not added any points for this raid. Are you sure you want to end this raid now? " blob += self.text.make_tellcmd("Yes", "raid end --force") return ChatBlob("End Raid Confirmation", blob) sql = "UPDATE raid_log SET raid_end = ? WHERE raid_id = ?" self.db.exec(sql, [int(time.time()), self.raid.raid_id]) for raider in self.raid.raiders: sql = "INSERT INTO raid_log_participants (raid_id, raider_id, accumulated_points, left_raid, was_kicked, was_kicked_reason) VALUES (?,?,?,?,?,?)" self.db.exec(sql, [self.raid.raid_id, raider.active_id, raider.accumulated_points, raider.left_raid, raider.was_kicked, raider.was_kicked_reason]) self.raid = None self.topic_controller.clear_topic() self.send_message("Raid saved and ended.", request.conn) @command(command="raid", params=[Const("history"), Int("raid_id")], description="Show log entry for raid", access_level="moderator", sub_command="manage") def raid_history_detail_cmd(self, request, _, raid_id: int): sql = "SELECT r.*, p.*, p2.name AS raider_name FROM raid_log r " \ "LEFT JOIN raid_log_participants p ON r.raid_id = p.raid_id " \ "LEFT JOIN player p2 ON p.raider_id = p2.char_id " \ "WHERE r.raid_id = ? ORDER BY p.accumulated_points DESC" log_entry = self.db.query(sql, [raid_id]) if not log_entry: return "No such log entry." blob = "Raid name: <highlight>%s</highlight>\n" % log_entry[0].raid_name blob += "Started by: <highlight>%s</highlight>\n" % self.character_service.resolve_char_to_name(log_entry[0].started_by) blob += "Start time: <highlight>%s</highlight>\n" % self.util.format_datetime(log_entry[0].raid_start) blob += "End time: <highlight>%s</highlight>\n" % self.util.format_datetime(log_entry[0].raid_end) blob += "Run time: <highlight>%s</highlight>\n" % self.util.time_to_readable(log_entry[0].raid_end - log_entry[0].raid_start) pts_sum = self.db.query_single("SELECT COALESCE(SUM(p.accumulated_points), 0) AS sum FROM raid_log_participants p WHERE p.raid_id = ?", [raid_id]).sum blob += "Total points: <highlight>%d</highlight>\n\n" % pts_sum blob += "<header2>Participants</header2>\n" for raider in log_entry: main_info = self.alts_service.get_main(raider.raider_id) if main_info.char_id != raider.raider_id: alt_link_text = "Alt of %s" % main_info.name else: alt_link_text = "Alts" alt_link = self.text.make_tellcmd(alt_link_text, "alts %s" % raider.raider_name) account_link = self.text.make_tellcmd("Account", "account %s" % raider.raider_name) blob += "%s - %d points earned [%s] [%s]\n" % (raider.raider_name, raider.accumulated_points, account_link, alt_link) if raider.left_raid: blob += "Left raid: %s\n" % self.util.format_datetime(raider.left_raid) if raider.was_kicked: blob += "Was kicked: %s\n" % self.util.format_datetime(raider.was_kicked) if raider.was_kicked_reason: blob += "Kick reason: %s\n" % raider.was_kicked_reason blob += "\n" return ChatBlob("Raid: %s" % log_entry[0].raid_name, blob) @command(command="raid", params=[Const("history")], description="Show a list of recent raids", access_level="member") def raid_history_cmd(self, request, _): sql = "SELECT * FROM raid_log ORDER BY raid_end DESC LIMIT 30" raids = self.db.query(sql) blob = "" for raid in raids: participant_link = self.text.make_tellcmd("Detail", "raid history %d" % raid.raid_id) timestamp = self.util.format_datetime(raid.raid_start) leader_name = self.character_service.resolve_char_to_name(raid.started_by) blob += "[%d] [%s] <highlight>%s</highlight> started by <highlight>%s</highlight> [%s]\n" % (raid.raid_id, timestamp, raid.raid_name, leader_name, participant_link) return ChatBlob("Raid History (%d)" % len(raids), blob) @command(command="raid", params=[Const("announce"), Any("message", is_optional=True)], access_level="moderator", sub_command="manage", description="Announce the current raid to members") def raid_announce_cmd(self, request, _, message): if not self.raid: return self.NO_RAID_RUNNING_RESPONSE if not self.bot.mass_message_queue: return "Could not announce raid since bot does not have mass messaging capabilities." join_link = self.text.paginate_single(ChatBlob("Click here", self.get_raid_join_blob()), request.conn) msg = "<highlight>%s</highlight> has started the raid <highlight>%s</highlight>. " % (self.raid.started_by.name, self.raid.raid_name) msg += "%s to join." % join_link if message: msg += " " + message count = 0 for member in self.member_controller.get_all_members(): main = self.alts_service.get_main(member.char_id) if self.buddy_service.is_online(member.char_id) and not self.is_in_raid(main.char_id): count += 1 self.bot.send_mass_message(member.char_id, msg, conn=request.conn) return f"Raid announcement is sending to <highlight>{count}</highlight> online members." def is_in_raid(self, main_id: int): if self.raid is None: return None for raider in self.raid.raiders: if raider.main_id == main_id: return raider def get_raid_join_blob(self): return "<header2>1. Join the raid</header2>\n" \ "To join the current raid <highlight>%s</highlight>, send the following tell to <myname>\n" \ "<tab><tab><a href='chatcmd:///tell <myname> <symbol>raid join'>/tell <myname> raid " \ "join</a>\n\n<header2>2. Enable LFT</header2>\nWhen you have joined the raid, go lft " \ "with \"<myname>\" as description\n<tab><tab><a href='chatcmd:///lft <myname>'>/lft <myname></a>\n\n" \ "<header2>3. Announce</header2>\nYou could announce to the raid leader, that you have enabled " \ "LFT\n<tab><tab><a href='chatcmd:///group <myname> I am on lft'>Announce</a> that you have enabled " \ "lft\n\n<header2>4. Rally with yer mateys</header2>\nFinally, move towards the starting location of " \ "the raid.\n<highlight>Ask for help</highlight> if you're in doubt of where to go." % self.raid.raid_name def send_message(self, msg, conn): # TODO remove once messagehub can handle ChatBlobs pages = self.bot.get_text_pages(msg, conn, self.setting_service.get("private_message_max_page_length").get_value()) for page in pages: self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, None, page)
class AdminController: def __init__(self): pass def inject(self, registry): self.bot = registry.get_instance("bot") self.admin_service = registry.get_instance("admin_service") self.pork_service = registry.get_instance("pork_service") self.command_alias_service = registry.get_instance( "command_alias_service") self.buddy_service = registry.get_instance("buddy_service") self.ts: TranslationService = registry.get_instance( "translation_service") self.getresp = self.ts.get_response def start(self): self.command_alias_service.add_alias("adminlist", "admin") self.command_alias_service.add_alias("admins", "admin") self.ts.register_translation("module/admin", self.load_admin_msg) def load_admin_msg(self): with open("modules/core/admin/admin.msg", mode="r", encoding="UTF-8") as f: return hjson.load(f) @command(command="admin", params=[], access_level="all", description="Show the admin list") def admin_list_cmd(self, request): admins = self.admin_service.get_all() blob = "" current_access_level = "" for row in admins: if row.access_level != current_access_level: blob += "\n<header2>%s</header2>\n" % row.access_level.capitalize( ) current_access_level = row.access_level blob += row.name if self.buddy_service.is_online(row.char_id): blob += " [<green>Online</green>]" blob += "\n" return ChatBlob("Admin List (%d)" % len(admins), blob) @command(command="admin", params=[Const("add"), Character("character")], access_level="superadmin", description="Add an admin", sub_command="modify") def admin_add_cmd(self, request, _, char): if not char.char_id: return self.getresp("global", "char_not_found", {"char": char.name}) if self.admin_service.add(char.char_id, AdminService.ADMIN): return self.getresp("module/admin", "add_success", { "char": char.name, "rank": AdminService.ADMIN }) else: return self.getresp("module/admin", "add_fail", { "char": char.name, "rank": AdminService.ADMIN }) @command(command="admin", params=[Options(["remove", "rem"]), Character("character")], access_level="superadmin", description="Remove an admin", sub_command="modify") def admin_remove_cmd(self, request, _, char): if not char.char_id: return self.getresp("global", "char_not_found", {"char": char.name}) if self.admin_service.remove(char.char_id): return self.getresp("module/admin", "rem_success", { "char": char.name, "rank": AdminService.ADMIN }) else: return self.getresp("module/admin", "rem_fail", { "char": char.name, "rank": AdminService.ADMIN }) @command(command="moderator", params=[Const("add"), Character("character")], access_level="admin", description="Add a moderator", sub_command="modify") def moderator_add_cmd(self, request, _, char): if not char.char_id: return self.getresp("global", "char_not_found", {"char": char.name}) if self.admin_service.add(char.char_id, AdminService.MODERATOR): return self.getresp("module/admin", "add_success", { "char": char.name, "rank": AdminService.MODERATOR }) else: return self.getresp("module/admin", "add_fail", { "char": char.name, "rank": AdminService.MODERATOR }) @command(command="moderator", params=[Options(["remove", "rem"]), Character("character")], access_level="admin", description="Remove a moderator", sub_command="modify") def moderator_remove_cmd(self, request, _, char): if not char.char_id: return self.getresp("global", "char_not_found", {"char": char.name}) if self.admin_service.remove(char.char_id): return self.getresp("module/admin", "rem_success", { "char": char.name, "rank": AdminService.MODERATOR }) else: return self.getresp("module/admin", "rem_fail", { "char": char.name, "rank": AdminService.MODERATOR })
class LeaderController: NOT_LEADER_MSG = "Error! You must be raid leader, or have higher access " \ "level than the raid leader to use this command." def __init__(self): self.leader = None self.last_activity = None self.echo = False def inject(self, registry): self.db: DB = registry.get_instance("db") self.text: Text = registry.get_instance("text") self.access_service: AccessService = registry.get_instance( "access_service") self.character_service: CharacterService = registry.get_instance( "character_service") self.bot: Tyrbot = registry.get_instance("bot") self.setting_service: SettingService = registry.get_instance( "setting_service") @setting( name="leader_echo_color", value="#00FF00", description="Color with which the leader's messages will be echoed with" ) def leader_echo_color(self): return ColorSettingType() @setting(name="leader_auto_echo", value=True, description= "If turned on, when someone assume the leader role, leader echo " "will automatically be activated for said person") def leader_auto_echo(self): return BooleanSettingType() @command(command="leader", params=[], access_level="all", description="Show the current raid leader") def leader_show_command(self, _): if self.leader: return "The current raid leader is <highlight>%s<end>." % self.leader.name else: return "There is no current raid leader. Use <highlight><symbol>leader set<end> to become the raid leader." @command( command="leader", params=[Const("echo"), Options(["on", "off"])], access_level="all", description= "Echo whatever the current leader types in channel, in a distinctive color" ) def leader_echo_command(self, request, _2, switch_to): if self.leader: if self.can_use_command(request.sender.char_id): self.echo = switch_to == "on" return "Leader echo for <highlight>%s<end> has been turned <highlight>%s<end>." % \ (self.leader.name, switch_to) else: return "Insufficient access level." elif self.leader is None and switch_to == "on": return "No current leader set, can't turn on leader echo." @command(command="leader", params=[Const("echo")], access_level="all", description="See the current status for leader echoing") def leader_echo_status_command(self, _1, _2): if self.leader: on_off = "on" if self.echo else "off" return "<highlight>%s<end> is set as leader, leader echo is <highlight>%s<end>" % \ (self.leader.name, on_off) return "No current leader set." @command(command="leader", params=[Const("clear")], access_level="all", description="Clear the current raid leader") def leader_clear_command(self, request, _): return self.set_raid_leader(request.sender, None) @command(command="leader", params=[Const("set")], access_level="all", description="Set (or unset) yourself as raid leader") def leader_set_self_command(self, request, _): return self.set_raid_leader(request.sender, request.sender) @command(command="leader", params=[Const("set", is_optional=True), Character("character")], access_level="all", description="Set another character as raid leader") def leader_set_other_command(self, request, _, char): if not char.char_id: return "Could not find <highlight>%s<end>." % char.name return self.set_raid_leader(request.sender, char) @timerevent( budatime="1h", description= "Remove raid leader if raid leader hasn't been active for more than 1 hour" ) def leader_auto_remove(self, _1, _2): if self.last_activity: if self.last_activity - int(time.time()) > 3600: self.leader = None self.last_activity = None self.echo = False self.bot.send_private_channel_message( "Raid leader has been automatically " "cleared because of inactivity.") self.bot.send_org_message( "Raid leader has been automatically cleared because of inactivity." ) @event(PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT, "Remove raid leader if raid leader leaves private channel") def leader_remove_on_leave_private(self, _, event_data): if self.leader: if self.leader.char_id == event_data.char_id: self.leader = None self.last_activity = None self.echo = False self.bot.send_private_channel_message( "%s left private channel, and has been cleared as raid leader." % self.character_service.resolve_char_to_name( event_data.char_id)) @event(OrgMemberController.ORG_MEMBER_LOGOFF_EVENT, "Remove raid leader if raid leader logs off") def leader_remove_on_logoff(self, _, event_data): if self.leader: if self.leader.char_id == event_data.char_id: self.leader = None self.last_activity = None self.echo = False self.bot.send_org_message( "%s has logged off, and has been cleared as raid leader." % self.character_service.resolve_char_to_name( event_data.char_id)) @event(PrivateChannelService.PRIVATE_CHANNEL_MESSAGE_EVENT, "Echo leader messages from private channel", is_hidden=True) def leader_echo_private_event(self, _, event_data): if self.leader and self.echo: if self.leader.char_id == event_data.char_id: if self.setting_service.get( "symbol").get_value() != event_data.message[0]: self.leader_echo(event_data.char_id, event_data.message, "priv") @event(PublicChannelService.ORG_CHANNEL_MESSAGE_EVENT, "Echo leader messages from org channel", is_hidden=True) def leader_echo_org_event(self, _, event_data): if self.leader and self.echo: if self.leader.char_id == event_data.char_id: if self.setting_service.get( "symbol").get_value() != event_data.message[0]: self.leader_echo(event_data.char_id, event_data.message, "org") def leader_echo(self, char_id, message, channel): sender = self.character_service.resolve_char_to_name(char_id) color = self.setting_service.get("leader_echo_color").get_value() if channel == "org": self.bot.send_org_message("%s: <font color=%s>%s" % (sender, color, message)) if channel == "priv": self.bot.send_private_channel_message("%s: <font color=%s>%s" % (sender, color, message)) self.activity_done() def activity_done(self): self.last_activity = int(time.time()) def can_use_command(self, char_id): if not self.leader or self.access_service.has_sufficient_access_level( char_id, self.leader.char_id): self.activity_done() return True return False def set_raid_leader(self, sender, set_to): if set_to is None: if not self.leader: return "There is no current raid leader." elif self.leader.char_id == sender.char_id: self.leader = None self.echo = False return "You have been removed as raid leader." elif self.can_use_command(sender.char_id): old_leader = self.leader self.leader = None self.echo = False self.bot.send_private_message( old_leader.char_id, "You have been removed as raid leader by <highlight>%s<end>." % sender.name) return "You have removed <highlight>%s<end> as raid leader." % old_leader.name else: return "You do not have a high enough access level to remove raid leader from <highlight>%s<end>." % \ self.leader.name elif sender.char_id == set_to.char_id: if not self.leader: self.leader = sender self.echo = self.setting_service.get( "leader_auto_echo").get_value() reply = "You have been set as raid leader." if self.echo: reply += " Leader echo is <green>enabled<end>." return reply elif self.leader.char_id == sender.char_id: self.leader = None self.echo = False return "You have been removed as raid leader." elif self.can_use_command(sender.char_id): old_leader = self.leader self.leader = sender self.echo = self.setting_service.get( "leader_auto_echo").get_value() reply = "<highlight>%s<end> has taken raid leader from you." % sender.name if self.echo: reply += " Leader echo is <green>enabled<end>." self.bot.send_private_message(old_leader.char_id, reply) reply = "You have taken raid leader from <highlight>%s<end>." % old_leader.name if self.echo: reply += " Leader echo is <green>enabled<end>." return reply else: return "You do not have a high enough access level to take raid leader from <highlight>%s<end>." % \ self.leader.name else: if self.can_use_command(sender.char_id): self.leader = set_to self.echo = self.setting_service.get( "leader_auto_echo").get_value() reply = "<highlight>%s<end> has set you as raid leader." % sender.name if self.echo: reply += " Leader echo is <green>enabled<end>." self.bot.send_private_message(set_to.char_id, reply) reply = "<highlight>%s<end> has been set as raid leader by %s." % ( set_to.name, sender.name) if self.echo: reply += " Leader echo is <green>enabled<end>." return reply else: return "You do not have a high enough access level to take raid leader from <highlight>%s<end>." % \ self.leader.name
class LastSeenController: def __init__(self): self.logger = Logger(__name__) def inject(self, registry): self.db = registry.get_instance("db") self.util = registry.get_instance("util") self.buddy_service = registry.get_instance("buddy_service") def start(self): self.db.exec("CREATE TABLE IF NOT EXISTS last_seen (char_id INT NOT NULL PRIMARY KEY, " "dt INT NOT NULL DEFAULT 0)") @command(command="lastseen", params=[Character("character"), NamedFlagParameters(["show_all"])], access_level="org_member", description="Show the last time an org member was online (on any alt)") def lastseen_cmd(self, request, char, flag_params): sql = "SELECT p.*, a.group_id, a.status, l.dt FROM player p " \ "LEFT JOIN alts a ON p.char_id = a.char_id " \ "LEFT JOIN last_seen l ON p.char_id = l.char_id " \ "WHERE p.char_id = ? OR a.group_id = (SELECT group_id FROM alts WHERE char_id = ?) " \ "ORDER BY l.dt DESC, p.name ASC" data = self.db.query(sql, [char.char_id, char.char_id]) blob = "" if len(data) == 0: return f"No lastseen information for <highlight>{char.name}</highlight> has been recorded." else: if flag_params.show_all: for row in data: blob += f"<highlight>{row.name}</highlight>" if row.dt: blob += " last seen at " + self.util.format_datetime(row.dt) else: blob += " unknown" blob += "\n\n" return ChatBlob("Last Seen Info for %s (%d)" % (char.name, len(data)), blob) else: online_alts = list(filter(lambda x: self.buddy_service.is_online(x.char_id), data)) if online_alts: online_alts_str = ", ".join(map(lambda x: f"<highlight>{x.name}</highlight>", online_alts)) return f"<highlight>{char.name}</highlight> is <green>online</green> with: {online_alts_str}." else: alt_name = data[0].name if data[0].dt: last_seen = self.util.format_datetime(data[0].dt) return f"<highlight>{char.name}</highlight> was last seen online with <highlight>{alt_name}</highlight> at <highlight>{last_seen}</highlight>." else: return f"No lastseen information for <highlight>{char.name}</highlight> has been recorded." @event(event_type=BuddyService.BUDDY_LOGON_EVENT, description="Record last seen info") def handle_buddy_logon_event(self, event_type, event_data): self.update_last_seen(event_data.char_id) def update_last_seen(self, char_id): t = int(time.time()) if self.db.exec("UPDATE last_seen SET dt = ? WHERE char_id = ?", [t, char_id]) == 0: self.db.exec("INSERT IGNORE INTO last_seen (char_id, dt) VALUES (?, ?)", [char_id, t]) def get_last_seen(self, char_id): return self.db.query_single("SELECT dt FROM last_seen WHERE char_id = ?", [char_id])
class PointsController: def __init__(self): pass def inject(self, registry): self.bot = registry.get_instance("bot") self.db: DB = registry.get_instance("db") self.text: Text = registry.get_instance("text") self.character_service: CharacterService = registry.get_instance("character_service") self.util: Util = registry.get_instance("util") self.setting_service: SettingService = registry.get_instance("setting_service") self.alts_service: AltsService = registry.get_instance("alts_service") def start(self): self.db.exec("CREATE TABLE IF NOT EXISTS points (char_id BIGINT PRIMARY KEY, points INT DEFAULT 0, created_at INT NOT NULL, " "disabled SMALLINT DEFAULT 0)") self.db.exec("CREATE TABLE IF NOT EXISTS points_log (log_id INT PRIMARY KEY, char_id BIGINT NOT NULL, audit INT NOT NULL, " "leader_id BIGINT NOT NULL, reason VARCHAR(255), created_at INT NOT NULL)") self.db.exec("CREATE TABLE IF NOT EXISTS points_presets (preset_id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, " "points INT DEFAULT 1, UNIQUE(name))") if self.db.query_single("SELECT COUNT(*) AS count FROM points_presets").count < 1: # Populate with pre-made presets if empty presets = ["s13", "s28", "s35", "s42", "zodiac", "zod", "tnh", "beast", "12m", "tara", "pvp", "towers", "wipe", "clanwipe", "clan", "omniwipe", "omni", "bonus", "early"] sql = "INSERT INTO points_presets (name) VALUES (?)" for preset in presets: self.db.exec(sql, [preset]) @command(command="account", params=[Const("create"), Character("char")], access_level="moderator", description="Create a new account for given character name", sub_command="modify") def bank_create_cmd(self, request, _, char: SenderObj): alts_info = self.alts_service.get_alts(char.char_id) for alt in alts_info: sql = "SELECT char_id, disabled FROM points WHERE char_id = ? LIMIT 1" row = self.db.query_single(sql, [alt.char_id]) if row: was_disabled = False if row.disabled == 1: if self.db.exec("UPDATE points SET disabled = 0 WHERE char_id = ?", [alt.char_id]): was_disabled = True if alt.char_id == char.char_id: if was_disabled: self.add_log_entry(alt.char_id, request.sender.char_id, "Account was re-enabled by %s" % request.sender.name) return "<highlight>%s</highlight>'s account has been re-enabled." % char.name else: return "<highlight>%s</highlight> already has an account." % char.name else: if was_disabled: self.add_log_entry(alt.char_id, request.sender.char_id, "Account was re-enabled by %s" % request.sender.name) return "<highlight>%s</highlight>'s (%s) account has been re-enabled." % (char.name, alt.name) else: return "<highlight>%s</highlight> (%s) already has an account." % (char.name, alt.name) main_info = alts_info[0] changed_to_main = main_info.char_id == char.char_id self.create_account(main_info.char_id, request.sender) name_reference = "%s (%s)" % (char.name, main_info.name) if changed_to_main else char.name return "A new account has been created for <highlight>%s</highlight>." % name_reference @command(command="account", params=[Const("close"), Character("char")], access_level="moderator", description="Close the account for given character name", sub_command="modify") def close_account_cmd(self, request, _, char: SenderObj): main = self.alts_service.get_main(char.char_id) sql = "UPDATE points SET disabled = 1 WHERE char_id = ?" if self.db.exec(sql, [main.char_id]) > 0: reason = f"Account was closed by {request.sender.name}" self.add_log_entry(main.char_id, request.sender.char_id, reason) name_reference = "%s (%s)" % (char.name, main.name) if main.char_id != char.char_id else char.name return f"<highlight>{name_reference}</highlight> has had their account disabled. Logs have been preserved." else: return "<highlight>%s</highlight> does not have an open account." % char.name @command(command="account", params=[Const("history"), Int("log_id")], access_level="moderator", description="Look up specific account history record", sub_command="modify") def account_history_cmd(self, request, _, log_id: int): log_entry = self.db.query_single("SELECT log_id, char_id, audit, leader_id, reason, created_at FROM points_log WHERE log_id = ?", [log_id]) if not log_entry: return "No account history record with given ID <highlight>%d</highlight>." % log_id char_name = self.character_service.resolve_char_to_name(log_entry.char_id) leader_name = self.character_service.resolve_char_to_name(log_entry.leader_id) blob = f"ID: <highlight>{log_entry.log_id}</highlight>\n" blob += f"Account: <highlight>{char_name}</highlight>\n" blob += f"Action by: <highlight>{leader_name}</highlight>\n" blob += "Type: <highlight>%s</highlight>\n" % ("Management" if log_entry.audit == 0 else "Altering of points") blob += f"Reason: <highlight>{log_entry.reason}</highlight>\n" blob += f"Audit: <highlight>{log_entry.audit}</highlight>\n" action_links = None if log_entry.audit == 0: if "closed" in log_entry.reason: action_links = self.text.make_tellcmd("Open the account", "account create %s" % char_name) elif "re-enabled" in log_entry.reason: action_links = self.text.make_tellcmd("Close the account", "account close %s" % char_name) else: reason = f"Points from event ({log_id}) have been retracted" if log_entry.audit < 0: action_links = self.text.make_tellcmd("Retract", f"account add {char_name} {-log_entry.audit} {reason}") else: action_links = self.text.make_tellcmd("Retract", f"account rem {char_name} {log_entry.audit} {reason}") blob += "Actions available: [%s]\n" % (action_links if action_links is not None else "No actions available") return ChatBlob(f"Account History Record ({log_id})", blob) @command(command="account", params=[Const("add"), Character("char"), Int("amount"), Any("reason")], access_level="moderator", description="Add points to an account", sub_command="modify") def account_add_cmd(self, request, _, char: SenderObj, amount: int, reason: str): main = self.alts_service.get_main(char.char_id) row = self.get_account(main.char_id, request.conn) if not row: return f"<highlight>{char.name}</highlight> does not have an account." if row.disabled == 1: return f"Account for <highlight>{char.name}</highlight> is disabled and cannot be altered." self.alter_points(main.char_id, request.sender.char_id, reason, amount) return f"<highlight>{char.name}</highlight> has had <highlight>{amount}</highlight> points added to their account." @command(command="account", params=[Options(["rem", "remove"]), Character("char"), Int("amount"), Any("reason")], access_level="moderator", description="Remove points from an account", sub_command="modify") def account_remove_cmd(self, request, _, char: SenderObj, amount: int, reason: str): main = self.alts_service.get_main(char.char_id) row = self.get_account(main.char_id, request.conn) if not row: return f"<highlight>{char.name}</highlight> does not have an account." if row.disabled == 1: return f"Account for <highlight>{char.name}</highlight> is disabled and cannot be altered." if amount > row.points: return f"<highlight>{char.name}</highlight> only has <highlight>{row.points}</highlight> points." self.alter_points(main.char_id, request.sender.char_id, reason, -amount) return f"<highlight>{char.name}</highlight> has had <highlight>{amount}</highlight> points removed from their account." @command(command="account", params=[NamedParameters(["page"])], access_level="all", description="Look up your account") def account_self_cmd(self, request, named_params): return self.get_account_display(request.sender, named_params.page) @command(command="account", params=[Character("char"), NamedParameters(["page"])], access_level="moderator", description="Look up account of another char", sub_command="modify") def account_other_cmd(self, request, char: SenderObj, named_params): return self.get_account_display(char, named_params.page) @command(command="raid", params=[Const("presets"), Const("add"), Any("name"), Int("points")], access_level="admin", description="Add new points preset", sub_command="manage_points") def presets_add_cmd(self, request, _1, _2, name: str, points: int): count = self.db.query_single("SELECT COUNT(*) AS count FROM points_presets WHERE name = ?", [name]).count if count > 0: return "A preset already exists with the name <highlight>%s</highlight>." % name sql = "INSERT INTO points_presets (name, points) VALUES (?,?)" self.db.exec(sql, [name, points]) return "A preset with the name <highlight>%s</highlight> was added, worth <green>%d</green> points." % (name, points) @command(command="raid", params=[Const("presets"), Const("rem"), Int("preset_id")], access_level="admin", description="Delete preset", sub_command="manage_points") def presets_rem_cmd(self, request, _1, _2, preset_id: int): if self.db.exec("DELETE FROM points_presets WHERE preset_id = ?", [preset_id]) > 0: return "Successfully removed preset with ID <highlight>%d</highlight>." % preset_id else: return "No preset with given ID <highlight>%d</highlight>." % preset_id @command(command="raid", params=[Const("presets"), Const("alter"), Int("preset_id"), Int("new_points")], access_level="admin", description="Alter the points dished out by given preset", sub_command="manage_points") def presets_alter_cmd(self, request, _1, _2, preset_id: int, new_points: int): preset = self.db.query_single("SELECT * FROM points_presets WHERE preset_id = ?", [preset_id]) if not preset: return f"Preset with ID <highlight>{preset_id}</highlight> does not exist." self.db.exec("UPDATE points_presets SET points = ? WHERE preset_id = ?", [new_points, preset_id]) return "Successfully updated the preset, <highlight>%s</highlight>, to dish out " \ "<green>%d</green> points instead of <red>%d</red>." % (preset.name, new_points, preset.points) @command(command="raid", params=[Options(["presets", "addpts"])], access_level="member", description="See list of points presets") def presets_cmd(self, request, _): return ChatBlob("Raid Points Presets", self.build_preset_list()) def build_preset_list(self): presets = self.db.query("SELECT * FROM points_presets ORDER BY name ASC, points DESC") if presets: blob = "" for preset in presets: add_points_link = self.text.make_tellcmd("Add pts", "raid addpts %s" % preset.name) blob += "<highlight>%s</highlight> worth <green>%d</green> points %s [id: %d]\n\n" \ % (preset.name, preset.points, add_points_link, preset.preset_id) return blob return "No presets available. To add new presets use <highlight><symbol>presets add preset_name preset_points</highlight>." def add_log_entry(self, char_id: int, leader_id: int, reason: str, amount=0): sql = "INSERT INTO points_log (char_id, audit, leader_id, reason, created_at) VALUES (?,?,?,?,?)" return self.db.exec(sql, [char_id, amount, leader_id, reason, int(time.time())]) def alter_points(self, char_id: int, leader_id: int, reason: str, amount: int): sql = "UPDATE points SET points = points + ? WHERE char_id = ?" self.db.exec(sql, [amount, char_id]) self.add_log_entry(char_id, leader_id, reason, amount) def get_account(self, main_id, conn): sql = "SELECT p.char_id, p.points, p.disabled FROM points p WHERE p.char_id = ?" row = self.db.query_single(sql, [main_id]) if not row: self.create_account(main_id, SenderObj(conn.get_char_id(), conn.get_char_name(), None)) row = self.db.query_single(sql, [main_id]) return row def create_account(self, main_id, sender): sql = "INSERT INTO points (char_id, points, created_at) VALUES (?,?,?)" self.db.exec(sql, [main_id, 0, int(time.time())]) self.add_log_entry(main_id, sender.char_id, "Account opened by %s" % sender.name) def get_account_display(self, char: SenderObj, page): main = self.alts_service.get_main(char.char_id) if not main: return "Could not find character <highlight>%s</highlight>." % char.name page = int(page) if page else 1 page_size = 20 offset = (page - 1) * page_size points = self.db.query_single("SELECT points, disabled FROM points WHERE char_id = ?", [main.char_id]) if not points: return "Could not find raid account for <highlight>%s</highlight>." % char.name alts_link = self.text.make_tellcmd("Alts", "alts %s" % main.name) blob = "" blob += "Account: %s [%s]\n" % (main.name, alts_link) blob += "Points: %d\n" % points.points blob += "Status: %s\n\n" % ("<green>Open</green>" if points.disabled == 0 else "<red>Disabled</red>") points_log = self.db.query("SELECT * FROM points_log WHERE char_id = ? ORDER BY created_at DESC LIMIT ?, ?", [main.char_id, offset, page_size]) blob += "<header2>Account log</header2>\n" if points_log is None: blob += "No entries in log." else: for entry in points_log: if entry.audit > 0: pts = "<green>+%d</green>" % entry.audit blob += "<grey>[%s]</grey> %s points by <highlight>%s</highlight>; <orange>%s</orange>" \ % (self.util.format_datetime(entry.created_at), pts, self.character_service.resolve_char_to_name(entry.leader_id), entry.reason) elif entry.audit < 0: pts = "<red>-%d</red>" % (-1 * entry.audit) blob += "<grey>[%s]</grey> %s points by <highlight>%s</highlight>; <orange>%s</orange>" \ % (self.util.format_datetime(entry.created_at), pts, self.character_service.resolve_char_to_name(entry.leader_id), entry.reason) else: # If points is 0, then it's a general case log blob += "<grey>[%s]</grey> <orange>%s</orange>" % (self.util.format_datetime(entry.created_at), entry.reason) log_entry_link = self.text.make_tellcmd(entry.log_id, f"account history {entry.log_id}") blob += " [%s]\n" % log_entry_link return ChatBlob("%s Account" % char.name, blob)
class UtilController: def inject(self, registry): self.bot = registry.get_instance("bot") self.db = registry.get_instance("db") self.util = registry.get_instance("util") self.command_service = registry.get_instance("command_service") self.buddy_service = registry.get_instance("buddy_service") self.access_service = registry.get_instance("access_service") self.event_service = registry.get_instance("event_service") self.public_channel_service = registry.get_instance( "public_channel_service") self.getresp = registry.get_instance( "translation_service").get_response @command(command="checkaccess", params=[Character("character", is_optional=True)], access_level="all", description="Check access level for a character") def checkaccess_cmd(self, request, char): char = char or request.sender if not char.char_id: return self.getresp("global", "char_not_found", {"char": char.name}) return self.getresp( "module/system", "check_access", { "char": char.name, "rank_main": char.access_level["label"], "rank_self": self.access_service.get_single_access_level( char.char_id)["label"] }) @command(command="macro", params=[Any("command1|command2|command3...")], access_level="all", description="Execute multiple commands at once") def macro_cmd(self, request, commands): commands = commands.split("|") for command_str in commands: self.command_service.process_command( self.command_service.trim_command_symbol(command_str), request.channel, request.sender.char_id, request.reply) @command(command="echo", params=[Any("message")], access_level="all", description="Echo back a message") def echo_cmd(self, request, message): return html.escape(message) @command(command="showcommand", params=[Character("character"), Any("message")], access_level="admin", description="Show command output to another character") def showcommand_cmd(self, request, char, command_str): if not char.char_id: return self.getresp("global", "char_not_found", {"char": char.name}) self.bot.send_private_message( char.char_id, self.getresp("module/system", "show_output_target", { "sender": request.sender.name, "cmd": command_str })) self.command_service.process_command( self.command_service.trim_command_symbol(command_str), request.channel, request.sender.char_id, lambda msg: self.bot.send_private_message(char.char_id, msg)) return self.getresp("module/system", "show_output_self", { "target": char.name, "cmd": command_str }) @command(command="system", params=[], access_level="admin", description="Show system information") def system_cmd(self, request): pub_channels = "" event_types = "" access_levels = "" bots_connected = "" for _id, conn in self.bot.conns.items(): bots_connected += f"{_id} - {conn.char_name} ({conn.char_id})\n" for channel_id, name in self.public_channel_service.get_all_public_channels( ).items(): pub_channels += "%s - <highlight>%d</highlight>\n" % (name, channel_id) for event_type in self.event_service.get_event_types(): event_types += "%s\n" % event_type for access_level in self.access_service.get_access_levels(): access_levels += "%s (%d)\n" % (access_level["label"], access_level["level"]) blob = self.getresp( "module/system", "status_blob", { "bot_ver": self.bot.version, "os_ver": platform.system() + " " + platform.release(), "python_ver": str(sys.version_info.major) + "." + str(sys.version_info.minor) + "." + str(sys.version_info.micro) + "." + sys.version_info.releaselevel, "db_type": self.db.type, "mem_usage": self.util.format_number( psutil.Process(os.getpid()).memory_info().rss / 1024), "superadmin": self.bot.superadmin, "bl_used": self.buddy_service.get_buddy_list_size(), "bl_size": self.buddy_service.buddy_list_size, "uptime": self.util.time_to_readable( int(time.time()) - self.bot.start_time, max_levels=None), "dim": self.bot.dimension, "org_id": self.public_channel_service.org_id, "org_name": self.public_channel_service.org_name, "bots_connected": bots_connected, "pub_channels": pub_channels, "event_types": event_types, "access_levels": access_levels }) return ChatBlob(self.getresp("module/system", "status_title"), blob) @command( command="htmldecode", params=[Any("command")], access_level="all", description= "Decode html entities from a command before passing to the bot for execution" ) def htmldecode_cmd(self, request, command_str): self.command_service.process_command(html.unescape(command_str), request.channel, request.sender.char_id, request.reply)
class LeaderController: NOT_LEADER_MSG = "Error! You must be leader, or have higher access level than the leader to use this command." def __init__(self): self.leader = None self.last_activity = None def inject(self, registry): self.db: DB = registry.get_instance("db") self.text: Text = registry.get_instance("text") self.access_service = registry.get_instance("access_service") self.character_service = registry.get_instance("character_service") self.bot: Tyrbot = registry.get_instance("bot") @command(command="leader", params=[], access_level="all", description="Show the current raidleader") def leader_show_command(self, request): if self.leader: return "The current raidleader is <highlight>%s<end>." % self.leader.name else: return "There is no current raidleader." @command(command="leader", params=[Const("set")], access_level="all", description="Set (or unset) yourself as raidleader") def leader_set_self_command(self, request, _): if not self.leader: self.leader = request.sender return "You have been set as raidleader." elif self.leader.char_id == request.sender.char_id: self.leader = None return "You have been removed as raidleader." elif self.access_service.has_sufficient_access_level( request.sender.char_id, self.leader.char_id): old_leader = self.leader self.leader = request.sender return "You have taken leader from <highlight>%s<end>." % old_leader.name else: return "You do not have a high enough access level to take raidleader from <highlight>%s<end>." % self.leader.name @command(command="leader", params=[Const("set", is_optional=True), Character("character")], access_level="all", description="Set another character as raidleader") def leader_set_other_command(self, request, _, char): if not char.char_id: return "Could not find <highlight>%s<end>." % char.name if not self.leader or self.access_service.has_sufficient_access_level( request.sender.char_id, self.leader.char_id): self.leader = char return "<highlight>%s<end> has been set as raidleader." % char.name else: return "You do not have a high enough access level to take raidleader from <highlight>%s<end>." % self.leader.name @timerevent( budatime="1h", description= "Remove leader if leader hasn't been active for more than 1 hour") def leader_auto_remove(self, event_type, event_data): if self.last_activity: if self.last_activity - int(time.time()) > 3600: self.leader = None self.last_activity = None self.bot.send_private_channel_message( "Leader has been automatically cleared because of inactivity." ) self.bot.send_org_message( "Leader has been automatically cleared because of inactivity." ) @event(PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT, "Remove leader if leader leaves private channel") def leader_remove_on_leave_private(self, event_type, event_data): if self.leader and self.leader.char_id == event_data.char_id: self.leader = None self.last_activity = None self.bot.send_private_channel_message( "%s left private channel, and has been cleared as leader." % self.character_service.resolve_char_to_name( event_data.char_id)) @event(OrgMemberController.ORG_MEMBER_LOGOFF_EVENT, "Remove leader if leader logs off") def leader_remove_on_logoff(self, event_type, event_data): if self.leader and self.leader.char_id == event_data.char_id: self.leader = None self.last_activity = None self.bot.send_org_message( "%s has logged off, and has been cleared as leader." % self.character_service.resolve_char_to_name( event_data.char_id)) def activity_done(self): self.last_activity = int(time.time()) def can_use_command(self, char_id): if not self.leader or self.access_service.has_sufficient_access_level( char_id, self.leader.char_id): self.activity_done() return True return False