-> !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)
This commit is contained in:
2021-11-25 14:09:43 +01:00
parent 2d7ecf4883
commit 17c776faec
44 changed files with 1669 additions and 1249 deletions
+166
View File
@@ -0,0 +1,166 @@
import time
from core.aochat.BaseModule import BaseModule
from core.command_alias_service import CommandAliasService
from core.command_param_types import Options, Int, Any, NamedParameters
from core.db import DB
from core.decorators import instance, command, event
from core.dict_object import DictObject
from core.event_service import EventService
from core.igncore import IgnCore
from core.lookup.pork_service import PorkService
from core.public_channel_service import PublicChannelService
from core.text import Text
from core.util import Util
from modules.standard.helpbot.playfield_controller import PlayfieldController
from modules.standard.tower.tower_events import TowerEventController
@instance()
class TowerHotController(BaseModule):
PAGE_SIZE = 30
# noinspection DuplicatedCode
def inject(self, registry):
self.bot: IgnCore = registry.get_instance("bot")
self.db: DB = registry.get_instance("db")
self.util: Util = registry.get_instance("util")
self.text: Text = registry.get_instance("text")
self.event_service: EventService = registry.get_instance("event_service")
self.pork_service: PorkService = registry.get_instance("pork_service")
self.playfield_controller: PlayfieldController = registry.get_instance("playfield_controller")
self.public_channel_service: PublicChannelService = registry.get_instance("public_channel_service")
self.command_alias_service: CommandAliasService = registry.get_instance("command_alias_service")
@event(event_type=TowerEventController.TOWER_ATTACK_EVENT, description="Mark Sites in penalty as in penalty",
is_enabled=False)
def tower_victory_event(self, _, event_data):
if event_data.attacker.org_id:
self.db.exec("UPDATE towers SET penalty_until=? where org_id=?",
[time.time() + 60 * 60, event_data.attacker.org_id])
@command(command="hot",
params=[Options(['tl1', 'tl2', 'tl3', 'tl4', 'tl5', 'tl6', 'tl7']), Any('faction', is_optional=True),
NamedParameters(["page"])],
access_level="member",
description="Shows hot playfields")
def hot_tl(self, _, tl, faction: str, named_params):
if faction:
if faction.startswith("--page="):
named_params = DictObject({'page': faction[7:]})
faction = None
if faction is not None and faction.lower() not in ['omni', 'clan', 'neut', 'neutral']:
return f"Unknown faction: {faction}"
tl = tl[2:]
page = int(named_params.page or "1")
offset = (page - 1) * self.PAGE_SIZE
towers = self.get_hot_sites_tl(int(tl), faction)
return self.text.format_pagination(towers, offset, page, self.formatter, f"Hot Sites TL{tl} ({len(towers)})",
f"There are no hot sites for TL <highlight>{tl}</highlight>.",
f'hot tl{tl} {faction or ""}', self.PAGE_SIZE)
def formatter(self, row, index, data):
d = {}
if index > 1:
d = data[index - 2]
status = ""
if row.status_time <= 3600:
status += f"<red>5%</red> (closes in {self.util.time_to_readable(row.status_time)})"
elif row.status_time <= (3600 * 6):
status += f"<orange>25%</orange> (closes in {self.util.time_to_readable(row.status_time)})"
else:
status += f"<green>75%</green> (opens in {self.util.time_to_readable(row.status_time - (3600 * 6))})"
if row.penalty_until > time.time():
status += f" <red>In Penalty for: {self.util.time_to_readable(row.penalty_until - time.time())}</red>"
blob = ""
if self.get_ct_type(d.get("ql", 0)) < (tl := self.get_ct_type(row.ql)):
blob += f"<notice>TL{tl}</notice><br>"
space = f"{row.short_name} x{row.site_number}"
place = "_" * (7 - len(space))
return blob + "<tab>" + self.text.make_tellcmd(space,
f'lc {row.short_name} {row.site_number}') + \
f"<black>{place}</black> QL {row.min_ql}/<highlight>{row.ql}</highlight>/{row.max_ql} - " \
f"{self.text.get_formatted_faction(row.faction, row.org_name)}, {status}\n"
@command(command="hot",
params=[Int('level', is_optional=True), Any('faction', is_optional=True), NamedParameters(["page"])],
access_level="member",
description="Shows hot playfields by level")
def hot_level(self, _, level, faction, named_params):
if faction:
if faction.startswith("--page="):
named_params = DictObject({'page': faction[7:]})
faction = None
if faction is not None and faction.lower() not in ['omni', 'clan', 'neut', 'neutral']:
return f"Unknown faction: {faction}"
if level:
if level < 0 | level > 220:
return f"Level out of range: {level}"
page = int(named_params.page or "1")
offset = (page - 1) * self.PAGE_SIZE
towers = self.get_hot_sites(level, faction)
level = f"{level}" if level else ""
faction = f"{faction} " if faction else ""
return self.text.format_pagination(towers, offset, page, self.formatter, f"Hot Towersites ({len(towers)})",
f"There are no hot sites.", f'hot {level}{faction}', self.PAGE_SIZE)
def get_hot_sites(self, level=None, faction=None):
where = ""
now = time.time() % 86400
params = [now, now, now]
if level:
where += " AND l.pvp_min <=? and pvp_max >= ? "
params.append(level)
params.append(level)
if faction:
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) "
" AS status_time FROM tower_sites b "
"LEFT JOIN towers a ON a.pf_id = b.playfield_id AND a.site_number = b.site_number "
"LEFT JOIN level l on a.ql = l.level "
"LEFT JOIN playfields f on a.pf_id = f.id "
f"LEFT JOIN all_orgs c ON a.org_id = c.org_id WHERE close_time IS NOT NULL {where} ORDER BY a.ql",
params)
return [x for x in data if x.status_time - (3600 * 6) < 60 * 60 or x.penalty_until > time.time()]
def get_hot_sites_tl(self, tl=7, faction=None):
min_ql, max_ql = self.util.get_level_range_tl(tl)
where = ""
now = time.time() % 86400
params = [now, now, now]
where += " AND ql between ? and ? "
params.append(min_ql)
params.append(max_ql)
if faction:
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) "
" AS status_time FROM tower_sites b "
"LEFT JOIN towers a ON a.pf_id = b.playfield_id AND a.site_number = b.site_number "
"LEFT JOIN level l on a.ql = l.level "
"LEFT JOIN playfields f on a.pf_id = f.id "
f"LEFT JOIN all_orgs c ON a.org_id = c.org_id WHERE close_time IS NOT NULL {where} ORDER BY a.ql",
params)
return [x for x in data if x.status_time - (3600 * 6) < 60 * 60 or x.penalty_until > time.time()]
def get_ct_type(self, ql):
if ql == 0:
return 0
elif ql < 34:
return 1
elif ql < 82:
return 2
elif ql < 129:
return 3
elif ql < 177:
return 4
elif ql < 201:
return 5
elif ql < 226:
return 6
else:
return 7