player sessions and coordinate models
All checks were successful
Build and Push Docker Image / build (push) Successful in 13s
All checks were successful
Build and Push Docker Image / build (push) Successful in 13s
This commit is contained in:
parent
e3da8ec805
commit
55304170b1
@ -118,7 +118,7 @@ class PlayersCog(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
log.debug("processing connected player")
|
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)
|
re_match = re.search(re_pattern, line)
|
||||||
|
|
||||||
if not re_match:
|
if not re_match:
|
||||||
@ -126,9 +126,11 @@ class PlayersCog(commands.Cog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
player, created = await Player.get_or_create(username=re_match.group("username"))
|
player, created = await Player.get_or_create(username=re_match.group("username"))
|
||||||
player.last_connection = datetime.strptime(
|
await player.open_session(
|
||||||
re_match.group("timestamp"),
|
timestamp=datetime.strptime(re_match.group("timestamp"), "%m-%d-%y %H:%M:%S.%f"),
|
||||||
"%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.update_steam_summary(re_match.group("steam_id"), self.bot.steam_api_key)
|
||||||
await player.save()
|
await player.save()
|
||||||
@ -153,7 +155,7 @@ class PlayersCog(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
log.debug("processing disconnected player")
|
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)
|
re_match = re.search(re_pattern, line)
|
||||||
|
|
||||||
if not re_match:
|
if not re_match:
|
||||||
@ -161,9 +163,11 @@ class PlayersCog(commands.Cog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
player, created = await Player.get_or_create(username=re_match.group("username"))
|
player, created = await Player.get_or_create(username=re_match.group("username"))
|
||||||
player.last_disconnection = datetime.strptime(
|
await player.close_session(
|
||||||
re_match.group("timestamp"),
|
timestamp=datetime.strptime(re_match.group("timestamp"), "%m-%d-%y %H:%M:%S.%f"),
|
||||||
"%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.update_steam_summary(re_match.group("steam_id"), self.bot.steam_api_key)
|
||||||
await player.save()
|
await player.save()
|
||||||
|
137
utils/models.py
137
utils/models.py
@ -3,12 +3,13 @@ Database schemas.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from tortoise import fields
|
from tortoise import fields
|
||||||
from tortoise.queryset import QuerySet
|
from tortoise.functions import Sum
|
||||||
from tortoise.models import Model
|
from tortoise.expressions import F
|
||||||
|
from tortoise.models import Model, Q
|
||||||
from discord import Embed
|
from discord import Embed
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -29,68 +30,141 @@ class SteamProfileSummary(Model):
|
|||||||
table = "steam_profile_summary"
|
table = "steam_profile_summary"
|
||||||
|
|
||||||
|
|
||||||
|
class Coordinates(Model):
|
||||||
|
x = fields.IntField()
|
||||||
|
y = fields.IntField()
|
||||||
|
z = fields.IntField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table = "ingame_coordinates"
|
||||||
|
|
||||||
|
|
||||||
class PlayerDeath(Model):
|
class PlayerDeath(Model):
|
||||||
player = fields.ForeignKeyField(
|
player = fields.ForeignKeyField(
|
||||||
model_name="models.Player",
|
model_name="models.Player",
|
||||||
on_delete=fields.CASCADE
|
on_delete=fields.CASCADE
|
||||||
)
|
)
|
||||||
coordinate_x = fields.IntField()
|
|
||||||
coordinate_y = fields.IntField()
|
|
||||||
coordinate_z = fields.IntField()
|
|
||||||
cause = fields.CharField(max_length=32)
|
cause = fields.CharField(max_length=32)
|
||||||
timestamp = fields.DatetimeField(auto_now_add=True)
|
timestamp = fields.DatetimeField(auto_now_add=True)
|
||||||
|
coordinates = fields.ForeignKeyField(
|
||||||
|
model_name="models.Coordinates",
|
||||||
|
on_delete=fields.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table = "player_deaths"
|
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):
|
class Player(Model):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
username = fields.CharField(max_length=20, unique=True)
|
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)
|
is_dead = fields.BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
table = "players"
|
table = "players"
|
||||||
|
|
||||||
@property
|
async def get_playtime(self) -> timedelta:
|
||||||
def is_online(self) -> bool:
|
playtime = await PlayerSession.filter(player=self).exclude(disconnected_at=None).annotate(
|
||||||
"""
|
total_playtime=Sum(F("disconnected_at") - F("connected_at"))
|
||||||
"""
|
).values()
|
||||||
if not self.last_connection:
|
return timedelta(seconds=playtime[0]["total_playtime"].total_seconds() if playtime else timedelta())
|
||||||
return False
|
|
||||||
|
|
||||||
return (self.last_connection and not self.last_disconnection) \
|
async def get_deaths(self) -> list[PlayerDeath]:
|
||||||
or self.last_connection > self.last_disconnection
|
|
||||||
|
|
||||||
async def get_deaths(self) -> QuerySet[PlayerDeath]:
|
|
||||||
return await PlayerDeath.filter(player=self)
|
return await PlayerDeath.filter(player=self)
|
||||||
|
|
||||||
async def add_death(
|
async def add_death(
|
||||||
self,
|
self,
|
||||||
coord_x: str | int,
|
coord_x: str | int,
|
||||||
coord_y: str | int,
|
coord_y: str | int,
|
||||||
coord_z: str | int,
|
coord_z: str | int,
|
||||||
cause: str,
|
cause: str,
|
||||||
timestamp: datetime
|
timestamp: datetime
|
||||||
) -> PlayerDeath:
|
) -> PlayerDeath:
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
log.debug("Assigning death to player: %s", self.username)
|
log.debug("Assigning death to player: %s", self.username)
|
||||||
self.is_dead = True
|
self.is_dead = True
|
||||||
await self.save()
|
await self.save()
|
||||||
|
coordinates = await Coordinates.create(x=coord_x, y=coord_y, z=coord_z)
|
||||||
return await PlayerDeath.create(
|
return await PlayerDeath.create(
|
||||||
player=self,
|
player=self,
|
||||||
coordinate_x=coord_x,
|
|
||||||
coordinate_y=coord_y,
|
|
||||||
coordinate_z=coord_z,
|
|
||||||
cause=cause,
|
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:
|
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.")
|
raise ValueError("You must fetch the steam_profile_summary before creating an embed.")
|
||||||
|
|
||||||
death_count = len(await self.get_deaths())
|
death_count = len(await self.get_deaths())
|
||||||
|
playtime = str(await self.get_playtime())
|
||||||
|
|
||||||
embed = Embed(
|
embed = Embed(
|
||||||
title="Player",
|
title="Player",
|
||||||
description=(
|
description=(
|
||||||
f"{self.username} ([{summary.profile_name}]({summary.url}))\n"
|
f"{self.username} ([{summary.profile_name}]({summary.url}))\n"
|
||||||
f"Deaths: {death_count}\n"
|
f"Deaths: {death_count}\n"
|
||||||
f"Playtime ???"
|
f"Playtime: {playtime}"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
embed.set_thumbnail(url=summary.avatar_url)
|
embed.set_thumbnail(url=summary.avatar_url)
|
||||||
|
Reference in New Issue
Block a user