122 lines
3.8 KiB
Python
122 lines
3.8 KiB
Python
from dataclasses import dataclass
|
|
from typing import List, Any, Optional
|
|
from enum import Enum
|
|
import re
|
|
|
|
|
|
class EventType(Enum):
|
|
RESONATOR_DEPLOYED = "RESONATOR_DEPLOYED"
|
|
RESONATOR_DESTROYED = "RESONATOR_DESTROYED"
|
|
PORTAL_CAPTURED = "PORTAL_CAPTURED"
|
|
PORTAL_NEUTRALIZED = "PORTAL_NEUTRALIZED"
|
|
PORTAL_UNDER_ATTACK = "PORTAL_UNDER_ATTACK"
|
|
LINK_CREATED = "LINK_CREATED"
|
|
LINK_DESTROYED = "LINK_DESTROYED"
|
|
CONTROL_FIELD_CREATED = "CONTROL_FIELD_CREATED"
|
|
UNKNOWN = "UNKNOWN"
|
|
|
|
|
|
EVENT_TYPE_KEYWORDS = {
|
|
EventType.RESONATOR_DEPLOYED: ["deployed a Resonator on"],
|
|
EventType.RESONATOR_DESTROYED: ["destroyed a Resonator on"],
|
|
EventType.LINK_DESTROYED: ["destroyed the", "Link"],
|
|
EventType.PORTAL_CAPTURED: ["captured"],
|
|
EventType.PORTAL_NEUTRALIZED: ["neutralized by"],
|
|
EventType.PORTAL_UNDER_ATTACK: ["is under attack by"],
|
|
EventType.LINK_CREATED: ["linked from"],
|
|
EventType.CONTROL_FIELD_CREATED: ["created a Control Field"],
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class Markup:
|
|
type: str
|
|
plain: str
|
|
team: str = ""
|
|
name: str = ""
|
|
address: str = ""
|
|
latE6: int = 0
|
|
lngE6: int = 0
|
|
|
|
|
|
@dataclass
|
|
class Plext:
|
|
id: str
|
|
timestamp: int
|
|
text: str
|
|
team: str
|
|
plext_type: str
|
|
categories: int
|
|
markup: List[Markup]
|
|
|
|
@classmethod
|
|
def from_json(cls, data: List[Any]) -> "Plext":
|
|
plext_data = data[2]["plext"]
|
|
markup_data = plext_data["markup"]
|
|
|
|
markup = []
|
|
for m in markup_data:
|
|
markup_type = m[0]
|
|
markup_details = m[1]
|
|
markup.append(
|
|
Markup(
|
|
type=markup_type,
|
|
plain=markup_details.get("plain", ""),
|
|
team=markup_details.get("team", ""),
|
|
name=markup_details.get("name", ""),
|
|
address=markup_details.get("address", ""),
|
|
latE6=markup_details.get("latE6", 0),
|
|
lngE6=markup_details.get("lngE6", 0),
|
|
)
|
|
)
|
|
|
|
return cls(
|
|
id=data[0],
|
|
timestamp=data[1],
|
|
text=plext_data["text"],
|
|
team=plext_data["team"],
|
|
plext_type=plext_data["plextType"],
|
|
categories=plext_data["categories"],
|
|
markup=markup,
|
|
)
|
|
|
|
def get_event_type(self) -> EventType:
|
|
for event_type, keywords in EVENT_TYPE_KEYWORDS.items():
|
|
if all(keyword in self.text for keyword in keywords):
|
|
# A special case for "captured", to avoid matching "destroyed"
|
|
if event_type == EventType.PORTAL_CAPTURED and "destroyed" in self.text:
|
|
continue
|
|
return event_type
|
|
return EventType.UNKNOWN
|
|
|
|
def get_player_name(self) -> str:
|
|
for m in self.markup:
|
|
if m.type == "PLAYER":
|
|
return m.plain
|
|
# If player name is not in markup, try to extract from text
|
|
if "agent" in self.text:
|
|
match = re.search(r"agent (\w+)", self.text)
|
|
if match:
|
|
return match.group(1)
|
|
return ""
|
|
|
|
def get_portal_name(self) -> str:
|
|
for m in self.markup:
|
|
if m.type == "PORTAL":
|
|
return m.name
|
|
# If portal name is not in markup, try to extract from text
|
|
match = re.search(r"(?:deployed|destroyed|captured|linked from|created a Control Field @) (.+?) \(", self.text)
|
|
if not match:
|
|
match = re.search(r"Your Portal (.+?) is under attack by", self.text)
|
|
if not match:
|
|
match = re.search(r"Your Portal (.+?) neutralized by", self.text)
|
|
if match:
|
|
return match.group(1).strip()
|
|
return ""
|
|
|
|
def get_event_coordinates(self) -> Optional[tuple[int, int]]:
|
|
for m in self.markup:
|
|
if m.type == "PORTAL":
|
|
return m.latE6, m.lngE6
|
|
return None
|