17c776faec
-> !wants -> !orgs info -> special cmd's -> !assist -> "afk" for players without active account -> !loot add <item_ref> <count> => nolonger breaks !account Changes: -> grouped !tara, !gaunt, .. into !wb -> Display the most recent news entry on logon (default: enabled) -> improved grouping of !items -> Added the option to authentificate WS connections (Datanet module). This is used in special cases, where the Websocket Server requires the clien tto authentificate itself. (Server sends "#auth", client responds with the auth string) -> Add main name to relaying (priv <-> org) [default: disabled] -> Added logon/logoff messages back -> restricted default access to "dangerous" commands to moderator -> Added optional logging (Private Channel, Org Channel, Tells, ... disabled by default) Rewrite of the Tower Module. -> More verbosity, if enabled in config. by default, GAS and Hot timer only. -> !hot displays currently hot (and in penalty) sites, and these which go hot in < 60 minutes -> !attacks filterable by PF and Site -> display current contract QL's grouped by org: !contracts (requires managed cache)
193 lines
8.6 KiB
Python
193 lines
8.6 KiB
Python
import time
|
|
|
|
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
|
|
from core.dict_object import DictObject
|
|
from core.igncore import IgnCore
|
|
from core.job_scheduler import JobScheduler
|
|
from core.logger import Logger
|
|
from core.setting_service import SettingService
|
|
from core.setting_types import BooleanSettingType
|
|
from core.text import Text
|
|
from core.util import Util
|
|
from modules.standard.datanet.ws_controller import WebsocketRelayController
|
|
|
|
|
|
@instance()
|
|
class WorldBossController:
|
|
# Timers are provided through a local websocket relay, which gets fed by an external API.
|
|
# example timer data:
|
|
# [{"name":"Tarasque","time": <mortal time>},
|
|
# {"name":"Vizaresh","time": <mortal 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 = []
|
|
|
|
def inject(self, registry):
|
|
self.logger = Logger(__name__)
|
|
self.bot: IgnCore = registry.get_instance("bot")
|
|
self.text: Text = registry.get_instance("text")
|
|
self.util: Util = registry.get_instance("util")
|
|
self.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service")
|
|
self.job_scheduler: JobScheduler = registry.get_instance("job_scheduler")
|
|
self.setting_service: SettingService = registry.get_instance("setting_service")
|
|
|
|
def pre_start(self):
|
|
self.setting_service.register(self.module_name, 'timer_spam', False, BooleanSettingType(),
|
|
"should timers be spammed")
|
|
self.command_alias_service.add_alias("tara", "wb tara")
|
|
self.command_alias_service.add_alias("gaunt", "wb gaunt")
|
|
self.command_alias_service.add_alias("loren", "wb loren")
|
|
self.command_alias_service.add_alias("reaper", "wb reaper")
|
|
|
|
@event(WebsocketRelayController.WS_RELAY, "save most current local_timers")
|
|
def get_timer(self, _, data):
|
|
if data.type != "timer":
|
|
return
|
|
local_timers = {}
|
|
spam = True if self.setting_service.get_value("timer_spam") == "1" else False
|
|
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})
|
|
|
|
def send_warn(self, msg):
|
|
self.bot.send_private_channel_message(f"[<red>WB</red>] {msg}")
|
|
|
|
def get_next_alert(self, duration):
|
|
for alert in self.alerts:
|
|
if duration - 1 > alert:
|
|
return duration - alert - 1
|
|
return duration
|
|
|
|
@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)
|
|
|
|
def show_user(self, timer):
|
|
timer = self.get_spawn(timer)
|
|
if not timer:
|
|
return "No timers cached; please try again later."
|
|
if timer.type == "mortal":
|
|
return f"<highlight>{timer.name}</highlight> :: mortal in {self.util.format_time(timer.time)}"
|
|
elif timer.type == "spawn":
|
|
return f"<highlight>{timer.name}</highlight> :: spawn in {self.util.format_time(timer.time)}"
|
|
|
|
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"<highlight>{timer.name}</highlight> :: is now mortal")
|
|
self.jobs = [x for x in self.jobs if x['name'] != timer.name]
|
|
elif timer.type == "spawn":
|
|
self.send_warn(f"<highlight>{timer.name}</highlight> :: 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"<highlight>{timer.name}</highlight> :: mortal in {self.util.format_time(timer.time)}")
|
|
elif timer.type == "spawn":
|
|
if alert_duration > 60 * 2:
|
|
self.send_warn(
|
|
f"<highlight>{timer.name}</highlight> :: 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})
|