406 lines
19 KiB
Python
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 IgnCore
|
|
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: IgnCore = 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("'", "'")
|
|
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)
|