player sessions and coordinate models
All checks were successful
Build and Push Docker Image / build (push) Successful in 13s

This commit is contained in:
Corban-Lee Jones 2024-12-11 21:41:21 +00:00
parent e3da8ec805
commit 55304170b1
2 changed files with 118 additions and 39 deletions

View File

@ -118,7 +118,7 @@ class PlayersCog(commands.Cog):
"""
"""
log.debug("processing connected player")
re_pattern = r'\[(?P<timestamp>.*?)\] (?P<steam_id>\d+) "(?P<username>.*?)" fully connected \((?P<coordinates>\d+,\d+,\d+)\)'
re_pattern = r'\[(?P<timestamp>.*?)\] (?P<steam_id>\d+) "(?P<username>.*?)" fully connected \((?P<x>\d+),(?P<y>\d+),(?P<z>\d+)\)'
re_match = re.search(re_pattern, line)
if not re_match:
@ -126,9 +126,11 @@ class PlayersCog(commands.Cog):
return
player, created = await Player.get_or_create(username=re_match.group("username"))
player.last_connection = datetime.strptime(
re_match.group("timestamp"),
"%m-%d-%y %H:%M:%S.%f"
await player.open_session(
timestamp=datetime.strptime(re_match.group("timestamp"), "%m-%d-%y %H:%M:%S.%f"),
coord_x=re_match.group("x"),
coord_y=re_match.group("y"),
coord_z=re_match.group("z")
)
await player.update_steam_summary(re_match.group("steam_id"), self.bot.steam_api_key)
await player.save()
@ -153,7 +155,7 @@ class PlayersCog(commands.Cog):
"""
"""
log.debug("processing disconnected player")
re_pattern = r'\[(?P<timestamp>.*?)\] (?P<steam_id>\d+) "(?P<username>.*?)" disconnected player \((?P<coordinates>\d+,\d+,\d+)\)'
re_pattern = r'\[(?P<timestamp>.*?)\] (?P<steam_id>\d+) "(?P<username>.*?)" disconnected player \((?P<x>\d+),(?P<y>\d+),(?P<z>\d+)\)'
re_match = re.search(re_pattern, line)
if not re_match:
@ -161,9 +163,11 @@ class PlayersCog(commands.Cog):
return
player, created = await Player.get_or_create(username=re_match.group("username"))
player.last_disconnection = datetime.strptime(
re_match.group("timestamp"),
"%m-%d-%y %H:%M:%S.%f"
await player.close_session(
timestamp=datetime.strptime(re_match.group("timestamp"), "%m-%d-%y %H:%M:%S.%f"),
coord_x=re_match.group("x"),
coord_y=re_match.group("y"),
coord_z=re_match.group("z")
)
await player.update_steam_summary(re_match.group("steam_id"), self.bot.steam_api_key)
await player.save()

View File

@ -3,12 +3,13 @@ Database schemas.
"""
import logging
from datetime import datetime
from datetime import datetime, timedelta
import httpx
from tortoise import fields
from tortoise.queryset import QuerySet
from tortoise.models import Model
from tortoise.functions import Sum
from tortoise.expressions import F
from tortoise.models import Model, Q
from discord import Embed
log = logging.getLogger(__name__)
@ -29,68 +30,141 @@ class SteamProfileSummary(Model):
table = "steam_profile_summary"
class Coordinates(Model):
x = fields.IntField()
y = fields.IntField()
z = fields.IntField()
class Meta:
table = "ingame_coordinates"
class PlayerDeath(Model):
player = fields.ForeignKeyField(
model_name="models.Player",
on_delete=fields.CASCADE
)
coordinate_x = fields.IntField()
coordinate_y = fields.IntField()
coordinate_z = fields.IntField()
cause = fields.CharField(max_length=32)
timestamp = fields.DatetimeField(auto_now_add=True)
coordinates = fields.ForeignKeyField(
model_name="models.Coordinates",
on_delete=fields.CASCADE
)
class Meta:
table = "player_deaths"
class PlayerSession(Model):
player = fields.ForeignKeyField(
model_name="models.Player",
on_delete=fields.CASCADE
)
connected_at = fields.DatetimeField()
disconnected_at = fields.DatetimeField(null=False)
connected_coords = fields.ForeignKeyField(
model_name="models.Coordinates",
on_delete=fields.CASCADE
)
disconnected_coords = fields.ForeignKeyField(
model_name="models.Coordinates",
on_delete=fields.CASCADE,
null=True
)
class Meta:
table = "player_session"
@property
def playtime(self) -> timedelta:
if not self.disconnected_at:
return datetime.now() - self.connected_at
return self.disconnected_at - self.connected_at
class Player(Model):
"""
"""
username = fields.CharField(max_length=20, unique=True)
last_connection = fields.DatetimeField(null=True)
last_disconnection = fields.DatetimeField(null=True)
play_time_seconds = fields.IntField(default=0)
is_dead = fields.BooleanField(default=False)
class Meta:
table = "players"
@property
def is_online(self) -> bool:
"""
"""
if not self.last_connection:
return False
async def get_playtime(self) -> timedelta:
playtime = await PlayerSession.filter(player=self).exclude(disconnected_at=None).annotate(
total_playtime=Sum(F("disconnected_at") - F("connected_at"))
).values()
return timedelta(seconds=playtime[0]["total_playtime"].total_seconds() if playtime else timedelta())
return (self.last_connection and not self.last_disconnection) \
or self.last_connection > self.last_disconnection
async def get_deaths(self) -> QuerySet[PlayerDeath]:
async def get_deaths(self) -> list[PlayerDeath]:
return await PlayerDeath.filter(player=self)
async def add_death(
self,
coord_x: str | int,
coord_y: str | int,
coord_z: str | int,
cause: str,
timestamp: datetime
) -> PlayerDeath:
self,
coord_x: str | int,
coord_y: str | int,
coord_z: str | int,
cause: str,
timestamp: datetime
) -> PlayerDeath:
"""
"""
log.debug("Assigning death to player: %s", self.username)
self.is_dead = True
await self.save()
coordinates = await Coordinates.create(x=coord_x, y=coord_y, z=coord_z)
return await PlayerDeath.create(
player=self,
coordinate_x=coord_x,
coordinate_y=coord_y,
coordinate_z=coord_z,
cause=cause,
timestamp=timestamp
timestamp=timestamp,
coordinates=coordinates
)
async def get_latest_session(self, ignore_closed_sessions: bool = False) -> PlayerSession:
queryset = PlayerSession.filter(player=self)
if ignore_closed_sessions:
queryset = queryset.filter(disconnected_at=None)
return await queryset.last()
async def open_session(
self,
timestamp: datetime,
coord_x: str | int,
coord_y: str | int,
coord_z: str | int,
) -> PlayerSession:
log.debug("creating session for player: %s", self.username)
existing_session = await self.get_latest_session(ignore_closed_sessions=True)
if existing_session:
raise ValueError("Tried to open session while an open one exists.")
coordinates = await Coordinates.create(x=coord_x, y=coord_y, z=coord_z)
return await PlayerSession.create(
player=self,
connected_at=timestamp,
connected_coords=coordinates
)
async def close_session(
self,
timestamp: datetime,
coord_x: str | int,
coord_y: str | int,
coord_z: str | int,
) -> PlayerSession:
log.debug("closing session for player: %s", self.username)
current_session = await self.get_latest_session(ignore_closed_sessions=True)
if not current_session:
raise ValueError("Tried to close session that doesn't exist.")
coordinates = await Coordinates.create(x=coord_x, y=coord_y, z=coord_z)
current_session.disconnected_coords = coordinates
current_session.disconnected_at = timestamp
await current_session.save()
async def get_steam_summary(self) -> SteamProfileSummary | None:
"""
"""
@ -139,13 +213,14 @@ class Player(Model):
raise ValueError("You must fetch the steam_profile_summary before creating an embed.")
death_count = len(await self.get_deaths())
playtime = str(await self.get_playtime())
embed = Embed(
title="Player",
description=(
f"{self.username} ([{summary.profile_name}]({summary.url}))\n"
f"Deaths: {death_count}\n"
f"Playtime ???"
f"Playtime: {playtime}"
)
)
embed.set_thumbnail(url=summary.avatar_url)