Files
igncore/modules/standard/loot/loot_controller.py
T
Minidodo 9f1da9a00d Fixed warnings caused by non-existing messagehub channels.
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.
2021-08-29 17:54:18 +02:00

406 lines
19 KiB
Python

import re
import secrets
import time
from collections import OrderedDict
from core.chat_blob import ChatBlob
from core.command_alias_service import CommandAliasService
from core.command_param_types import Const, Int, Any
from core.db import DB
from core.decorators import instance, command, timerevent
from core.setting_service import SettingService
from core.text import Text
from core.igncore import Tyrbot
from modules.core.accounting.services.account_service import AccountService
from modules.raidbot.raid.raidbot_controller import Raider
from modules.standard.items.items_controller import ItemsController
from modules.standard.loot.item_types import LootItem
from modules.standard.raid.leader_controller import LeaderController
@instance()
class LootController:
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.loot_list = OrderedDict()
self.last_modify = 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.leader_controller: LeaderController = registry.get_instance("leader_controller", is_optional=True)
self.setting_service: SettingService = registry.get_instance("setting_service")
self.items_controller: ItemsController = registry.get_instance("items_controller")
self.raid_controller = None
self.raid_controller = registry.get_instance("raidbot_controller", is_optional=True)
self.account_service: AccountService = registry.get_instance("account_service")
self.alts_service: AccountService = registry.get_instance("account_service")
self.alias: CommandAliasService = registry.get_instance("command_alias_service")
def pre_start(self):
self.db.load_sql_file(self.module_dir + "/raid_loot.sql", pre_optimized=True)
self.db.create_view("raid_loot")
self.alias.add_alias("add", "loot add")
self.alias.add_alias("rem", "loot rem")
self.alias.add_alias("remove", "loot remo")
self.alias.add_alias("flatroll", "loot roll")
self.alias.add_alias("result", "loot roll")
self.alias.add_alias("results", "loot roll")
self.alias.add_alias("list", "loot")
self.alias.add_alias("win", "loot roll")
@command(command="loot", params=[], description="Show the list of added items", access_level="member")
def loot_cmd(self, _):
if not self.loot_list:
return "Loot list is empty."
return self.get_loot_list()
@command(command="loot", params=[Const("edit")], description="Show the list of added items", access_level="leader",
sub_command="modify")
def loot_edit_cmd(self, _, _1):
if not self.loot_list:
return "Loot list is empty."
return self.get_loot_list(edit=True)
@command(command="loot", params=[Const("clear")], description="Clear all loot", access_level="leader",
sub_command="modify")
def loot_clear_cmd(self, request, _):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if self.loot_list:
self.loot_list.clear()
self.last_modify = None
self.bot.send_private_channel_message("Loot list cleared.")
else:
return "Loot list is already empty."
@command(command="loot", params=[Const("remitem"), Int("item_index")],
description="Remove an existing loot item", access_level="leader", sub_command="modify")
def loot_rem_item_cmd(self, request, _, item_index: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if not self.loot_list:
return "Loot list is empty."
try:
if self.loot_list[item_index]:
self.last_modify = int(time.time())
self.bot.send_private_channel_message(
f"Removed {self.loot_list.pop(item_index).get_item_str()} from loot list.")
else:
return "Item error."
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("increase"), Int("item_index")], description="Increase item count",
access_level="leader", sub_command="modify")
def loot_increase_item_cmd(self, request, _, item_index: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if not self.loot_list:
return "Loot list is empty."
try:
loot_item = self.loot_list[item_index]
if loot_item:
loot_item.count += 1
self.last_modify = int(time.time())
self.bot.send_private_channel_message(
f"Increased item count for {loot_item.get_item_str()} to {loot_item.count:d}.")
else:
return "Item error."
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("decrease"), Int("item_index")], description="Decrease item count",
access_level="leader", sub_command="modify")
def loot_decrease_item_cmd(self, request, _, item_index: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if not self.loot_list:
return "Loot list is empty."
try:
loot_item = self.loot_list[item_index]
if loot_item:
loot_item.count = loot_item.count - 1 if loot_item.count > 1 else 1
self.last_modify = int(time.time())
self.bot.send_private_channel_message(
f"Decreased item count for {loot_item.get_item_str()} to {loot_item.count:d}.")
else:
return "Item error."
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("add"), Int("item_index")], description="Add yourself to item",
access_level="member")
def loot_add_to_cmd(self, request, _, item_index: int):
main = self.alts_service.get_account(request.sender.char_id)
if self.raid_controller:
raider: Raider = self.raid_controller.is_in_raid(main.char_id)
if type(raider) == Raider:
if not raider.is_active:
self.bot.send_mass_message(request.sender.char_id,
"You are not participating in the raid, and cannot add to the loot.")
return
elif raider is None:
self.bot.send_mass_message(request.sender.char_id,
"You are not participating in the raid, and cannot add to the loot.")
return
try:
loot_item = self.loot_list[item_index]
old_item = self.is_already_added(request.sender.name)
if old_item:
if old_item.get_item_str() == loot_item.get_item_str():
name = "You have" if request.channel == "msg" else request.sender.name
self.bot.send_mass_message(request.sender.char_id,
"%s already added to %s." % (name, loot_item.get_item_str()))
old_item.bidders.remove(request.sender.name)
loot_item.bidders.append(request.sender.name)
self.last_modify = int(time.time())
if old_item is not None:
text = f"moved from {old_item.get_item_str()} to {loot_item.get_item_str()}."
self.bot.send_mass_message(request.sender.char_id, f"You have {text}")
self.bot.send_private_channel_message(request.sender.name + " " + text)
else:
text = f"added to {loot_item.get_item_str()}."
self.bot.send_mass_message(request.sender.char_id, f"You have {text}")
self.bot.send_private_channel_message(request.sender.name + " " + text)
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("rem")], description="Remove yourself from item", access_level="member")
def loot_rem_from_cmd(self, request, _):
try:
loot_item = self.is_already_added(request.sender.name)
if loot_item is not None:
loot_item.bidders.remove(request.sender.name)
self.last_modify = int(time.time())
text = f"removed from {loot_item.get_item_str()}."
self.bot.send_private_message(request.sender.char_id, f"You were {text}")
self.bot.send_private_channel_message(request.sender.name + " was " + text)
else:
return "You are not added to any loot."
except KeyError:
return "Wrong index given."
@command(command="loot", params=[Const("roll")], description="Roll all loot", access_level="leader",
sub_command="modify")
def loot_roll_cmd(self, request, _):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if self.loot_list:
blob = ""
remove = []
for i, loot_item in self.loot_list.items():
winners = []
if loot_item.bidders:
if len(loot_item.bidders) <= loot_item.count:
winners = loot_item.bidders.copy()
loot_item.count -= len(loot_item.bidders)
loot_item.bidders = []
else:
for j in range(0, loot_item.count):
winner = secrets.choice(loot_item.bidders)
winners.append(winner)
loot_item.bidders.remove(winner)
loot_item.count = loot_item.count - 1 if loot_item.count > 0 else 0
for user in winners:
self.account_service.add_log(self.account_service.character_service.resolve_char_to_id(user),
"loot",
f"{loot_item.get_item_str()}",
request.sender.char_id)
blob += f"{i:d}. {loot_item.get_item_str()}\n"
blob += f" | Winners: <red>{'<end>, <red>'.join(winners)}<end>\n\n"
if loot_item.count == 0:
remove.append(i)
for i in remove:
self.loot_list.pop(i)
msg = ChatBlob("Roll results", blob, "\n________________\n\n<red>Time is UP!</red>\n", "\n________________")
self.bot.send_private_channel_message(msg if len(blob) > 0 else "No one was added to any loot")
if self.loot_list:
count = 1
for key in sorted(list(self.loot_list.keys())):
if self.loot_list[key].count <= 0:
del self.loot_list[key]
else:
loot_item = self.loot_list[key]
del self.loot_list[key]
self.loot_list[count] = loot_item
count += 1
if len(self.loot_list) > 0:
self.bot.send_private_channel_message(f"Theres still loot left to roll, the list has been rebuilt.")
else:
return "No loot to roll."
@command(command="loot", params=[Const("addraiditem"), Int("raid_item_id"), Int("item_count")],
description="Add item from pre-defined raid to loot list", access_level="leader", sub_command="modify")
def loot_add_raid_item(self, request, _, raid_item_id: int, item_count: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
sql = "SELECT r.name, r.comment, r.ql, a.lowid AS low_id, a.highid AS high_id FROM aodb a " \
"LEFT JOIN raid_loot r ON (a.name = r.name AND a.highql >= r.ql) " \
"WHERE r.id = ? LIMIT 1"
item = self.db.query_single(sql, [raid_item_id])
if item:
self.add_item_to_loot(item, item.comment, item_count)
self.bot.send_private_channel_message(f"Added {item.name} to loot list.")
else:
return f"Failed to add item with ID {raid_item_id}."
@command(command="loot", params=[Const("addraid"), Any("raid"), Any("category")],
description="Add all loot from pre-defined raid", access_level="leader", sub_command="modify")
def loot_add_raid_loot(self, request, _, raid: str, category: str):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
items = self.db.query(
"SELECT r.raid, r.category, r.id, r.ql, r.name, r.comment, r.multiloot, "
"a.lowid AS low_id, "
"a.highid AS high_id, a.icon "
"FROM raid_loot r "
"LEFT JOIN aodb a "
"ON (r.name = a.name AND r.ql <= a.highql) "
"WHERE r.raid = ? AND r.category = ? "
"ORDER BY r.name",
[raid, category]
)
if items:
for item in items:
self.add_item_to_loot(item, item.comment, item.multiloot)
self.bot.send_private_channel_message(f"{category} table was added to loot.")
else:
return f"{category} does not have any items registered in loot table."
@command(command="loot",
params=[Const("additem", is_optional=True), Int("item"), Int("item_count", is_optional=True)],
description="Add an item to loot list by item id", access_level="leader", sub_command="modify")
def loot_add_item_id_cmd(self, request, _, item_id, item_count: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
if item_count is None:
item_count = 1
item = self.items_controller.get_by_item_id(item_id)
if not item:
return f"Could not find item with ID <highlight>{item_id}<end>."
self.add_item_to_loot(item, item_count=item_count)
self.bot.send_private_channel_message(f"{item.name} was added to loot list.")
@command(command="loot",
params=[Const("additem", is_optional=True), Any("item"), Int("item_count", is_optional=True)],
description="Add an item to loot list", access_level="leader", sub_command="modify")
def loot_add_item_cmd(self, request, _, item, item_count: int):
if not self.leader_controller.can_use_command(request.sender.char_id) if self.leader_controller else False:
return LeaderController.NOT_LEADER_MSG
loot = ""
if item_count is None:
item_count = 1
item = item.replace("&#39;", "'")
items = re.findall(r"(([^<]+)?<a href=[\"\']itemref://(\d+)/(\d+)/(\d+)[\"\']>([^<]+)</a>([^<]+)?)", item)
if items and item_count == 1:
for item in items:
item = self.text.make_item(int(item[2]), int(item[3]), int(item[4]), item[5])
if loot != "":
loot += ", " + item
else:
loot += item
self.add_item_to_loot(item)
else:
loot += item
self.add_item_to_loot(item, item_count=item_count)
self.bot.send_private_channel_message(f"<highlight>{loot}<end> was added to loot list.")
@timerevent(budatime="1h",
description="Periodically check when loot list was last modified, and clear it "
"if last modification was done 1+ hours ago")
def loot_clear_event(self, _1, _2):
if self.loot_list and self.last_modify:
if int(time.time()) - self.last_modify > 3600 and self.loot_list:
self.last_modify = None
self.loot_list = OrderedDict()
self.bot.send_org_message("Loot was last modified more than 1 hour ago, list has been cleared.")
self.bot.send_private_channel_message(
"Loot was last modified more than 1 hour ago, list has been cleared.")
def is_already_added(self, name: str):
for i, loot_item in self.loot_list.items():
if name in loot_item.bidders:
return loot_item
return None
def add_item_to_loot(self, item, comment=None, item_count=1):
existing_item = next((loot_item for x, loot_item in self.loot_list.items() if loot_item.item == item), None)
if existing_item:
existing_item.count += 1
else:
# this prevents loot items from being re-numbered when items are removed
end_index = list(self.loot_list.keys())[-1] + 1 if len(self.loot_list) > 0 else 1
self.loot_list[end_index] = LootItem(item, comment, item_count)
self.last_modify = int(time.time())
def get_loot_list(self, edit=False):
blob = ""
remove = self.text.make_chatcmd("Remove from rolls", "/tell <myname> loot rem")
blob += f"[{remove}]\n\n"
for i, loot_item in self.loot_list.items():
bidders = loot_item.bidders
img = ""
if loot_item.get_item_str()[:2] == "<a" and loot_item.get_item_str()[-4:] == "</a>":
item = re.findall(r"(([^<]+)?<a href=[\"\']itemref://(\d+)/(\d+)/(\d+)[\"\']>([^<]+)</a>([^<]+)?)",
loot_item.get_item_str())
imgid = self.db.query_single("SELECT icon from aodb where lowid=? or highid=?",
[item[0][2], item[0][3]])
if imgid is not None:
img = self.text.make_image(imgid.get("icon")) + " - "
blob += f"{i:d}. {img}{item[0][0]} "
else:
blob += f"{i:d}. {loot_item.get_item_str()} "
blob += f"x{loot_item.count} [{self.text.make_chatcmd('Add', f'/tell <myname> loot add {i}')}]\n"
blob += ""
if edit:
blob += f"<yellow>[ {self.text.make_tellcmd('+1', f'loot increase {i}')} | " \
f"{self.text.make_tellcmd('-1', f'loot decrease {i}')} ]" \
f"[ {self.text.make_tellcmd('REM', f'loot remitem {i}')} ]</yellow>\n\n"
else:
if len(bidders) > 0:
blob += f"<yellow>{', '.join(bidders)}<end>\n\n"
else:
blob += "<yellow>None added<end>\n\n"
return ChatBlob(f"Loot ({len(self.loot_list):d})", blob)