-> !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
+170
View File
@@ -0,0 +1,170 @@
import time
from core.chat_blob import ChatBlob
from core.command_param_types import Const, Int, NamedParameters, Any
from core.decorators import instance, command
from core.igncore import IgnCore
from core.logger import Logger
from core.text import Text
from core.util import Util
from modules.standard.helpbot.playfield_controller import PlayfieldController
@instance()
class TowerController:
def __init__(self):
self.logger = Logger(__name__)
def inject(self, registry):
self.bot: IgnCore = registry.get_instance("bot")
self.db = registry.get_instance("db")
self.text: Text = registry.get_instance("text")
self.util: Util = registry.get_instance("util")
self.tower: TowerController = registry.get_instance("tower_controller")
self.playfield_controller: PlayfieldController = registry.get_instance("playfield_controller")
self.public_channel_service = registry.get_instance("public_channel_service")
@command(command="attacks", params=[Const("battle"), Int("battle_id")], access_level="member",
description="Show battle info for a specific battle")
def attacks_battle_cmd(self, _, _1, battle_id):
battle = self.db.query_single(
"SELECT b.*, p.short_name FROM tower_battle b "
"LEFT JOIN playfields p ON p.id = b.playfield_id WHERE b.id = ?",
[battle_id])
if not battle:
return "Could not find battle with ID <highlight>%d</highlight>." % battle_id
t = int(time.time())
attackers = self.db.query("SELECT * FROM tower_attacker WHERE tower_battle_id = ? ORDER BY created_at DESC",
[battle_id])
first_activity = attackers[-1].created_at if len(attackers) > 0 else battle.last_updated
blob = ""
blob += self.format_battle_info(battle, t)
blob += f"Duration: <highlight>" \
f"{self.util.time_to_readable(battle.last_updated - first_activity)}</highlight>\n\n"
blob += "<header2>Attackers:</header2>\n"
for row in attackers:
blob += "<tab>" + self.format_attacker(row)
blob += " " + self.format_timestamp(row.created_at, t)
blob += "\n"
return ChatBlob(f"Battle Info {battle_id}", blob)
@command(command="attacks", params=[NamedParameters(["page"])], access_level="member",
description="Show recent tower attacks and victories")
def attacks_cmd(self, _, named_params):
page = int(named_params.page or "1")
page_size = 30
offset = (page - 1) * page_size
data = self.get_recent_attacks(offset, page_size)
t = int(time.time())
return self.display(page, data, time.time())
@command(command="attacks",
params=[Any("playfield"), Any("site_number", is_optional=True), NamedParameters(["page"])],
access_level="member",
description="Show recent tower attacks and victories")
def cmd_attacks_pf_site(self, _, pf, site, named_params):
page = int(named_params.page or "1")
page_size = 30
offset = (page - 1) * page_size
playfield = self.playfield_controller.get_playfield_by_name_or_id(pf)
if not playfield:
return f"Could not find Playfield <highlight>{pf}</highlight>."
pf = playfield.id
data = self.get_recent_attacks_by_lca(offset, page_size, pf, site)
blob = self.display(page, data, time.time())
if site:
blob.page_postfix = f" in {playfield.short_name} on x{site}"
else:
blob.page_postfix = f" in {playfield.short_name}"
return blob
def display(self, page, data, t):
blob = ""
if page > 1:
blob += " " + self.text.make_chatcmd(f"<< Page {page - 1:d}", self.get_chat_command(page - 1))
if len(data) > 0:
blob += " Page " + str(page)
blob += " " + self.text.make_chatcmd(f"Page {page + 1:d} >>", self.get_chat_command(page + 1))
blob += "\n"
current_battle_id = -1
for row in data:
if current_battle_id != row.battle_id:
blob += "\n<pagebreak>"
current_battle_id = row.battle_id
blob += self.format_battle_info(row, t)
blob += self.text.make_tellcmd("More Info", f"attacks battle {row.battle_id:d}") + "\n"
blob += "<header2>Attackers:</header2>\n"
blob += "<tab>" + self.format_attacker(row) + "\n"
blob = ChatBlob(f"Tower Attacks", blob)
return blob
def format_attacker(self, row):
level = f"{row.att_level}/<green>{row.att_ai_level}</green>" if row.att_ai_level > 0 else f"{row.att_level}"
org = row.att_org_name + " " if row.att_org_name else ""
victor = " - <notice>Winner!</notice>" if row.is_victory else ""
return f"{row.att_char_name or 'Unknown attacker'} ({level} {row.att_profession})" \
f" {org}({row.att_faction}){victor}"
def format_battle_info(self, row, t):
blob = ""
defeated = " - <notice>Defeated!</notice>" if row.is_finished else ""
blob += f"Site: <highlight>{row.short_name} {row.site_number or '?'}</highlight>\n"
blob += f"Defender: <highlight>{row.def_org_name}</highlight> ({row.def_faction}){defeated}\n"
blob += f"Last Activity: {self.format_timestamp(row.last_updated, t)}\n"
return blob
def format_timestamp(self, t, current_t):
return f"<highlight>{self.util.format_datetime(t)}</highlight> " \
f"({self.util.time_to_readable(current_t - t)} ago)"
def get_chat_command(self, page):
return f"/tell <myname> attacks --page={page}"
def get_recent_attacks(self, offset, page_size):
return self.db.query("SELECT b.*, a.*, "
"COALESCE(a.att_level, 0) AS att_level, "
"COALESCE(a.att_ai_level, 0) AS att_ai_level, "
"p.short_name, "
"b.id AS battle_id "
"FROM tower_battle b "
"LEFT JOIN tower_attacker a ON a.tower_battle_id = b.id "
"LEFT JOIN playfields p ON p.id = b.playfield_id "
"ORDER BY b.last_updated DESC, a.created_at DESC "
"LIMIT ?, ?", [offset, page_size])
def get_recent_attacks_by_lca(self, offset, page_size, playfield, site_number=None):
if not site_number:
return self.db.query("SELECT b.*, a.*, "
"COALESCE(a.att_level, 0) AS att_level, "
"COALESCE(a.att_ai_level, 0) AS att_ai_level, "
"p.short_name, "
"b.id AS battle_id "
"FROM tower_battle b "
"LEFT JOIN tower_attacker a ON a.tower_battle_id = b.id "
"LEFT JOIN playfields p ON p.id = b.playfield_id "
"WHERE p.id =? ORDER BY b.last_updated DESC, a.created_at DESC "
"LIMIT ?, ?", [playfield, offset, page_size])
else:
return self.db.query("SELECT b.*, a.*, "
"COALESCE(a.att_level, 0) AS att_level, "
"COALESCE(a.att_ai_level, 0) AS att_ai_level, "
"p.short_name, "
"b.id AS battle_id "
"FROM tower_battle b "
"LEFT JOIN tower_attacker a ON a.tower_battle_id = b.id "
"LEFT JOIN playfields p ON p.id = b.playfield_id "
"WHERE p.id = ? AND b.site_number = ? ORDER BY b.last_updated DESC, a.created_at DESC "
"LIMIT ?, ?", [playfield, site_number, offset, page_size])