import math from core.aochat.BaseModule import BaseModule from core.aochat.server_packets import BuddyAdded from core.buddy_service import BuddyService from core.chat_blob import ChatBlob from core.command_param_types import Character, Options, Const, Any, NamedParameters from core.db import DB from core.decorators import instance, command, event from core.dict_object import DictObject from core.event_service import EventService from core.job_scheduler import JobScheduler from core.lookup.pork_service import PorkService from core.message_hub_service import MessageHubService from core.private_channel_service import PrivateChannelService from core.setting_service import SettingService from core.setting_types import TextSettingType, ColorSettingType from core.text import Text from core.tyrbot import Tyrbot from core.util import Util from modules.core.accounting.services.account_service import AccountService from modules.raidbot.tower.tower_controller import TowerController # noinspection DuplicatedCode,SqlCaseVsIf @instance() class TrackController(BaseModule): PRIVATE_CHANNEL_PREFIX = "[Priv]" PAGE_SIZE = 20 MESSAGE_SOURCE = "track_log" def inject(self, registry): self.bot: Tyrbot = registry.get_instance("bot") self.util: Util = registry.get_instance("util") self.pork: PorkService = registry.get_instance("pork_service") self.job_scheduler: JobScheduler = registry.get_instance("job_scheduler") self.text: Text = registry.get_instance("text") self.event_service: EventService = registry.get_instance("event_service") self.setting_service: SettingService = registry.get_instance("setting_service") self.buddy_service: BuddyService = registry.get_instance("buddy_service") self.account_service: AccountService = registry.get_instance("account_service") self.db: DB = registry.get_instance("db") self.priv: PrivateChannelService = registry.get_instance("private_channel_service") self.tower: TowerController = registry.get_instance("tower_controller") self.message_hub_service: MessageHubService = registry.get_instance("message_hub_service") def pre_start(self): self.event_service.register_event_type("track_logon") self.event_service.register_event_type("track_logoff") self.setting_service.register_new(self.module_name, "track_on_color", "#FF0000", ColorSettingType(), "Color for Track logon") self.setting_service.register_new(self.module_name, "track_off_color", "#00FF00", ColorSettingType(), "Color for Track logoff") self.setting_service.register_new(self.module_name, "autotrack", 'none', TextSettingType(['omni', 'clan', 'neutral', "none"]), "Autotrack all players initiating tower attacks towards this faction:") self.db.exec( "CREATE TABLE IF NOT EXISTS track(" "char_id int not null primary key, " "initiator int not null, " "reason varchar(255))") self.message_hub_service.register_message_source(self.MESSAGE_SOURCE) @event(event_type="connect", description="Autoadd tracked players on connect", is_hidden=True) def connect_add(self, _1, _2): query = self.db.query("SELECT * from track") for user in query: self.buddy_service.add_buddy(user.char_id, "track") @event(event_type="buddy_logon", description="Fire tracker events", is_hidden=True) def track_fire_logon(self, _, event_data): if self.bot.is_ready(): if "track" in (self.buddy_service.get_buddy(event_data.char_id) or {'types': []})["types"]: if type(event_data) == BuddyAdded: self.event_service.fire_event("track_logon", self.db.query_single("SELECT * from player where char_id=?", [event_data.char_id])) self.db.exec("INSERT IGNORE INTO online VALUES(?, ?, ?)", [event_data.char_id, 'track', self.bot.get_char_id()]) else: self.job_scheduler.delayed_job(self.track_fire_logon, 10, DictObject({'char_id': event_data.char_id, 'repeat': True})) @event(event_type="buddy_logoff", description="Fire tracker events", is_hidden=True) def track_fire_logoff(self, _1, event_data): if "track" in self.buddy_service.get_buddy(event_data.char_id)["types"]: self.event_service.fire_event("track_logoff", self.db.query_single("SELECT * from player where char_id=?", [event_data.char_id])) self.db.exec("DELETE FROM online where char_id=? and bot=?", [event_data.char_id, self.bot.get_char_id()]) @event(event_type="track_logon", description="Fire tracker events", is_hidden=True) def track_logon(self, _1, user): if self.bot.is_ready(): color = self.setting_service.get("track_on_color").format_text("ON") self.send_t_warn(0, f'{color} :: {self.text.format_char_info(user)}') @event(event_type="track_logoff", description="Fire tracker events", is_hidden=True) def track_logoff(self, _1, user): if self.bot.is_ready(): color = self.setting_service.get("track_off_color").format_text("OFF") self.send_t_warn(0, f'{color} :: {self.text.format_char_info(user)}') @event(event_type=TowerController.TOWER_ATTACK_EVENT, description="Warn on Tower attacks", is_hidden=True) def tower_attack_event(self, _, event_data): attacker = event_data.attacker if event_data.defender.faction.lower() == self.setting_service.get_value("autotrack"): if not self.buddy_service.get_buddy(attacker.char_id): if self.buddy_service.add_buddy(attacker.char_id, "track"): self.send_t_warn(0, f"Now tracking: " f"<{attacker.faction.lower()}>{attacker.name}") self.db.exec("INSERT IGNORE INTO track VALUES(?, ?, ?)", [attacker.char_id, self.bot.get_char_id(), "attacked us"]) def send_t_warn(self, _, msg): self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, "[T] " + msg, "[T] " + msg) def get_tracked(self, order, online): where = "where o.char_id IS NOT NULL" if online else "" return self.db.query(f"SELECT p.name, p.level, p.ai_level, p.org_name, p.profession, p.faction, " f"p2.name as initiator, " f"CASE WHEN o.char_id IS NOT NULL THEN 1 ELSE 0 END AS online, " f"t.reason from track t " f"left join player p on p.char_id = t.char_id " f"left join player p2 on p2.char_id = t.initiator " f"left join online o on o.char_id = t.char_id {where} group by t.char_id order by {order}") @command(command="track", params=[Const('add'), Character("character"), Any('reason')], access_level="leader", description="Initiate tracking for the character", sub_command="add") def track_add(self, request, _, user, reason): if not user.char_id: return f"Character {user.name} not found." self.pork.load_character_info(user.char_id, user.name) if acc := self.account_service.get_account(request.sender.char_id): if "track" in (self.buddy_service.get_buddy(user.char_id) or {'types': []})["types"]: return f"Character {user.name} is already being tracked." else: self.pork.load_character_info(user.char_id, user.name) self.send_t_warn(0, f'Tracking initiated: {user.name} by {acc.name}') self.buddy_service.add_buddy(user.char_id, 'track') self.db.exec("INSERT IGNORE INTO track VALUES(?, ?, ?)", [user.char_id, acc.main, reason]) @command(command="track", params=[Const('rem'), Character("character")], access_level="moderator", description="Remove character from the tracking", sub_command="rem") def track_rem(self, request, _, user): if not user.char_id: return f"Character {user.name} not found." if acc := self.account_service.get_account(request.sender.char_id): if self.buddy_service.remove_buddy(user.char_id, "track"): self.db.exec("DELETE FROM track where char_id=?", [user.char_id]) self.send_t_warn(0, f'Tracking stopped: {user.name} by {acc.name}') return f"Character {user.name} is nolonger being tracked." else: return f"Character {user.name} is not being tracked." @command(command="track", params=[Const('list', is_optional=True), Options(["org", "prof", "tl"], is_optional=True), NamedParameters(["page"])], access_level="member", description="Shows tracked players") def track_list(self, _, const, group, named_params): page = int(named_params.page or "1") offset = (page - 1) * self.PAGE_SIZE if group is None: group = 'tl' if group == "org": players = self.get_tracked('p.org_name, p.profession, p.level desc, p.name', False if const else True) return self.format_page(players, "org", offset, page, f"Tracklist by Organisation ({len(players)})", 'No tracked users online', f"track {const or ''} {group or ''}") elif group == "prof": players = self.get_tracked('p.profession, p.level desc, p.name', False if const else True) return self.format_page(players, "prof", offset, page, f"Tracklist by Profession ({len(players)})", 'No tracked users online', f"track {const or ''} {group or ''}") elif group == "tl": players = self.get_tracked('p.level desc, p.profession, p.name', False if const else True) return self.format_page(players, "tl", offset, page, f"Tracklist by Titlelevel ({len(players)})", 'No tracked users online', f"track {const or ''} {group or ''}") def format_row(self, user): org = f"[<{user.faction.lower()}>{user.org_name}] " if user.org_name else "" return f"{self.util.get_prof_icon(user.profession)} " \ f"{self.text.zfill(user.level, 220)}:{self.text.zfill(user.ai_level, 220)} " \ f"<{user.faction.lower()}>{user.name}" \ f" {org}init by {user.initiator}: {user.reason} " \ f"{'[ONLINE]' if user.online == 1 else ''}\n" def format_page(self, tracked, order, offset, page, title, nullmsg, cmd): selected = tracked[offset:offset + self.PAGE_SIZE] count = len(selected) pages = "" if page > 1: pages += "Pages: " + self.text.make_tellcmd("«« Page %d" % (page - 1), f'{cmd} --page={page - 1}') if offset + self.PAGE_SIZE < len(tracked): pages += f" Page {page}/{math.ceil(len(tracked) / self.PAGE_SIZE)}" pages += " " + self.text.make_tellcmd("Page %d »»" % (page + 1), f'{cmd} --page={page + 1}') pages += "\n" if count == 0: return nullmsg else: blob = "\n\n" + pages + "" if order == "prof": prof = "" for player in selected: if player.profession != prof: prof = player.profession blob += f"\n
{player.profession}
\n" blob += self.format_row(player) elif order == "tl": tl = "" for player in selected: titlelevel = self.util.get_title_level(player.level) if titlelevel != tl: tl = titlelevel blob += f"\n
TL{titlelevel}
\n" blob += self.format_row(player) elif order == "org": org_name = "" for player in selected: if player.org_name != org_name: org_name = player.org_name blob += f"\n
{player.org_name}
\n" blob += self.format_row(player) blob += "
\n" + pages return ChatBlob(title, blob)