Skip to content

Commit

Permalink
Move away from Steam client to full Stratz client
Browse files Browse the repository at this point in the history
- A stratz token is necessary for the app to work
- GSI is necessary for the app to work
- Get game info from Stratz live graphql
- Get player info from Stratz player graphql
- Detect last game with Stratz instead of Opendota
- Added player account level and country code
  • Loading branch information
Philaeux committed Aug 14, 2023
1 parent 96c8ce9 commit ed1dd81
Show file tree
Hide file tree
Showing 298 changed files with 12,983 additions and 1,844 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ __pycache__

src/.wvenv/
src/.venv/
src/dota_notes/data/sqlite.db
src/dota_notes/data/models/sqlite.db

src/build
src/dist
Expand Down
6 changes: 3 additions & 3 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ unix-clean:

# Generate UI from Qt Designer files
unix-gen-ui:
./.venv/Scripts/pyside6-uic ./d2notes/ui/ui_main_window.ui -o ./d2notes/ui/ui_main_window.py --absolute-imports -python-paths .
./.venv/bin/pyside6-uic ./d2notes/ui/ui_main_window.ui -o ./d2notes/ui/ui_main_window.py --absolute-imports -python-paths .

# Generate embedded resources from Qt resource file
unix-gen-resources:
./.venv/Scripts/pyside6-rcc ./d2notes/ui/resources.qrc -o ./d2notes/ui/resources_rc.py
./.venv/bin/pyside6-rcc ./d2notes/ui/resources.qrc -o ./d2notes/ui/resources_rc.py

## Generate a production build
unix-build:
./.venv/Script/pyinstaller --noconfirm ./DotaNotes.spec
./.venv/bin/pyinstaller --noconfirm ./DotaNotes.spec

78 changes: 0 additions & 78 deletions src/dota_notes/app_dota.py

This file was deleted.

34 changes: 12 additions & 22 deletions src/dota_notes/app_flask.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from flask import Flask, request, jsonify

from dota_notes.data.messages import MessageGSI

def flask_process(port, match_info_queue):

def flask_process(port, message_queue_qt):
"""Flask app spawner"""
flask_app = FlaskApp(port, match_info_queue)
flask_app = FlaskApp(port, message_queue_qt)
flask_app.run()


Expand All @@ -12,36 +14,24 @@ class FlaskApp:
Args:
port: port to listen to
match_info_queue: Queue to transmit the match information
message_queue_qt: Queue to transmit the match information to the qt app
"""
def __init__(self, port, match_info_queue):
def __init__(self, port, message_queue_qt):
self.app = Flask(__name__)
self.port = port
self.match_info_queue = match_info_queue
self.message_queue_qt = message_queue_qt
self.last_match_id_sent = 0

@self.app.route('/', methods=['POST'])
def gsi_endpoint():
payload = request.get_json()
if ('map' in payload and "matchid" in payload["map"] and
"player" in payload and
"team2" in payload["player"] and
len(payload["player"]["team2"]) > 1):
info = {
"match_id": int(payload["map"]["matchid"]),
"players": []}
if self.last_match_id_sent == info["match_id"] or info["match_id"] == 0:
if 'map' in payload and "matchid" in payload["map"] and payload["map"]["matchid"] is not None:
info = MessageGSI(int(payload["map"]["matchid"]))
if self.last_match_id_sent == info.match_id or info.match_id == 0:
return jsonify({})
else:
self.last_match_id_sent = info["match_id"]

for team in payload["player"].values():
for player in team.values():
if "accountid" not in player or "name" not in player:
continue
info["players"].append({"accountid": int(player["accountid"]), "name": player["name"]})

self.match_info_queue.put(info)
self.last_match_id_sent = info.match_id
self.message_queue_qt.put(info)
return jsonify({})

def run(self):
Expand Down
48 changes: 5 additions & 43 deletions src/dota_notes/data/messages.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,6 @@
class MessageConnect:
"""Ask the client to connect with specific credentials
class MessageGSI:
"""MatchId send by the GSI"""
match_id: str

Attributes:
user: steam username
password: steam password
"""
user: str
password: str

def __init__(self, user: str, password: str):
self.user = user
self.password = password


class MessageConnectionStatus:
"""Report the status of the connections of steam/dota clients
Attributes:
steam: Steam connection status
dota: Dota connection status
"""
steam: str
dota: str

def __init__(self, steam: str, dota: str):
self.steam = steam
self.dota = dota


class MessageServerIdRequest:
"""Request the server ID a specific account is playing on"""
account_id: str

def __init__(self, account_id: str):
self.account_id = account_id


class MessageServerIdResponse:
"""Server ID where a specific player is playing on"""
server_id: int

def __init__(self, server_id: int):
self.server_id = server_id
def __init__(self, match_id):
self.match_id = match_id
Empty file.
6 changes: 6 additions & 0 deletions src/dota_notes/data/models/base_entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from sqlalchemy.orm import DeclarativeBase


class BaseEntity(DeclarativeBase):
"""Database model base class"""
pass
41 changes: 41 additions & 0 deletions src/dota_notes/data/models/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import os
import sys

from sqlalchemy import create_engine
from sqlalchemy.orm import Session

from dota_notes.data.models.base_entity import BaseEntity
from dota_notes.data.models.settings_entity import SettingEntity


class Database:
"""Singleton defining database URI and unique ressources.
Attributes:
_instance: Singleton instance
uri: database location
engine: database connection used for session generation
"""

_instance = None

def __new__(cls, *args, **kwargs):
"""New overload to create a singleton."""
if not isinstance(cls._instance, cls):
cls._instance = object.__new__(cls)
return cls._instance

def __init__(self):
"""Defines all necessary ressources (URI & engine) and create database if necessary."""
if getattr(sys, 'frozen', False):
file_uri = os.path.dirname(sys.executable)
elif __file__:
file_uri = os.path.dirname(__file__)
self.uri = f"sqlite+pysqlite:///{file_uri}/sqlite.db"
self.engine = create_engine(self.uri, echo=False)
BaseEntity.metadata.create_all(self.engine)

with Session(self.engine) as session:
if session.get(SettingEntity, "version") is None:
session.add(SettingEntity("version", "1"))
session.commit()
Original file line number Diff line number Diff line change
@@ -1,67 +1,9 @@
import os
import sys
from typing import Optional

from sqlalchemy import create_engine, String
from sqlalchemy.orm import Session, DeclarativeBase, mapped_column, Mapped
from sqlalchemy import String
from sqlalchemy.orm import Mapped, mapped_column


class Database(object):
"""Singleton defining database URI and unique ressources.
Attributes:
_instance: Singleton instance
uri: database location
engine: database connection used for session generation
"""

_instance = None

def __new__(cls, *args, **kwargs):
"""New overload to create a singleton."""
if not isinstance(cls._instance, cls):
cls._instance = object.__new__(cls)
return cls._instance

def __init__(self):
"""Defines all necessary ressources (URI & engine) and create database if necessary."""
if getattr(sys, 'frozen', False):
file_uri = os.path.dirname(sys.executable)
elif __file__:
file_uri = os.path.dirname(__file__)
self.uri = 'sqlite+pysqlite:///{0}/sqlite.db'.format(file_uri)
self.engine = create_engine(self.uri, echo=False)
BaseEntity.metadata.create_all(self.engine)

with Session(self.engine) as session:
if session.get(SettingEntity, "version") is None:
session.add(SettingEntity("version", "1"))
session.commit()


class BaseEntity(DeclarativeBase):
"""Database model base class"""
pass


class SettingEntity(BaseEntity):
"""An application setting.
Attributes:
key: unique string defining a setting
value: value of the setting
"""
__tablename__ = 'settings'

key: Mapped[str] = mapped_column(primary_key=True)
value: Mapped[str] = mapped_column()

def __init__(self, key, value):
self.key = key
self.value = value

def __repr__(self) -> str:
return f"Setting(key={self.key!r}, value={self.value!r})"
from dota_notes.data.models.base_entity import BaseEntity


class PlayerEntity(BaseEntity):
Expand All @@ -72,7 +14,9 @@ class PlayerEntity(BaseEntity):
name: last seen name
pro_name: pro name (if fetched from the API)
custom_name: user set name
match_count: number of games played by the user (set by stratz)
smurf: user defined smurf indicator
smurf_stratz: smurf flag (set by stratz)
is_racist: flag
is_sexist: flag
is_toxic: flag
Expand All @@ -87,7 +31,9 @@ class PlayerEntity(BaseEntity):
name: Mapped[str] = mapped_column()
pro_name: Mapped[Optional[str]] = mapped_column()
custom_name: Mapped[str] = mapped_column()
match_count: Mapped[Optional[int]] = mapped_column()
smurf: Mapped[str] = mapped_column()
smurf_stratz: Mapped[Optional[int]] = mapped_column()
is_racist: Mapped[bool] = mapped_column()
is_sexist: Mapped[bool] = mapped_column()
is_toxic: Mapped[bool] = mapped_column()
Expand All @@ -96,13 +42,16 @@ class PlayerEntity(BaseEntity):
destroys_items: Mapped[bool] = mapped_column()
note: Mapped[str] = mapped_column(String(500))

def __init__(self, steam_id, name, pro_name=None, custom_name="", smurf="", is_racist=False, is_sexist=False,
is_toxic=False, is_feeder=False, gives_up=False, destroys_items=False, note=""):
def __init__(self, steam_id, name, pro_name=None, custom_name="", match_count=None, smurf="", smurf_stratz=None,
is_racist=False, is_sexist=False, is_toxic=False, is_feeder=False, gives_up=False,
destroys_items=False, note=""):
self.steam_id = steam_id
self.name = name
self.pro_name = pro_name
self.custom_name = custom_name
self.match_count = match_count
self.smurf = smurf
self.smurf_stratz = smurf_stratz
self.is_racist = is_racist
self.is_sexist = is_sexist
self.is_toxic = is_toxic
Expand All @@ -123,7 +72,9 @@ def make_from_state(player_state):
player_state.name,
player_state.pro_name if player_state.pro_name != "" else None,
player_state.custom_name,
player_state.match_count,
player_state.smurf,
player_state.smurf_stratz,
player_state.is_racist,
player_state.is_sexist,
player_state.is_toxic,
Expand All @@ -141,7 +92,9 @@ def import_export(from_object, to_object):
"""
to_object.pro_name = from_object.pro_name
to_object.custom_name = from_object.custom_name
to_object.match_count = from_object.match_count
to_object.smurf = from_object.smurf
to_object.smurf_stratz = from_object.smurf_stratz
to_object.is_racist = from_object.is_racist
to_object.is_sexist = from_object.is_sexist
to_object.is_toxic = from_object.is_toxic
Expand Down
Loading

0 comments on commit ed1dd81

Please sign in to comment.