diff --git a/core/command_service.py b/core/command_service.py index 6e4ad5a..4a41d26 100644 --- a/core/command_service.py +++ b/core/command_service.py @@ -89,8 +89,7 @@ class CommandService: command_key = self.get_command_key(cmd_name.lower(), sub_command.lower() if sub_command else "") al = access_levels.get(command_key, None) if al is not None and al != access_level.lower(): - print(handler) - raise Exception(f"Different access levels specified for forms of command '{command_key}'") + raise Exception(f"Different access levels specified for forms of command '{command_key}' with handler '{handler}'") access_levels[command_key] = access_level self.register(handler, cmd_name, params, access_level, description, inst.module_name, help_text, diff --git a/core/igncore.py b/core/igncore.py index 4184afc..f9ee44a 100644 --- a/core/igncore.py +++ b/core/igncore.py @@ -48,7 +48,7 @@ class IgnCore: self.last_timer_event = 0 self.start_time = int(time.time()) self.major_version = "IGNCore v2.9" - self.minor_version = "4" + self.minor_version = "5" self.incoming_queue = FifoQueue() self.mass_message_queue = None self.conns = DictObject() diff --git a/core/lookup/org_pork_service.py b/core/lookup/org_pork_service.py index c010cec..d00e0d5 100644 --- a/core/lookup/org_pork_service.py +++ b/core/lookup/org_pork_service.py @@ -35,6 +35,7 @@ class OrgPorkService: cache_result = self.cache_service.retrieve(self.CACHE_GROUP, cache_key) is_cache = False + result = None if cache_result and cache_result.last_modified > (t - self.CACHE_MAX_AGE): result = json.loads(cache_result.data) is_cache = True @@ -43,8 +44,13 @@ class OrgPorkService: try: r = requests.get(url, timeout=5) - result = r.json() - + d = r.json() + if len(d[1]) > 0: + result = d + elif cache_result: + result = json.loads(cache_result.data) + if not result: + return None # if data is invalid if result[0]["ORG_INSTANCE"] != org_id: result = None diff --git a/core/text.py b/core/text.py index 3feffec..87c7c1e 100644 --- a/core/text.py +++ b/core/text.py @@ -113,7 +113,8 @@ class Text: return count def zfill(self, numb, highest_number): - return f"{(self.get_count_digits(highest_number) - self.get_count_digits(numb)) * '0'}{numb}" + val = (self.get_count_digits(highest_number) - self.get_count_digits(numb)) + return f"{(''+ (val * '0')+'') if val > 0 else ''}{numb}" def format_pagination(self, data, offset, page, formatter, title, no_data_msg, cmd, page_size=10, headline=""): selected = data[offset:offset + page_size] @@ -330,7 +331,7 @@ class Text: def format_message(self, msg, replace_br=True): for t in ["", "", "", "", "", "", "", "", "", "", "", "", "", - "", "", "", "", ""]: + "", "", "", "", "", ""]: msg = msg.replace(t, "") if replace_br: msg = msg.replace("
", "\n") @@ -344,6 +345,7 @@ class Text: .replace("", "") \ .replace("", "") \ .replace("", "") \ + .replace("", "") \ .replace("", "") \ .replace("", "") \ .replace("", "") \ diff --git a/modules/core/accounting/account_controller.py b/modules/core/accounting/account_controller.py index 49201e4..9a428e3 100644 --- a/modules/core/accounting/account_controller.py +++ b/modules/core/accounting/account_controller.py @@ -230,7 +230,7 @@ class AccountController: if len(alts) < 1: return "No Account registered for this user." prefs = self.preferences.get_pref_view_small(alts[0]) - response = f"
{alts[0].name}'s Account
\n\n" \ + response = f"" \ f" Owner: {alts[0].name} ({alts[0].char_id}) " \ f"[{self.text.make_tellcmd('W', f'whois {alts[0].name}')}] " \ f"[{self.text.make_tellcmd('Alts', f'alts {alts[0].name}')}] " \ diff --git a/modules/core/discord/discord_command_handler.py b/modules/core/discord/discord_command_handler.py index ae9cf4f..e2b4ab4 100644 --- a/modules/core/discord/discord_command_handler.py +++ b/modules/core/discord/discord_command_handler.py @@ -69,7 +69,7 @@ class DiscordCommandHandler(BaseModule): 'discord_handle': f'{ctx.author.name}#{ctx.author.discriminator}'}), reply, reply) - if type(reply) == ChatBlob: + elif type(reply) == ChatBlob: if reply.embed: embeds = Embed(title=self.parseDiscord(f"{reply.page_prefix} {reply.title} {reply.page_postfix}"), color=0x00FF00, @@ -89,22 +89,36 @@ class DiscordCommandHandler(BaseModule): reply, reply) if embeds: if len(embeds.description) > 4000: - body = self.discord.text.split_by_separators(embeds.description, 4000, 10) + body = self.discord.text.split_by_separators(embeds.description, 3000, 10) tmp1 = embeds embeds = [] for x, e in enumerate(body): tmp = tmp1.copy() tmp.description = e - tmp.title += f"*Page ({x}/{len(body)})*" + tmp.title += f"*Page ({x+1}/{len(body)})*" embeds.append(tmp) + else: embeds = [embeds] - if len(rsp) > 2000: - rsp = self.discord.text.split_by_separators(rsp, 2000) - for page in rsp: - self.discord.client.loop.create_task(ctx.reply(page)) + # if len(rsp) > 2000: + # rsp = self.discord.text.split_by_separators(rsp, 2000) + # for page in rsp: + # self.discord.client.loop.create_task(ctx.reply(page)) + # else: + tmp = [] + _len = 0 + for x in embeds: + _len += len(x.description) + if _len < 6000: + tmp.append(x) + else: + self.discord.client.loop.create_task(ctx.reply(rsp, embeds=tmp)) + tmp = [x] + _len = len(x.description) + if len(tmp) > 0: + self.discord.client.loop.create_task(ctx.reply(rsp, embeds=tmp)) else: - self.discord.client.loop.create_task(ctx.reply(rsp, embeds=embeds)) + self.discord.client.loop.create_task(ctx.reply(rsp)) def parseDiscord(self, ctx: str, blob=False): proficon = {1: "sold", 2: "ma", 3: "eng", 4: "fixer", 5: "agent", 6: "adv", 7: "trad", 8: "crat", 9: "enf", @@ -115,8 +129,9 @@ class DiscordCommandHandler(BaseModule): f"{self.discord.util.get_profession(proficon.get(int(match)))}") for x in ["`", '*', '_', '|']: - ctx = ctx.replace(x, f' \\{x.strip()}') + ctx = ctx.replace(x, f'\\{x}') for pattern, sub in [(r"(
|\n|
)", r"\n"), + (r"`", "ยด"), (r"(.*?)", r"**\1**"), (r"(.*?)", r"**__\1__**"), (r"
(.*?)
", r"**__\1__**"), @@ -126,8 +141,9 @@ class DiscordCommandHandler(BaseModule): (r"", self.bot.get_char_name()), (r"", self.discord.setting_service.get_value("symbol")), # (r"(.+?)<\/a>", r"`\2`"), - (r"(.+?)<\/a>", r"[\4](https://aoitems.com/item\/\2\/\3/)" if blob else r"`\4`"), - (r"(.+?)<\/a>", r"[\4](https://aoitems.com/item\/\2\/\3/)" if blob else r"`\4`"), + (r"(.+?)", r"[\3](\2)"), + (r"(.+?)<\/a>", r"[\4](https://aoitems.com/item/\2/\3/)" if blob else r"`\4`"), + (r"(.+?)<\/a>", r"[\4](https://aoitems.com/item/\2/\3/)" if blob else r"`\4`"), (r"<(.+?)>\s*?<\1>", ''), (r"", ''), (r"(.*?)", r'`\1`'), @@ -147,9 +163,15 @@ class DiscordCommandHandler(BaseModule): while cnt > 0: ctx, cnt = re.subn(r"<(black|white|yellow|blue|green|red|orange|grey|cyan|violet)>(.*?|\d+?)", r"\2", ctx) + ctx.rstrip().rstrip(">") + y = re.split('(`.+?`)', ctx) + ctx = "" + for x in y: + if x.startswith("`"): + for var in ['*', '_', '|']: + x = x.replace(f"\\{var}", f'{var}') + ctx += x - ctx.rstrip() - ctx.rstrip(">") return self.discord.text.strip_html_tags(ctx) @command(command="discord", params=[Const("invite")], access_level="member", diff --git a/modules/core/discord/discord_data.py b/modules/core/discord/discord_data.py index 1765f89..a5a582b 100644 --- a/modules/core/discord/discord_data.py +++ b/modules/core/discord/discord_data.py @@ -18,7 +18,7 @@ class DiscordData: return self.db.exec("UPDATE account SET discord_handle = ? WHERE discord_id = ?", [handle, discord_id]) def get_discord_invite(self, invite): - return self.db.query_single("SELECT * FROM account WHERE discord_invite = ? and a.main=a.char_id", [invite]) + return self.db.query_single("SELECT * FROM account a WHERE discord_invite = ? and a.main=a.char_id", [invite]) def get_account_discord_id(self, discord_id): return self.db.query_single("SELECT * FROM account a left join player p on a.char_id=p.char_id WHERE a.discord_id = ? and a.main=a.char_id", [discord_id]) \ No newline at end of file diff --git a/modules/core/private_channel/private_channel_controller.py b/modules/core/private_channel/private_channel_controller.py index 65e7dd1..9dc48d5 100644 --- a/modules/core/private_channel/private_channel_controller.py +++ b/modules/core/private_channel/private_channel_controller.py @@ -66,7 +66,7 @@ class PrivateChannelController: @event("connect", "Reinvite previous raiders") def reinvite_all(self, _, _1): - for user in self.reinvite: + for user in set(self.reinvite): self.bot.send_mass_message(user, "You have been reinvited into my private channel " "after a bot restart or crash; Sorry for the inconvenience.") self.priv.invite(user) diff --git a/modules/onlinebot/online/org_controller.py b/modules/onlinebot/online/org_controller.py index cc84edc..757b5ad 100644 --- a/modules/onlinebot/online/org_controller.py +++ b/modules/onlinebot/online/org_controller.py @@ -203,7 +203,7 @@ class OrgController: "left join all_orgs a on o.org_id = a.org_id order by lower(a.org_name)") for org in ours: result = requests.get(self.single_org_uri % org.org_id).json() - if result: + if result and len(result[1]) > 0: self.cache.store('org_roster', f"{org.org_id}.5.json", json.dumps(result)) else: result = json.loads(self.cache.retrieve('org_roster', f"{org.org_id}.5.json").data) diff --git a/modules/orgbot/broadcast/broadcast_controller.py b/modules/orgbot/broadcast/broadcast_controller.py index 713970c..c62546f 100644 --- a/modules/orgbot/broadcast/broadcast_controller.py +++ b/modules/orgbot/broadcast/broadcast_controller.py @@ -1,4 +1,4 @@ -import time +#import time from core.chat_blob import ChatBlob from core.command_param_types import Const, Character, Options, Any diff --git a/modules/orgbot/org/org_controller.py b/modules/orgbot/org/org_controller.py index 926dd84..272666b 100644 --- a/modules/orgbot/org/org_controller.py +++ b/modules/orgbot/org/org_controller.py @@ -85,7 +85,7 @@ class OrgChannelController: if not self.bot.is_ready(): return main = self.account_service.get_account(event_data.char_info.char_id) - alt = f":: Alt of {main.name} " if event_data.account else "" + alt = f":: Alt of {main.name} " if event_data.account and main.name else "" if main.char_id == event_data.char_info.char_id: alt = "" logon = f" :: {event_data.account.logon}" if event_data.account.logon else "" diff --git a/modules/raidbot/raid/raidbot_controller.py b/modules/raidbot/raid/raidbot_controller.py index 75fb199..cf656cc 100644 --- a/modules/raidbot/raid/raidbot_controller.py +++ b/modules/raidbot/raid/raidbot_controller.py @@ -1,4 +1,5 @@ import time +from collections import OrderedDict from typing import Union from core.aochat import server_packets @@ -9,6 +10,7 @@ from core.command_param_types import Const, Int, Any, Options, Character from core.command_request import CommandRequest from core.db import DB from core.decorators import instance, command, timerevent, event +from core.dict_object import DictObject from core.event_service import EventService from core.lookup.character_service import CharacterService from core.lookup.pork_service import PorkService @@ -20,6 +22,7 @@ from core.igncore import IgnCore from core.util import Util from modules.core.accounting.services.account_service import AccountService from modules.raidbot.raid.preset_controller import PresetController +from modules.standard.online.online_controller import OnlineController from modules.standard.raid.leader_controller import LeaderController @@ -75,6 +78,7 @@ class RaidbotController(BaseModule): self.private_channel_service: PrivateChannelService = registry.get_instance("private_channel_service") self.account_service: AccountService = registry.get_instance("account_service") self.buddy_service: BuddyService = registry.get_instance("buddy_service") + self.online: OnlineController = registry.get_instance("online_controller") @event("connect", "Adds all raiders to buddylist") def connect(self, _, _1): @@ -192,7 +196,7 @@ class RaidbotController(BaseModule): if not char.char_id: return self.getresp("global", "char_not_found", {"char": char.name}) if not self.private_channel_service.in_private_channel(char.char_id): - return f"The player {char.name} is not in my private channel. Could not add him." + return f"The player {char.name} is not in my private channel. You cannot add him/her to the raid." main_id = self.account_service.get_main(char.char_id).char_id in_raid = self.is_in_raid(main_id) if in_raid is not None: @@ -365,6 +369,47 @@ class RaidbotController(BaseModule): raider_names.clear() return ChatBlob("Active check", blob) + @command(command="raid", params=[Const("list")], description="Get a list if (in)active raiders.", + access_level="leader", sub_command="manage") + def raid_check_cmd(self, _1, _): + if not self.raid: + return self.NO_RAID_RUNNING_RESPONSE + + raiders = {} + o = self.online.online_display.get_online_players("ORDER BY p.profession, p.name", "", [self.bot.get_char_name(), self.bot.get_char_id()]) + users = [] + for x in self.raid.raiders: + if x.is_active: + users.append(self.character_service.resolve_char_to_name(x.active_id)) + + for x in o: + if x.channel_id not in [1, 2, 4] and x.name not in users: + continue + if x.name in raiders.keys(): + if raiders[x.name].channel_id < x.channel_id: + continue + raiders[x.name] = x + if x.name in users: + raiders[x.name].update({'in_raid': True}) + else: + raiders[x.name].update({'in_raid': False}) + + raiders = DictObject(raiders) + blob = "" + for name, raider in raiders.items(): + if not raider.in_raid and raider.channel_id > 2: + continue + entry = f"{self.util.get_prof_icon(raider.profession)} " \ + f"{self.text.zfill(raider.level, 220)}/{self.text.zfill(raider.ai_level, 30)} " \ + f"{raider.name} [{raider.main_name or raider.name}] - " \ + f"{'In Raid' if raider.in_raid else 'Not in Raid'}" + if raider.in_raid: + entry += f" - [{self.text.make_tellcmd('Kick', f'raid kick {raider.name} ')}]" + else: + entry += f" - [{self.text.make_tellcmd('Add', f'raid add {raider.name}')}]" + blob += f"{entry}\n" + return ChatBlob("Raid list", blob) + @command(command="raid", params=[Const("kick"), Character("char"), Any("reason")], description="Set raider as kicked with a reason", access_level="leader", sub_command="manage") def raid_kick_cmd(self, request, _2, char: SenderObj, reason: str): diff --git a/modules/standard/items/what_buffs_controller.py b/modules/standard/items/what_buffs_controller.py index 51dc461..00ed657 100644 --- a/modules/standard/items/what_buffs_controller.py +++ b/modules/standard/items/what_buffs_controller.py @@ -141,4 +141,4 @@ class WhatBuffsController: return ChatBlob(f"Whatbuffs - {skill.name} {item_type} ({len(data):d})", blob) def get_footer(self): - return "\nItem DB Extraction Info provided by Unk" + return "\nItem DB Extraction Info provided by IGNCore" diff --git a/modules/standard/level/level_controller.py b/modules/standard/level/level_controller.py index fe83a81..f1db8e0 100644 --- a/modules/standard/level/level_controller.py +++ b/modules/standard/level/level_controller.py @@ -45,7 +45,7 @@ class LevelController: @command(command="mission", params=[Int("mission_level")], access_level="member", description="Show what character levels can roll a specified mission level", - extended_description="Updated mission levels provided by Lucier") + extended_description="Updated mission levels provided by Zetabyte") def mission_cmd(self, _, level): if 1 <= level <= 250: levels = self.get_mission_levels(level) diff --git a/modules/standard/news/news_controller.py b/modules/standard/news/news_controller.py index 16afcfb..ba599a9 100644 --- a/modules/standard/news/news_controller.py +++ b/modules/standard/news/news_controller.py @@ -134,10 +134,7 @@ hh:mm - DD.MM.YYYY out += f"[Starts in {self.util.time_to_readable(next_event.start - time.time())}] " \ f"[by {name}]\n" - for timer in self.worldboss.timer_data: - msg = self.worldboss.show_user(timer) - if msg != "No timers cached; please try again later.": - out += " " + msg + "\n" + out += self.worldboss.getWBTimer() return out def get_next_event(self): diff --git a/modules/standard/news/worldboss_controller.py b/modules/standard/news/worldboss_controller.py index 51858ba..2c18085 100644 --- a/modules/standard/news/worldboss_controller.py +++ b/modules/standard/news/worldboss_controller.py @@ -1,10 +1,10 @@ -import time +import datetime +from typing import Union from core.chat_blob import ChatBlob from core.command_alias_service import CommandAliasService -from core.command_param_types import Any -from core.decorators import instance, command, event, setting -from core.dict_object import DictObject +from core.command_param_types import Any, Const +from core.decorators import instance, command, event from core.igncore import IgnCore from core.job_scheduler import JobScheduler from core.logger import Logger @@ -16,17 +16,66 @@ from core.util import Util from modules.standard.datanet.ws_controller import WebsocketRelayController +class TimerObj: + name: str = "" + spawn: int = 0 + mortal: int = 0 + time: int = 0 + spam: int = 0 + + def __init__(self, obj): + self.name = obj.name + self.spawn = obj.spawn + self.mortal = obj.mortal + self.time = obj.time + + def update(self, obj): + self.spawn = obj.spawn + self.time = obj.time + + def __str__(self): + return f"{self.name} " \ + f"\n\tspawn: {datetime.datetime.fromtimestamp(self.spawn, tz=datetime.timezone.utc).strftime('%H:%M:%S')}" \ + f"\n\tmortal delta: {self.mortal}" \ + f"\n\tmortal: {datetime.datetime.fromtimestamp(self.time, tz=datetime.timezone.utc).strftime('%H:%M:%S')}" \ + f"\n\tImmortal: {self.alive_immortal()}" \ + f"\n\tMortal killable: {self.alive_mortal()}"\ + f"\n\t{self.name} - {self.spawn} - {self.mortal} - {self.time} - {self.spam} - {self.getTime()}" + + def alive_immortal(self, spam=False) -> bool: + if spam: + return self.spam < self.spawn < self.time and self.time > self.getTime() + return self.time > self.getTime() + + def alive_mortal(self, spam=False) -> bool: + if spam: + return self.spam < self.time < (self.getTime()) and (self.time+60) > self.getTime() + return self.time < self.getTime() + + @classmethod + def getTime(cls) -> int: + return int(datetime.datetime.now().timestamp()) + + def get_respawn_timer(self): + if self.name in ["Tarasque", "Loren Warr", "The Hollow Reaper", "Cerubin The Reborn"]: + return 9*60*60 + elif self.name in ["Vizaresh"]: + return 17*60*60 + elif self.name in ["Atma"]: + return 3 * 60 * 60 + elif self.name in ["T.A.M.", "Zaal The Immortal"]: + return 6 * 60 * 60 + elif self.name in ["Abmouth Indomitus"]: + return 0 + + @instance() class WorldBossController: - # Timers are provided through a local websocket relay, which gets fed by an external API. - # example timer data: - # [{"name":"Tarasque","time": }, - # {"name":"Vizaresh","time": }] timer_data = [] alerts = [480 * 60, 360 * 60, 240 * 60, 120 * 60, 60 * 60, 60 * 15, 60 * 5, 60 * 3, 60 * 2, 60, 30, 15, 5, 0] - jobs = [] + jobs = {} def inject(self, registry): self.logger = Logger(__name__) @@ -48,80 +97,46 @@ class WorldBossController: self.command_alias_service.add_alias("loren", "wb loren") self.command_alias_service.add_alias("reaper", "wb reaper") + def spam_timer(self, time, mob, msg): + self.send_warn(msg) + self.jobs.pop(mob, 0) + @event(WebsocketRelayController.WS_RELAY, "save most current local_timers") def get_timer(self, _, data): + # print(data) if data.type != "timer": return - local_timers = {} - spam = True if self.setting_service.get_value("timer_spam") == "1" else False + local = {} + for x in data.payload: + found = False + for y in self.timer_data: + if y.name == x.name and not found: + y.update(x) + found = True + if not found: + self.timer_data.append(TimerObj(x)) + # print("-"*50) for x in self.timer_data: - local_timers[x['name']] = x['time'] - for row in data.payload: - spawn = self.get_spawn(row) - if not spawn: - continue - if row['name'] not in local_timers: - self.timer_data.append(row) - if spam: - for x in self.jobs: - if x['name'] == row['name']: - self.job_scheduler.cancel_job(x['id']) - self.jobs.append( - {'name': row['name'], 'id': self.job_scheduler.delayed_job(self.timer_alert, 2, spawn)}) - continue - elif (local_timers[row['name']] + 1) < row['time']: - for timer in self.timer_data: - if timer['name'] == row['name']: - timer['time'] = row['time'] - if spam: - for job in self.jobs: - if job['name'] == row['name']: - self.job_scheduler.cancel_job(job['id']) - alert_duration = self.get_next_alert(spawn.at - time.time()) - job_id = self.job_scheduler.scheduled_job(self.timer_alert, 2, - spawn) - job['id'] = job_id - - def get_spawn(self, timer): - timer = DictObject(timer) - if timer.name == "Loren Warr": - data = self.calc_spawn_mortal(timer.time, 9 * 60 * 60, 15 * 60) - elif timer.name == "Tarasque": - data = self.calc_spawn_mortal(timer.time, 9 * 60 * 60, 30 * 60) - elif timer.name == "The Hollow Reaper": - data = self.calc_spawn_mortal(timer.time, 9 * 60 * 60, 15 * 60) - elif timer.name == "Vizaresh": - data = self.calc_spawn_mortal(timer.time, 17 * 60 * 60, 6 * 60) - else: - return None - if not data: - return - if data.spawn < time.time(): - if data.mortal > time.time(): - return DictObject( - {'name': timer.name, 'type': 'mortal', 'time': data.mortal - time.time(), 'at': data.mortal, - 'data': data}) - return DictObject( - {'name': timer.name, 'type': 'spawn', 'time': data.spawn - time.time(), 'at': data.spawn, 'data': data}) - - def calc_spawn_mortal(self, last, respawn, immortal): - pop = last + respawn - attackable = pop + immortal - now = time.time() - - # Mortal - if immortal > attackable - now > 0: - return DictObject({'spawn': pop, 'mortal': attackable}) - elif immortal > attackable + respawn - now > 0: - return DictObject({'spawn': pop + respawn, 'mortal': attackable + respawn}) - - # Spawn - elif respawn > (pop - now) > 0: - return DictObject({'spawn': pop, 'mortal': pop}) - elif respawn > pop + respawn - now > 0: - return DictObject({'spawn': attackable + respawn, 'mortal': attackable + respawn + immortal}) - elif last - now > 0: - return DictObject({'spawn': last - immortal, 'mortal': last}) + x: TimerObj + # print(x.__str__()) + if x.alive_immortal(True): + x.spam = x.getTime() + job = self.jobs.pop(x.name, None) + if job: + self.job_scheduler.cancel_job(job) + self.send_warn(f"{x.name} has spawned.") + i = self.job_scheduler.scheduled_job(self.spam_timer, x.time-1, mob=x.name, msg=f"{x.name} is now mortal.") + self.jobs[x.name] = i + elif x.alive_mortal(True): + x.spam = x.getTime() + job = self.jobs.pop(x.name, None) + if job: + self.job_scheduler.cancel_job(job) + self.send_warn(f"{x.name} is now mortal.") + if x.get_respawn_timer() != 0: + i = self.job_scheduler.scheduled_job(self.spam_timer, x.time+x.get_respawn_timer(), mob=x.name, msg=f"{x.name} should spawn soon.") + self.jobs[x.name] = i + # print("-"*50) def send_warn(self, msg): self.relay_hub.send_message("timers", None, f"[WB] {msg}", f"[WB] {msg}") @@ -135,65 +150,76 @@ class WorldBossController: @command(command="wb", params=[Any("worldboss", is_optional=True)], description="Displays the next worldboss spawns", access_level="member") def show_worldboss(self, request, boss: str): - if boss: - boss = boss.lower() - if boss in ["tara", "tarasque"]: - boss = "Tarasque" - elif boss in ["viza", "vizaresh", "gaunt", "gauntlet"]: - boss = "Vizaresh" - elif boss in ["loren", "loren warr", "loren war", "lw"]: - boss = "Loren Warr" - elif boss in ["thr", "the hollow", "the hollow reaper", "reaper"]: - boss = "The Hollow Reaper" - for x in self.timer_data: - if x['name'] == boss: - return self.show_user(x) - else: - blob = "\n".join([self.show_user(x) for x in self.timer_data if - self.show_user(x) != "No timers cached; please try again later."]) - return ChatBlob("Next Worldboss spawns", blob) + return ChatBlob("Next Worldboss spawns", self.getWBTimer()) + + @command(command="wb", params=[Const("settings")], + description="Worldboss Settings", access_level="moderator", sub_command="mod") + def wb_settings_cmd(self, request, boss: str): + pass def show_user(self, timer): - timer = self.get_spawn(timer) + timer = self.getWBTimer(timer) if not timer: return "No timers cached; please try again later." - if timer.type == "mortal": - return f"{timer.name} :: mortal in {self.util.format_time(timer.time)}" - elif timer.type == "spawn": - return f"{timer.name} :: spawn in {self.util.format_time(timer.time)}" + if timer.spawn < timer.getTime() < timer.time: + return f"{timer.name} :: mortal in {self.util.format_time(timer.time-timer.spawn)}" + elif timer.time < timer.getTime(): + rt = timer.get_respawn_timer() + + return f"{timer.name} :: spawn in {self.util.format_time(timer.time + timer.get_respawn_timer())}" + + def getWBTimer(self, name=None) -> Union[TimerObj, list[TimerObj]]: + if not name: + blob = "" + for x in self.timer_data: + x: TimerObj + if x.alive_immortal(): + blob += f" {x.name} :: mortal in {self.util.format_time(x.time - x.getTime())}\n" + else: + rt = x.get_respawn_timer() + if rt != 0: + if (x.time + rt) < x.getTime() < (x.time + 2 * rt): + blob += f" {x.name} :: should have spawned {self.util.format_time(abs(x.getTime() - (x.time + rt)))} ago\n" + continue + blob += f" {x.name} :: spawn in {self.util.format_time(x.time + rt - x.getTime())}\n" + return blob + for x in self.timer_data: + if x.name == name: + return x def timer_alert(self, t, timer): - if self.setting_service.get_value("timer_spam") == "0": - return - for row in self.timer_data: - if row["name"] == timer.name: - timer = self.get_spawn(row) - if not timer.at: - return - alert_duration = self.get_next_alert(timer.at - time.time()) - if timer.at - time.time() < 1: - if timer.type == "mortal": - self.send_warn(f"{timer.name} :: is now mortal") - self.jobs = [x for x in self.jobs if x['name'] != timer.name] - elif timer.type == "spawn": - self.send_warn(f"{timer.name} :: has just spawned") - self.jobs = [x for x in self.jobs if x['name'] != timer.name] - else: - if timer.type == "mortal": - self.send_warn(f"{timer.name} :: mortal in {self.util.format_time(timer.time)}") - elif timer.type == "spawn": - if alert_duration > 60 * 2: - self.send_warn( - f"{timer.name} :: spawn in {self.util.format_time(timer.time)}") - for row in self.timer_data: - if row["name"] == timer.name: - timer = self.get_spawn(row) - job_id = self.job_scheduler.scheduled_job(self.timer_alert, time.time() + alert_duration, timer) - for job in self.jobs: - if job['name'] == timer.name: - job['id'] = job_id - return - self.jobs.append({'name': timer.name, 'id': job_id}) + pass + # if self.setting_service.get_value("timer_spam") == "0": + # return + # for row in self.timer_data: + # if row["name"] == timer.name: + # timer = self.get_spawn(row) + # if not timer.at: + # return + # alert_duration = self.get_next_alert(timer.at - time.time()) + # if timer.at - time.time() < 1: + # if timer.type == "mortal": + # self.send_warn(f"{timer.name} :: is now mortal") + # self.jobs = [x for x in self.jobs if x['name'] != timer.name] + # elif timer.type == "spawn": + # self.send_warn(f"{timer.name} :: has just spawned") + # self.jobs = [x for x in self.jobs if x['name'] != timer.name] + # else: + # if timer.type == "mortal": + # self.send_warn(f"{timer.name} :: mortal in {self.util.format_time(timer.time)}") + # elif timer.type == "spawn": + # if alert_duration > 60 * 2: + # self.send_warn( + # f"{timer.name} :: spawn in {self.util.format_time(timer.time)}") + # for row in self.timer_data: + # if row["name"] == timer.name: + # timer = self.get_spawn(row) + # job_id = self.job_scheduler.scheduled_job(self.timer_alert, time.time() + alert_duration, timer) + # for job in self.jobs: + # if job['name'] == timer.name: + # job['id'] = job_id + # return + # self.jobs.append({'name': timer.name, 'id': job_id}) # @setting('alert_times', '[480 * 60, 360 * 60, 240 * 60, 120 * 60, 60 * 60, 60 * 15, 60 * 5, 60 * 3, 60 * 2, 60, 30, 15, 5, 0]', 'Worldboss timer spam messages (ETA and actual)') # def alert_times(self): diff --git a/modules/standard/online/online_controller.py b/modules/standard/online/online_controller.py index 7b05564..c962134 100644 --- a/modules/standard/online/online_controller.py +++ b/modules/standard/online/online_controller.py @@ -10,6 +10,7 @@ from core.command_request import CommandRequest from core.db import DB from core.decorators import instance, event, command from core.dict_object import DictObject +from core.event_service import EventService from core.fifo_queue import FifoQueue from core.igncore import IgnCore from core.logger import Logger @@ -49,6 +50,7 @@ class OnlineController: self.online_display: OnlineDisplay = OnlineDisplay(self.text, self.util, self.db, self.afk_list) self.account_service: AccountService = registry.get_instance("account_service") self.character_service: CharacterService = registry.get_instance("character_service") + self.event_service: EventService = registry.get_instance("event_service") def pre_start(self): self.db.exec("DROP TABLE IF EXISTS online") @@ -92,7 +94,7 @@ class OnlineController: while self.bot.status != BotStatus.SHUTDOWN: data, channel, logged = self.awaiting_data.get() buddy = (self.buddy_service.get_buddy(data.packet.char_id) or {}).get("types", []) - if ("org_member" in buddy) or ("member" in buddy): + if ("org_member" in buddy) or ("member" in buddy) or ("darknet" in buddy): if logged: self.db.exec("INSERT IGNORE INTO online VALUES(?, ?, ?)", [data.packet.char_id, channel, self.bot.get_char_id()]) diff --git a/modules/standard/timers/timer_controller.py b/modules/standard/timers/timer_controller.py index e459208..ef840aa 100644 --- a/modules/standard/timers/timer_controller.py +++ b/modules/standard/timers/timer_controller.py @@ -5,7 +5,7 @@ from core.command_param_types import Any, Const, Time, Options from core.decorators import instance, command from core.igncore import IgnCore from core.registry import Registry -from modules.standard.news.worldboss_controller import WorldBossController +from modules.standard.news.worldboss_controller import WorldBossController, TimerObj class TimerTime(Time): @@ -83,10 +83,9 @@ class TimerController: blob = "" if self.worldboss.timer_data: blob += "
Automatic Timers
\n" - for row in self.worldboss.timer_data: - msg, cnt = getmsg(self.worldboss.show_user(row)) - blob += msg - count += cnt + blob += self.worldboss.getWBTimer() + x: TimerObj = None + count += len([x for x in self.worldboss.timer_data if (x.time+x.get_respawn_timer()) < x.getTime()]) blob += "\n\n
Manual Timers
\n" for timer in data: repeats = "" diff --git a/modules/standard/tower/hot_controller.py b/modules/standard/tower/hot_controller.py index 9b6f8c0..7329be6 100644 --- a/modules/standard/tower/hot_controller.py +++ b/modules/standard/tower/hot_controller.py @@ -147,7 +147,7 @@ class TowerHotController(BaseModule): params.append(min_ql) params.append(max_ql) if faction: - where += " AND c.faction LIKE " + where += " AND c.faction LIKE ?" params.append("%" + faction.capitalize() + "%") data = self.db.query("SELECT a.*, f.short_name, c.org_name, b.min_ql, b.max_ql, " "(CASE WHEN (a.close_time-?) < 0 THEN a.close_time-? +86400 ELSE a.close_time-? END) " diff --git a/requirements.txt b/requirements.txt index 3ed08c0..8129aa3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,13 @@ bbcode==1.1.0 -beautifulsoup4==4.10.0 +beautifulsoup4==4.11.1 cryptography==3.3.2 discord.py @ git+https://github.com/Rapptz/discord.py@1bfe6b2bb160ce802a1f08afed73941a19a0a651 emojis==0.6.0 hjson==3.0.2 -mariadb==1.0.7 -psutil==5.8.0 -pytz==2021.3 -requests==2.26.0 +mariadb==1.0.11 +psutil==5.9.1 +pytz==2022.1 +requests==2.28.0 torpy==1.1.6 websock==1.0.4 websocket-client==1.2.1 \ No newline at end of file