polyhattrick 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- polyhattrick-0.1.0/LICENSE +21 -0
- polyhattrick-0.1.0/PKG-INFO +130 -0
- polyhattrick-0.1.0/README.md +59 -0
- polyhattrick-0.1.0/pyproject.toml +81 -0
- polyhattrick-0.1.0/src/polyhattrick/__init__.py +0 -0
- polyhattrick-0.1.0/src/polyhattrick/client/polyhattrick_client.py +86 -0
- polyhattrick-0.1.0/src/polyhattrick/polyhattrick.py +58 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Peter Toth
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polyhattrick
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Live Hattrick scores in your terminal and polybar
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Peter Toth
|
|
8
|
+
Author-email: work@petertoth.de
|
|
9
|
+
Requires-Python: >=3.12,<4.0
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: anyio (==4.12.0)
|
|
15
|
+
Requires-Dist: black (==25.12.0)
|
|
16
|
+
Requires-Dist: build (==1.3.0)
|
|
17
|
+
Requires-Dist: cachecontrol (==0.14.4)
|
|
18
|
+
Requires-Dist: certifi (==2025.11.12)
|
|
19
|
+
Requires-Dist: cffi (==2.0.0)
|
|
20
|
+
Requires-Dist: charset-normalizer (==3.4.4)
|
|
21
|
+
Requires-Dist: cleo (==2.1.0)
|
|
22
|
+
Requires-Dist: click (==8.3.1)
|
|
23
|
+
Requires-Dist: crashtest (==0.4.1)
|
|
24
|
+
Requires-Dist: cryptography (==46.0.3)
|
|
25
|
+
Requires-Dist: distlib (==0.4.0)
|
|
26
|
+
Requires-Dist: dulwich (==0.24.10)
|
|
27
|
+
Requires-Dist: fastjsonschema (==2.21.2)
|
|
28
|
+
Requires-Dist: filelock (==3.20.0)
|
|
29
|
+
Requires-Dist: findpython (==0.7.1)
|
|
30
|
+
Requires-Dist: h11 (==0.16.0)
|
|
31
|
+
Requires-Dist: httpcore (==1.0.9)
|
|
32
|
+
Requires-Dist: httpx (==0.28.1)
|
|
33
|
+
Requires-Dist: idna (==3.11)
|
|
34
|
+
Requires-Dist: installer (==0.7.0)
|
|
35
|
+
Requires-Dist: jaraco-classes (==3.4.0)
|
|
36
|
+
Requires-Dist: jaraco-context (==6.0.1)
|
|
37
|
+
Requires-Dist: jaraco-functools (==4.3.0)
|
|
38
|
+
Requires-Dist: jeepney (==0.9.0)
|
|
39
|
+
Requires-Dist: keyring (==25.7.0)
|
|
40
|
+
Requires-Dist: markdown-it-py (==4.0.0)
|
|
41
|
+
Requires-Dist: mdurl (==0.1.2)
|
|
42
|
+
Requires-Dist: more-itertools (==10.8.0)
|
|
43
|
+
Requires-Dist: msgpack (==1.1.2)
|
|
44
|
+
Requires-Dist: mypy-extensions (==1.1.0)
|
|
45
|
+
Requires-Dist: packaging (==25.0)
|
|
46
|
+
Requires-Dist: pathspec (==0.12.1)
|
|
47
|
+
Requires-Dist: pbs-installer (==2025.12.5)
|
|
48
|
+
Requires-Dist: pkginfo (==1.12.1.2)
|
|
49
|
+
Requires-Dist: platformdirs (==4.5.0)
|
|
50
|
+
Requires-Dist: poetry (==2.2.1)
|
|
51
|
+
Requires-Dist: poetry-core (==2.2.1)
|
|
52
|
+
Requires-Dist: pycparser (==2.23)
|
|
53
|
+
Requires-Dist: pygments (==2.19.2)
|
|
54
|
+
Requires-Dist: pyproject-hooks (==1.2.0)
|
|
55
|
+
Requires-Dist: pytokens (==0.3.0)
|
|
56
|
+
Requires-Dist: rapidfuzz (==3.14.3)
|
|
57
|
+
Requires-Dist: requests (==2.32.5)
|
|
58
|
+
Requires-Dist: requests-toolbelt (==1.0.0)
|
|
59
|
+
Requires-Dist: rich (==14.2.0)
|
|
60
|
+
Requires-Dist: secretstorage (==3.5.0)
|
|
61
|
+
Requires-Dist: shellingham (==1.5.4)
|
|
62
|
+
Requires-Dist: tomlkit (==0.13.3)
|
|
63
|
+
Requires-Dist: trove-classifiers (==2025.12.1.14)
|
|
64
|
+
Requires-Dist: typer (==0.20.0)
|
|
65
|
+
Requires-Dist: typing-extensions (==4.15.0)
|
|
66
|
+
Requires-Dist: urllib3 (==2.5.0)
|
|
67
|
+
Requires-Dist: virtualenv (==20.35.4)
|
|
68
|
+
Requires-Dist: zstandard (==0.25.0)
|
|
69
|
+
Description-Content-Type: text/markdown
|
|
70
|
+
|
|
71
|
+
# polyhattrick
|
|
72
|
+
|
|
73
|
+
`polyhattrick` is a simple command-line utility for fetching live match scores from [Hattrick](https://www.hattrick.org). It can be easily integrated into `polybar`, allowing you to see how your team is performing live directly on your desktop.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
Install `polyhattrick` using `pip`:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install polyhattrick
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Authentication
|
|
86
|
+
|
|
87
|
+
Before you can display match results in your terminal or in polybar, you need to authorize polyhattrick to access your match data on Hattrick.
|
|
88
|
+
|
|
89
|
+
First, start the authentication process:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
polyhattrick login authenticate
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This command will generate a login link. Open it in your browser to log in to Hattrick and authorize polyhattrick. Once authorized, Hattrick will display a token. Copy this token and complete the authorization process:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
polyhattrick login exchange [TOKEN]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Usage
|
|
102
|
+
|
|
103
|
+
After authentication, you can start fetching live match results.
|
|
104
|
+
|
|
105
|
+
To display the score of the first match in your live ticker on Hattrick, run:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
polyhattrick match live watch
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If you want to see the result of a different match, reorder your live ticker on the Hattrick website so that the desired match appears first.
|
|
112
|
+
|
|
113
|
+
## Integration with polybar
|
|
114
|
+
|
|
115
|
+
To display live match results in polybar, add the following module to your polybar configuration:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
[module/hattrick]
|
|
119
|
+
type = custom/script
|
|
120
|
+
exec = polyhattrick match live watch
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Then place the module wherever you want it to appear in your bar.
|
|
124
|
+
|
|
125
|
+
## Notes
|
|
126
|
+
|
|
127
|
+
- This tool is intended for Linux systems.
|
|
128
|
+
|
|
129
|
+
- Make sure polyhattrick is available in your PATH when used with polybar.
|
|
130
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# polyhattrick
|
|
2
|
+
|
|
3
|
+
`polyhattrick` is a simple command-line utility for fetching live match scores from [Hattrick](https://www.hattrick.org). It can be easily integrated into `polybar`, allowing you to see how your team is performing live directly on your desktop.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Install `polyhattrick` using `pip`:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install polyhattrick
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Authentication
|
|
16
|
+
|
|
17
|
+
Before you can display match results in your terminal or in polybar, you need to authorize polyhattrick to access your match data on Hattrick.
|
|
18
|
+
|
|
19
|
+
First, start the authentication process:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
polyhattrick login authenticate
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This command will generate a login link. Open it in your browser to log in to Hattrick and authorize polyhattrick. Once authorized, Hattrick will display a token. Copy this token and complete the authorization process:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
polyhattrick login exchange [TOKEN]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
After authentication, you can start fetching live match results.
|
|
34
|
+
|
|
35
|
+
To display the score of the first match in your live ticker on Hattrick, run:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
polyhattrick match live watch
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If you want to see the result of a different match, reorder your live ticker on the Hattrick website so that the desired match appears first.
|
|
42
|
+
|
|
43
|
+
## Integration with polybar
|
|
44
|
+
|
|
45
|
+
To display live match results in polybar, add the following module to your polybar configuration:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
[module/hattrick]
|
|
49
|
+
type = custom/script
|
|
50
|
+
exec = polyhattrick match live watch
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Then place the module wherever you want it to appear in your bar.
|
|
54
|
+
|
|
55
|
+
## Notes
|
|
56
|
+
|
|
57
|
+
- This tool is intended for Linux systems.
|
|
58
|
+
|
|
59
|
+
- Make sure polyhattrick is available in your PATH when used with polybar.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "polyhattrick"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Live Hattrick scores in your terminal and polybar"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Peter Toth",email = "work@petertoth.de"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
license = "MIT"
|
|
10
|
+
license-files = ["LICENSE"]
|
|
11
|
+
requires-python = ">=3.12,<4.0"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"anyio (==4.12.0)",
|
|
14
|
+
"black (==25.12.0)",
|
|
15
|
+
"build (==1.3.0)",
|
|
16
|
+
"cachecontrol (==0.14.4)",
|
|
17
|
+
"certifi (==2025.11.12)",
|
|
18
|
+
"cffi (==2.0.0)",
|
|
19
|
+
"charset-normalizer (==3.4.4)",
|
|
20
|
+
"cleo (==2.1.0)",
|
|
21
|
+
"click (==8.3.1)",
|
|
22
|
+
"crashtest (==0.4.1)",
|
|
23
|
+
"cryptography (==46.0.3)",
|
|
24
|
+
"distlib (==0.4.0)",
|
|
25
|
+
"dulwich (==0.24.10)",
|
|
26
|
+
"fastjsonschema (==2.21.2)",
|
|
27
|
+
"filelock (==3.20.0)",
|
|
28
|
+
"findpython (==0.7.1)",
|
|
29
|
+
"h11 (==0.16.0)",
|
|
30
|
+
"httpcore (==1.0.9)",
|
|
31
|
+
"httpx (==0.28.1)",
|
|
32
|
+
"idna (==3.11)",
|
|
33
|
+
"installer (==0.7.0)",
|
|
34
|
+
"jaraco-classes (==3.4.0)",
|
|
35
|
+
"jaraco-context (==6.0.1)",
|
|
36
|
+
"jaraco-functools (==4.3.0)",
|
|
37
|
+
"jeepney (==0.9.0)",
|
|
38
|
+
"keyring (==25.7.0)",
|
|
39
|
+
"markdown-it-py (==4.0.0)",
|
|
40
|
+
"mdurl (==0.1.2)",
|
|
41
|
+
"more-itertools (==10.8.0)",
|
|
42
|
+
"msgpack (==1.1.2)",
|
|
43
|
+
"mypy-extensions (==1.1.0)",
|
|
44
|
+
"packaging (==25.0)",
|
|
45
|
+
"pathspec (==0.12.1)",
|
|
46
|
+
"pbs-installer (==2025.12.5)",
|
|
47
|
+
"pkginfo (==1.12.1.2)",
|
|
48
|
+
"platformdirs (==4.5.0)",
|
|
49
|
+
"poetry (==2.2.1)",
|
|
50
|
+
"poetry-core (==2.2.1)",
|
|
51
|
+
"pycparser (==2.23)",
|
|
52
|
+
"pygments (==2.19.2)",
|
|
53
|
+
"pyproject-hooks (==1.2.0)",
|
|
54
|
+
"pytokens (==0.3.0)",
|
|
55
|
+
"rapidfuzz (==3.14.3)",
|
|
56
|
+
"requests (==2.32.5)",
|
|
57
|
+
"requests-toolbelt (==1.0.0)",
|
|
58
|
+
"rich (==14.2.0)",
|
|
59
|
+
"secretstorage (==3.5.0)",
|
|
60
|
+
"shellingham (==1.5.4)",
|
|
61
|
+
"tomlkit (==0.13.3)",
|
|
62
|
+
"trove-classifiers (==2025.12.1.14)",
|
|
63
|
+
"typer (==0.20.0)",
|
|
64
|
+
"typing-extensions (==4.15.0)",
|
|
65
|
+
"urllib3 (==2.5.0)",
|
|
66
|
+
"virtualenv (==20.35.4)",
|
|
67
|
+
"zstandard (==0.25.0)"
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
[dependency-groups]
|
|
71
|
+
dev = [
|
|
72
|
+
"black (>=25.12.0,<26.0.0)"
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
[build-system]
|
|
77
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
78
|
+
build-backend = "poetry.core.masonry.api"
|
|
79
|
+
|
|
80
|
+
[project.scripts]
|
|
81
|
+
polyhattrick = "polyhattrick.polyhattrick:main"
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from urllib.parse import parse_qs
|
|
2
|
+
import requests
|
|
3
|
+
import configparser
|
|
4
|
+
import logging
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
auth_secrets_path = Path.home() / ".config/polyhattrick/auth"
|
|
8
|
+
auth_secrets_path.parent.mkdir(parents=True, exist_ok=True)
|
|
9
|
+
|
|
10
|
+
environments = {"local": "http://localhost:8000", "prod": "http://161.35.142.18"}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def host(env):
|
|
14
|
+
return environments[env]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PolyhattrickClient:
|
|
18
|
+
def __init__(self, env):
|
|
19
|
+
self.env = env
|
|
20
|
+
|
|
21
|
+
def authenticate(self):
|
|
22
|
+
headers = {
|
|
23
|
+
"User-Agent": "polyhattrick_client/0.1",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
response = requests.post(
|
|
27
|
+
f"{host(self.env)}/login/authenticate", headers=headers
|
|
28
|
+
)
|
|
29
|
+
json = response.json()
|
|
30
|
+
|
|
31
|
+
config = configparser.ConfigParser()
|
|
32
|
+
config["auth"] = {
|
|
33
|
+
"resource_owner_key": json["resource_owner_key"],
|
|
34
|
+
"resource_owner_secret": json["resource_owner_secret"],
|
|
35
|
+
}
|
|
36
|
+
with auth_secrets_path.open("w") as configfile:
|
|
37
|
+
config.write(configfile)
|
|
38
|
+
|
|
39
|
+
return json["auth_url"]
|
|
40
|
+
|
|
41
|
+
def exchange_tokens(self, pin):
|
|
42
|
+
config = configparser.ConfigParser()
|
|
43
|
+
config.read(auth_secrets_path)
|
|
44
|
+
|
|
45
|
+
if "auth" not in config:
|
|
46
|
+
logging.warning("Auth section not found in config")
|
|
47
|
+
print(
|
|
48
|
+
"Looks like you haven't initiated the authentication flow yet. Please run `TODO`"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
headers = {
|
|
52
|
+
"User-Agent": "polyhattrick_client/0.1",
|
|
53
|
+
"Content-Type": "application/json",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
data = {
|
|
57
|
+
"verification_code": pin,
|
|
58
|
+
"resource_owner_key": config["auth"]["resource_owner_key"],
|
|
59
|
+
"resource_owner_secret": config["auth"]["resource_owner_secret"],
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
response = requests.post(
|
|
63
|
+
f"{host(self.env)}/login/exchange_tokens", headers=headers, json=data
|
|
64
|
+
).json()
|
|
65
|
+
config.set("auth", "secret", response["secret"])
|
|
66
|
+
|
|
67
|
+
with auth_secrets_path.open("w") as configfile:
|
|
68
|
+
config.write(configfile)
|
|
69
|
+
|
|
70
|
+
def get_live_result(self):
|
|
71
|
+
config = configparser.ConfigParser()
|
|
72
|
+
config.read(auth_secrets_path)
|
|
73
|
+
|
|
74
|
+
if "auth" not in config or "secret" not in config["auth"]:
|
|
75
|
+
logging.warning("Auth secret not found.")
|
|
76
|
+
print("Not authenticated. Please authenticate first, then try again.")
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
headers = {
|
|
80
|
+
"Authorization": "Bearer " + config["auth"]["secret"],
|
|
81
|
+
"User-Agent": "polyhattrick_client/0.1",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
response = requests.get(f"{host(self.env)}/watch/live", headers=headers)
|
|
85
|
+
|
|
86
|
+
return response.text
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import typer
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from polyhattrick.client import polyhattrick_client
|
|
7
|
+
|
|
8
|
+
app = typer.Typer()
|
|
9
|
+
login_app = typer.Typer()
|
|
10
|
+
app.add_typer(login_app, name="login")
|
|
11
|
+
|
|
12
|
+
match_app = typer.Typer()
|
|
13
|
+
app.add_typer(match_app, name="match")
|
|
14
|
+
live_app = typer.Typer()
|
|
15
|
+
match_app.add_typer(live_app, name="live")
|
|
16
|
+
|
|
17
|
+
LOG_DIR = Path("logs")
|
|
18
|
+
LOG_DIR.mkdir(exist_ok=True)
|
|
19
|
+
|
|
20
|
+
logging.basicConfig(
|
|
21
|
+
filename=LOG_DIR / "client.log",
|
|
22
|
+
level=logging.INFO,
|
|
23
|
+
format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
client = polyhattrick_client.PolyhattrickClient("prod")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@login_app.command("authenticate")
|
|
30
|
+
def authenticate():
|
|
31
|
+
response = client.authenticate()
|
|
32
|
+
print(
|
|
33
|
+
f"Please follow this URL to grant polyhattrick access to read your data. Then, run `login exchange [TOKEN]`:\n{response}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@login_app.command("exchange")
|
|
38
|
+
def exchange(pin: Annotated[str, typer.Argument()]):
|
|
39
|
+
client.exchange_tokens(pin)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@live_app.command("watch")
|
|
43
|
+
def watch():
|
|
44
|
+
try:
|
|
45
|
+
res = client.get_live_result()
|
|
46
|
+
if res:
|
|
47
|
+
print(res)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logging.error("Unable to get live result.", exc_info=True)
|
|
50
|
+
return ""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main():
|
|
54
|
+
app()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
main()
|