9f1da9a00d
Changed the setting registration, removed the warnings. Loot roll messages are more obvious now. Superadmins are meant to stay mostily hidden, but are being exposed in !system again.
396 lines
20 KiB
Python
396 lines
20 KiB
Python
import math
|
|
|
|
from core.buddy_service import BuddyService
|
|
from core.chat_blob import ChatBlob
|
|
from core.command_param_types import Const, Character, Any, NamedParameters
|
|
from core.decorators import instance, command, event
|
|
from core.dict_object import DictObject
|
|
from core.logger import Logger
|
|
from core.lookup.character_service import CharacterService
|
|
from core.lookup.pork_service import PorkService
|
|
from core.private_channel_service import PrivateChannelService
|
|
from core.setting_service import SettingService
|
|
from core.setting_types import TextSettingType
|
|
from core.text import Text
|
|
from core.translation_service import TranslationService
|
|
from core.igncore import Tyrbot
|
|
|
|
|
|
# noinspection SqlCaseVsIf,SqlCaseVsCoalesce
|
|
@instance()
|
|
class RIAdminController:
|
|
PAGE_SIZE = 20
|
|
UNASSIGNED_RAID_INSTANCE_ID = 0
|
|
|
|
def __init__(self):
|
|
self.logger = Logger(__name__)
|
|
|
|
def inject(self, registry):
|
|
self.bot: Tyrbot = registry.get_instance("bot")
|
|
self.db = registry.get_instance("db")
|
|
self.text: Text = registry.get_instance("text")
|
|
self.setting_service: SettingService = registry.get_instance("setting_service")
|
|
self.util = registry.get_instance("util")
|
|
self.character_service: CharacterService = registry.get_instance("character_service")
|
|
self.private_channel_service: PrivateChannelService = registry.get_instance("private_channel_service")
|
|
self.ts: TranslationService = registry.get_instance("translation_service")
|
|
self.getresp = self.ts.get_response
|
|
self.buddy_service: BuddyService = registry.get_instance("buddy_service")
|
|
self.pork: PorkService = registry.get_instance("pork_service")
|
|
|
|
def start(self):
|
|
self.db.exec(
|
|
"CREATE TABLE IF NOT EXISTS raid_instance (id INT PRIMARY KEY AUTO_INCREMENT, "
|
|
"name VARCHAR(255) NOT NULL, "
|
|
"bot varchar(32)) ENGINE MEMORY")
|
|
self.db.exec(
|
|
"CREATE TABLE IF NOT EXISTS raid_instance_char ("
|
|
"raid_instance_id INT NOT NULL, "
|
|
"char_id INT PRIMARY KEY, "
|
|
"is_leader TINYINT NOT NULL) ENGINE MEMORY")
|
|
self.setting_service.register(self.module_name, "riadmin_network",
|
|
"[905848882, 272234, 1923370, 313107, 1217210821, 1134420908]",
|
|
TextSettingType(), "Allowed bots (charID's)",
|
|
extended_description="This setting is *NOT* synchronized across the network;"
|
|
" this needs to be done manually!")
|
|
|
|
@event(event_type="buddy_logoff", description="Track raiders")
|
|
def raider_logoff(self, _, event_data):
|
|
if self.bot.is_ready():
|
|
buddy = self.buddy_service.get_buddy(event_data.char_id)
|
|
if not buddy:
|
|
return
|
|
if "raider" in buddy.get('types', []):
|
|
user = self.db.query_single("SELECT * from player where char_id=?", [event_data.char_id])
|
|
raid = self.get_raid_instance_by_char(event_data.char_id)
|
|
if user and raid:
|
|
if raid.bot != 0:
|
|
self.bot.send_private_channel_message(f'[<red>RI</red>] Raider logged off: '
|
|
f'{user.name} - {user.profession} [{raid.name}]')
|
|
self.db.exec("DELETE FROM raid_instance_char WHERE char_id = ?", [event_data.char_id])
|
|
self.buddy_service.remove_buddy(event_data.char_id, 'raider')
|
|
|
|
@event(event_type=PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT, description="Track Raiders")
|
|
def raider_leave(self, _, event_data):
|
|
if self.bot.is_ready():
|
|
buddy = self.buddy_service.get_buddy(event_data.char_id)
|
|
if not buddy:
|
|
return
|
|
if "raider" in buddy.get('types', []):
|
|
user = self.pork.get_character_info(event_data.char_id)
|
|
raid = self.get_raid_instance_by_char(event_data.char_id)
|
|
part = int(raid.bot) if raid else 0
|
|
if part in [self.bot.get_char_id(), 0]:
|
|
if part != 0:
|
|
self.bot.send_private_channel_message(
|
|
f'[<red>RI</red>] Raider left: {user.name} - {user.profession} '
|
|
f'[{raid.name if raid else "UNASSIGNED"}]')
|
|
self.db.exec("DELETE FROM raid_instance_char WHERE char_id = ?", [event_data.char_id])
|
|
self.buddy_service.remove_buddy(event_data.char_id, 'raider')
|
|
|
|
@event(event_type="connect", description="Init raider tracking on startup")
|
|
def connect(self, _, _1):
|
|
for user in self.get_raid_instance_chars():
|
|
self.buddy_service.add_buddy(user.char_id, 'raider')
|
|
|
|
@command(command="riadmin", params=[NamedParameters(['page'])], access_level="leader",
|
|
description="Show the current RIs")
|
|
def riadmin(self, _, named_params):
|
|
refresh = self.text.make_tellcmd("Refresh", "riadmin")
|
|
send = self.text.make_tellcmd("Apply", "riadmin send")
|
|
clear = self.text.make_tellcmd("Clear All", "riadmin clear")
|
|
blob = f"[{refresh}] - Update the RI List\n" \
|
|
f"[{send}] - Send all RI's to their bots\n" \
|
|
f"[{clear}] - Clear all RI's, and delete them"
|
|
blob += "\n\n"
|
|
|
|
page = int(named_params.page or "1")
|
|
offset = (page - 1) * self.PAGE_SIZE
|
|
data = self.get_raid_instance_chars()
|
|
return self.format_pagination(data, offset, page, self.compact_char_display, 'Raid Instances',
|
|
'No Raidinstances found.', 'riadmin ', self.PAGE_SIZE, blob)
|
|
|
|
def format_pagination(self, data, offset, page, formatter, title, nullmsg, cmd, page_size=10, headline=""):
|
|
raid_instances = self.get_raid_instances()
|
|
blob = ""
|
|
if page == 1:
|
|
blob = headline
|
|
selected = data[offset:offset + page_size]
|
|
if len(data) == 0:
|
|
return nullmsg
|
|
num_assigned = 0
|
|
num_unassigned = 0
|
|
pages = ""
|
|
|
|
if page > 1:
|
|
pages += "Pages: " + self.text.make_tellcmd("«« Page %d" % (page - 1), f'{cmd} --page={page - 1}')
|
|
if offset + page_size < len(data):
|
|
pages += f" Page {page}/{math.ceil(len(data) / page_size)}"
|
|
pages += " " + self.text.make_tellcmd("Page %d »»" % (page + 1), f'{cmd} --page={page + 1}')
|
|
pages += "\n"
|
|
current_raid_instance_id = None
|
|
for row in data:
|
|
if row.raid_instance_id == self.UNASSIGNED_RAID_INSTANCE_ID:
|
|
num_unassigned += 1
|
|
else:
|
|
if row.name:
|
|
num_assigned += 1
|
|
blob += "" + pages
|
|
for row in selected:
|
|
if row.raid_instance_id != current_raid_instance_id:
|
|
name = ""
|
|
if row.bot:
|
|
bot = self.pork.get_character_info(row.bot)
|
|
name = f"[{bot.name}]" if bot else ""
|
|
blob += f"\n<header2>{row.raid_instance_name} {name}</header2>\n"
|
|
current_raid_instance_id = row.raid_instance_id
|
|
|
|
if not row.char_id:
|
|
continue
|
|
|
|
blob += formatter(row)
|
|
add_leader_link = (row.raid_instance_id != self.UNASSIGNED_RAID_INSTANCE_ID and not row.is_leader)
|
|
blob += " " + self.get_assignment_links(raid_instances, row.name, add_leader_link)
|
|
blob += "\n"
|
|
blob += "\n" + pages
|
|
out = ChatBlob(title, blob)
|
|
out.page_postfix = f" (<highlight>{num_assigned}</highlight> Players)"
|
|
if num_unassigned > 0:
|
|
out.page_postfix = f" (Assigned: <highlight>{num_assigned}</highlight>, " \
|
|
f"Unassigned: <highlight>{num_unassigned}</highlight>)"
|
|
return out
|
|
|
|
@command(command="ritake", params=[Any("raiders")],
|
|
access_level="all",
|
|
description="take the RI from another bot")
|
|
def ritake(self, request, raiders: str):
|
|
users = raiders.split(",")
|
|
if request.sender.char_id not in eval(self.setting_service.get_value('riadmin_network')):
|
|
return
|
|
for user in users:
|
|
char = self.character_service.resolve_char_to_id(user.strip())
|
|
if not char:
|
|
continue
|
|
if self.private_channel_service.in_private_channel(char):
|
|
self.buddy_service.add_buddy(char, 'raider')
|
|
continue
|
|
self.bot.send_mass_message(char, 'You have been assigned to my RI :: <red>accept the invite!</red>')
|
|
self.private_channel_service.invite(char)
|
|
|
|
@command(command="riadmin",
|
|
params=[Const("add"), Any("raid_instance"), Character("char")],
|
|
access_level="leader",
|
|
description="Add a character to a RI", sub_command="leader")
|
|
def riadmin_add(self, _, _1, raid_instance_name, char):
|
|
if not char.char_id:
|
|
return self.getresp("global", "char_not_found", {"char": char.name})
|
|
raid_instance = self.get_raid_instance(raid_instance_name)
|
|
if not raid_instance:
|
|
return f"Raid instance <highlight>{raid_instance_name}</highlight> does not exist."
|
|
|
|
self.refresh_raid_instance_chars()
|
|
|
|
row = self.db.query_single("SELECT raid_instance_id FROM raid_instance_char WHERE char_id = ?", [char.char_id])
|
|
if row:
|
|
if raid_instance.id == row.raid_instance_id:
|
|
if 'raider' not in (
|
|
self.buddy_service.get_buddy(char.char_id) or DictObject({'types': []}).get('types', [])):
|
|
self.buddy_service.add_buddy(char.char_id, 'raider')
|
|
return f"Character <highlight>{char.name}</highlight> is already assigned to " \
|
|
f"raid instance <highlight>{raid_instance.name}</highlight>."
|
|
else:
|
|
if 'raider' not in (
|
|
self.buddy_service.get_buddy(char.char_id) or DictObject({'types': []}).get('types', [])):
|
|
self.buddy_service.add_buddy(char.char_id, 'raider')
|
|
self.update_char_raid_instance(char.char_id, raid_instance.id)
|
|
return f"Character <highlight>{char.name}</highlight> has been assigned to " \
|
|
f"raid instance <highlight>{raid_instance.name}</highlight>."
|
|
else:
|
|
return f"Character <highlight>{char.name}</highlight> is not in the private channel."
|
|
|
|
@command(command="riadmin", params=[Const("clear")], access_level="leader",
|
|
description="Remove all raids and their players", sub_command="leader")
|
|
def riadmin_clear(self, _, _1):
|
|
query = self.db.query("SELECT * FROM raid_instance_char")
|
|
for user in query:
|
|
self.buddy_service.remove_buddy(user.char_id, 'raider')
|
|
self.db.exec("DELETE FROM raid_instance_char where 1")
|
|
self.db.exec("DELETE FROM raid_instance where 1")
|
|
return f"All characters have been removed from raid instances."
|
|
|
|
@command(command="riadmin", params=[Const("rem"), Character("char")], access_level="leader",
|
|
description="remove a character from the RI", sub_command="leader")
|
|
def riadmin_rem(self, _, _2, char):
|
|
if not char.char_id:
|
|
return self.getresp("global", "char_not_found", {"char": char.name})
|
|
|
|
self.refresh_raid_instance_chars()
|
|
|
|
row = self.db.query_single(
|
|
"SELECT r2.name FROM raid_instance_char r1 "
|
|
"JOIN raid_instance r2 ON r1.raid_instance_id = r2.id WHERE r1.char_id = ?",
|
|
[char.char_id])
|
|
if row:
|
|
self.update_char_raid_instance(char.char_id, self.UNASSIGNED_RAID_INSTANCE_ID)
|
|
return f"Character <highlight>{char.name}</highlight> has been removed from " \
|
|
f"raid instance <highlight>{row.name}</highlight>."
|
|
else:
|
|
return f"Character <highlight>{char.name}</highlight> is not assigned to any raid instances."
|
|
|
|
@command(command="riadmin", params=[Const("leader"), Character("char")], access_level="leader",
|
|
description="Set the leader for a RI", sub_command="leader")
|
|
def riadmin_leader(self, _, _2, char):
|
|
if not char.char_id:
|
|
return self.getresp("global", "char_not_found", {"char": char.name})
|
|
|
|
raid_instance = self.get_raid_instance_by_char(char.char_id)
|
|
if not raid_instance:
|
|
return f"Character <highlight>{char.name}</highlight> does not belong to a raid instance."
|
|
|
|
self.set_leader(char.char_id, raid_instance.id)
|
|
|
|
return f"Character <highlight>{char.name}</highlight> has been set as the leader for " \
|
|
f"raid instance <highlight>{raid_instance.name}</highlight>."
|
|
|
|
@command(command="riadmin", params=[Const("send")], access_level="leader",
|
|
description="Send the RIs to their preset bots", sub_command="leader")
|
|
def riadmin_send(self, _, _2):
|
|
self.bot.send_private_channel_message("Exporting raids..")
|
|
for raid_instance in self.get_raid_instances():
|
|
data = self.db.query(
|
|
"SELECT r.char_id, p.name, r.is_leader FROM raid_instance_char r "
|
|
"left join player p on r.char_id=p.char_id WHERE raid_instance_id = ?",
|
|
[raid_instance.id])
|
|
if int(raid_instance.bot) == self.bot.get_char_id() or raid_instance.bot == "0" or not raid_instance.bot:
|
|
continue
|
|
self.bot.send_private_message(int(raid_instance.bot), "ritake " + ", ".join([k['name'] for k in data]),
|
|
add_color=False)
|
|
for char in data:
|
|
if char.is_leader == 0:
|
|
self.private_channel_service.kick(char.char_id)
|
|
|
|
return "Raid instance configuration has been applied."
|
|
|
|
@command(command="riadmin", params=[Const("create"), Any("Raid_name"), Character("bot")],
|
|
access_level="leader",
|
|
description="Create or change a raid instance", sub_command="leader")
|
|
def riadmin_create(self, _, _2, raid_instance_name, bot):
|
|
if not bot.char_id:
|
|
return self.getresp("global", "char_not_found", {"char": bot.name})
|
|
if bot.char_id not in eval(self.setting_service.get_value('riadmin_network')):
|
|
return "Bot not valid: please ask an Administrator to verify it first."
|
|
raid_instance = self.get_raid_instance(raid_instance_name)
|
|
if raid_instance:
|
|
if raid_instance.name == raid_instance_name and raid_instance.bot == bot.char_id:
|
|
return f"Raid instance <highlight>{raid_instance_name}</highlight> already exists."
|
|
else:
|
|
self.db.exec("UPDATE raid_instance SET name = ?, bot = ? WHERE id = ?",
|
|
[raid_instance_name, bot.char_id, raid_instance.id])
|
|
return f"Raid instance <highlight>{raid_instance_name}</highlight> has been updated."
|
|
else:
|
|
self.db.exec("INSERT INTO raid_instance (name, bot) VALUES (?, ?)", [raid_instance_name, bot.char_id])
|
|
return f"Raid instance <highlight>{raid_instance_name}</highlight> [<highlight>{bot.name}</highlight>] " \
|
|
f"has been created."
|
|
|
|
@command(command="riadmin", params=[Const("delete"), Any("raid_instance_name")], access_level="leader",
|
|
description="Remove a RI", sub_command="leader")
|
|
def raid_instance_delete_cmd(self, _, _1, raid_instance_name):
|
|
raid_instance = self.get_raid_instance(raid_instance_name)
|
|
if not raid_instance:
|
|
return f"Raid instance <highlight>{raid_instance_name}</highlight> does not exist."
|
|
query = self.db.query("SELECT char_id from raid_instance_char where raid_instance_id=?", [raid_instance.id])
|
|
for user in query:
|
|
self.buddy_service.remove_buddy(user.char_id, 'raider')
|
|
self.db.exec("DELETE FROM raid_instance_char WHERE raid_instance_id = ?", [raid_instance.id])
|
|
self.db.exec("DELETE FROM raid_instance WHERE id = ?", [raid_instance.id])
|
|
|
|
return f"Raid instance <highlight>{raid_instance_name}</highlight> has been deleted."
|
|
|
|
def get_raid_instance_chars(self):
|
|
self.refresh_raid_instance_chars()
|
|
|
|
data = self.db.query("SELECT * FROM ("
|
|
"SELECT p.*, r2.id AS raid_instance_id, "
|
|
"r1.is_leader, r2.name AS raid_instance_name, r2.bot "
|
|
"FROM raid_instance r2 "
|
|
"LEFT JOIN raid_instance_char r1 ON r1.raid_instance_id = r2.id "
|
|
"LEFT JOIN player p ON r1.char_id = p.char_id "
|
|
"UNION "
|
|
"SELECT p.*, r3.raid_instance_id, "
|
|
"r3.is_leader, 'Unassigned' AS raid_instance_name, '' AS bot "
|
|
"FROM raid_instance_char r3 "
|
|
"LEFT JOIN player p ON r3.char_id = p.char_id "
|
|
"WHERE r3.raid_instance_id = ?) as r2r1pr3p "
|
|
"ORDER BY raid_instance_id != ? DESC, "
|
|
"raid_instance_name, "
|
|
"is_leader desc, "
|
|
"profession, level desc, "
|
|
"name",
|
|
[self.UNASSIGNED_RAID_INSTANCE_ID, self.UNASSIGNED_RAID_INSTANCE_ID])
|
|
|
|
return data
|
|
|
|
def refresh_raid_instance_chars(self):
|
|
users = self.db.query('SELECT * from online o '
|
|
'left join player p on o.char_id=p.char_id '
|
|
'where o.channel = ?', [self.bot.name])
|
|
for user in users:
|
|
self.db.exec(
|
|
"INSERT IGNORE INTO raid_instance_char (char_id, raid_instance_id, is_leader) VALUES (?, ?, 0)",
|
|
[user.char_id, self.UNASSIGNED_RAID_INSTANCE_ID])
|
|
self.buddy_service.add_buddy(user.char_id, 'raider')
|
|
|
|
def update_char_raid_instance(self, char_id, raid_instance_id):
|
|
return self.db.exec("UPDATE raid_instance_char SET raid_instance_id = ?, is_leader = 0 WHERE char_id = ?",
|
|
[raid_instance_id, char_id])
|
|
|
|
def set_leader(self, char_id, raid_instance_id):
|
|
self.db.exec("UPDATE raid_instance_char SET is_leader = 0 WHERE raid_instance_id = ?", [raid_instance_id])
|
|
self.db.exec("UPDATE raid_instance_char SET is_leader = 1 WHERE raid_instance_id = ? AND char_id = ?",
|
|
[raid_instance_id, char_id])
|
|
|
|
def compact_char_display(self, char_info):
|
|
if char_info.level:
|
|
msg = f" {self.util.get_prof_icon(char_info.profession)} " \
|
|
f"{self.text.zfill(char_info.level, 220)}/<green>{self.text.zfill(char_info.ai_level, 30)}</green> " \
|
|
f"{char_info.name: <13}"
|
|
elif char_info.name:
|
|
msg = " <highlight>%s</highlight>" % char_info.name
|
|
else:
|
|
msg = " <highlight>Unknown(%d)</highlight>" % char_info.char_id
|
|
|
|
if char_info.is_leader:
|
|
msg += " [<highlight>Leader</highlight>]"
|
|
|
|
return msg
|
|
|
|
def get_assignment_links(self, raid_instances, char_name, add_leader_link):
|
|
links = list(map(lambda x: self.text.make_tellcmd(x.name, f"riadmin add {x.name} {char_name}"), raid_instances))
|
|
links.insert(0, self.text.make_tellcmd("Rem", f"riadmin rem {char_name}"))
|
|
if add_leader_link:
|
|
links.insert(0, self.text.make_tellcmd("L", f"riadmin leader {char_name}"))
|
|
return f'[{"|".join(links)}]'
|
|
|
|
def get_raid_instances(self):
|
|
data = self.db.query("SELECT id, name, bot FROM raid_instance ORDER BY name")
|
|
return data
|
|
|
|
def get_raid_instance(self, raid_instance_name):
|
|
return self.db.query_single("SELECT id, name, bot FROM raid_instance WHERE name LIKE ?", [raid_instance_name])
|
|
|
|
def get_raid_instance_by_char(self, char_id):
|
|
return self.db.query_single("SELECT id, name, CASE WHEN bot IS NOT NULL THEN bot else 0 END as bot "
|
|
"FROM raid_instance r1 JOIN raid_instance_char r2 ON r1.id = r2.raid_instance_id "
|
|
"WHERE r2.char_id = ?", [char_id])
|
|
|
|
def get_conn_by_id(self, bot):
|
|
conn = self.bot.conns.get(bot)
|
|
if conn:
|
|
return conn
|
|
|
|
conns = self.bot.get_conns(lambda x: x.char_name.lower() == bot.lower())
|
|
if conns:
|
|
return conns[0][1]
|
|
|
|
return None
|