cs2tracker 2.1.15__tar.gz → 2.1.17__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.
Potentially problematic release.
This version of cs2tracker might be problematic. Click here for more details.
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/.gitignore +1 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/PKG-INFO +35 -15
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/README.md +34 -14
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/_version.py +2 -2
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/app/app.py +48 -15
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/app/editor_frame.py +40 -16
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/constants.py +104 -27
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/scraper/background_task.py +79 -5
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/scraper/parser.py +7 -1
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker.egg-info/PKG-INFO +35 -15
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/.flake8 +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/.isort.cfg +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/.pre-commit-config.yaml +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/.pylintrc +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/LICENSE +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/MANIFEST.in +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/assets/demo.gif +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/assets/icon.png +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/__init__.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/__main__.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/app/__init__.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/app/history_frame.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/app/scraper_frame.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/config.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/data/config.ini +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/data/convert_inventory.js +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/data/get_inventory.js +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/data/output.csv +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/logs.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/main.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/scraper/__init__.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/scraper/discord_notifier.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/scraper/scraper.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/util/__init__.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/util/currency_conversion.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/util/padded_console.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker/util/tkinter.py +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker.egg-info/SOURCES.txt +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker.egg-info/dependency_links.txt +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker.egg-info/entry_points.txt +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker.egg-info/requires.txt +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/cs2tracker.egg-info/top_level.txt +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/pyproject.toml +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/requirements.txt +0 -0
- {cs2tracker-2.1.15 → cs2tracker-2.1.17}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cs2tracker
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.17
|
|
4
4
|
Summary: Tracking the steam market prices of CS2 items
|
|
5
5
|
Home-page: https://github.com/ashiven/cs2tracker
|
|
6
6
|
Author: Jannik Novak
|
|
@@ -49,18 +49,19 @@ Dynamic: license-file
|
|
|
49
49
|
|
|
50
50
|
- [Features](#features)
|
|
51
51
|
- [Getting Started](#getting-started)
|
|
52
|
-
- [Prerequisites](#prerequisites)
|
|
53
52
|
- [Installation](#installation)
|
|
53
|
+
- [Additional Setup](#additional-setup)
|
|
54
54
|
- [Usage](#usage)
|
|
55
55
|
- [Configuration](#configuration)
|
|
56
56
|
- [Advanced Features](#advanced-features)
|
|
57
|
+
- [FAQ](#faq)
|
|
57
58
|
- [Contributing](#contributing)
|
|
58
59
|
- [License](#license)
|
|
59
60
|
|
|
60
61
|
## Features
|
|
61
62
|
|
|
62
63
|
- ⚡ Rapidly import your Storage Units
|
|
63
|
-
- 🔍 Track prices on Steam, Buff163,
|
|
64
|
+
- 🔍 Track prices on Steam, Buff163, CSFloat
|
|
64
65
|
- 📈 View investment price history
|
|
65
66
|
- 🧾 Export/Import history data
|
|
66
67
|
- 📤 Discord notifications on updates
|
|
@@ -69,19 +70,16 @@ Dynamic: license-file
|
|
|
69
70
|
|
|
70
71
|
## Getting Started
|
|
71
72
|
|
|
72
|
-
### Prerequisites
|
|
73
|
-
|
|
74
|
-
- Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required on Linux)
|
|
75
|
-
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
76
|
-
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
77
|
-
|
|
78
73
|
### Installation
|
|
79
74
|
|
|
80
|
-
####
|
|
75
|
+
#### Method 1: Executable
|
|
76
|
+
|
|
77
|
+
Simply download the program and run it:
|
|
81
78
|
|
|
82
|
-
-
|
|
79
|
+
- [Windows](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-windows.zip)
|
|
80
|
+
- [Linux](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-linux.zip)
|
|
83
81
|
|
|
84
|
-
####
|
|
82
|
+
#### Method 2: Install via Pip
|
|
85
83
|
|
|
86
84
|
1. Install the program:
|
|
87
85
|
|
|
@@ -95,9 +93,14 @@ Dynamic: license-file
|
|
|
95
93
|
cs2tracker
|
|
96
94
|
```
|
|
97
95
|
|
|
96
|
+
### Additional Setup
|
|
97
|
+
|
|
98
|
+
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
99
|
+
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
100
|
+
|
|
98
101
|
## Usage
|
|
99
102
|
|
|
100
|
-
- Click **Run!** to gather the current market prices of your items and calculate the total amount in USD and
|
|
103
|
+
- Click **Run!** to gather the current market prices of your items and calculate the total amount in USD and your selected currency.
|
|
101
104
|
- The generated Excel sheet can be saved by right-clicking and then selecting **Save Sheet**.
|
|
102
105
|
- Use **Edit Config** to specify the numbers of items owned in the configuration.
|
|
103
106
|
- Click **Show History** to see a price chart consisting of past calculations.
|
|
@@ -121,14 +124,31 @@ This will open the config editor where you can change any setting by double clic
|
|
|
121
124
|
- Enable **Proxy Requests** to prevent your requests from being rate limited by the steamcommunity server.
|
|
122
125
|
- You need to register for a free API key on [Crawlbase](crawlbase.com) and enter it into the `proxy_api_key` field in the config `User Settings`.
|
|
123
126
|
|
|
127
|
+
## FAQ
|
|
128
|
+
|
|
129
|
+
**Q: Is it safe to login with my Steam account?**
|
|
130
|
+
|
|
131
|
+
**A:** Yes, the program uses the [SteamUser](https://github.com/DoctorMcKay/node-steam-user?tab=readme-ov-file#methods-) and [Globaloffensive](https://github.com/DoctorMcKay/node-globaloffensive) libraries to sign in and import your Storage Units (the same method is used by [casemove](https://github.com/nombersDev/casemove)) and all of the login-related code is transparently available in [this file](cs2tracker/data/get_inventory.js).
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
**Q: Do I have to login with my Steam account?**
|
|
135
|
+
|
|
136
|
+
**A:** No, you can also manually specify the number of items you own in the config editor.
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
**Q: Can I get VAC-banned for using this program?**
|
|
140
|
+
|
|
141
|
+
**A:** No, this program does not interact with the game in any way and only reads your Storage Units.
|
|
142
|
+
|
|
124
143
|
## Contributing
|
|
125
144
|
|
|
126
145
|
Please feel free to submit a pull request or open an issue. See [issues](https://github.com/ashiven/cs2tracker/issues) and [pull requests](https://github.com/ashiven/cs2tracker/pulls) for current work.
|
|
127
146
|
|
|
128
147
|
1. Fork the repository
|
|
129
|
-
2. Create a new branch
|
|
148
|
+
2. Create a new branch: `git checkout -b feature-name`.
|
|
130
149
|
3. Make your changes
|
|
131
|
-
4.
|
|
150
|
+
4. Push your branch: `git push origin feature-name`.
|
|
151
|
+
5. Submit a PR
|
|
132
152
|
|
|
133
153
|
## License
|
|
134
154
|
|
|
@@ -21,18 +21,19 @@
|
|
|
21
21
|
|
|
22
22
|
- [Features](#features)
|
|
23
23
|
- [Getting Started](#getting-started)
|
|
24
|
-
- [Prerequisites](#prerequisites)
|
|
25
24
|
- [Installation](#installation)
|
|
25
|
+
- [Additional Setup](#additional-setup)
|
|
26
26
|
- [Usage](#usage)
|
|
27
27
|
- [Configuration](#configuration)
|
|
28
28
|
- [Advanced Features](#advanced-features)
|
|
29
|
+
- [FAQ](#faq)
|
|
29
30
|
- [Contributing](#contributing)
|
|
30
31
|
- [License](#license)
|
|
31
32
|
|
|
32
33
|
## Features
|
|
33
34
|
|
|
34
35
|
- ⚡ Rapidly import your Storage Units
|
|
35
|
-
- 🔍 Track prices on Steam, Buff163,
|
|
36
|
+
- 🔍 Track prices on Steam, Buff163, CSFloat
|
|
36
37
|
- 📈 View investment price history
|
|
37
38
|
- 🧾 Export/Import history data
|
|
38
39
|
- 📤 Discord notifications on updates
|
|
@@ -41,19 +42,16 @@
|
|
|
41
42
|
|
|
42
43
|
## Getting Started
|
|
43
44
|
|
|
44
|
-
### Prerequisites
|
|
45
|
-
|
|
46
|
-
- Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required on Linux)
|
|
47
|
-
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
48
|
-
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
49
|
-
|
|
50
45
|
### Installation
|
|
51
46
|
|
|
52
|
-
####
|
|
47
|
+
#### Method 1: Executable
|
|
48
|
+
|
|
49
|
+
Simply download the program and run it:
|
|
53
50
|
|
|
54
|
-
-
|
|
51
|
+
- [Windows](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-windows.zip)
|
|
52
|
+
- [Linux](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-linux.zip)
|
|
55
53
|
|
|
56
|
-
####
|
|
54
|
+
#### Method 2: Install via Pip
|
|
57
55
|
|
|
58
56
|
1. Install the program:
|
|
59
57
|
|
|
@@ -67,9 +65,14 @@
|
|
|
67
65
|
cs2tracker
|
|
68
66
|
```
|
|
69
67
|
|
|
68
|
+
### Additional Setup
|
|
69
|
+
|
|
70
|
+
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
71
|
+
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
72
|
+
|
|
70
73
|
## Usage
|
|
71
74
|
|
|
72
|
-
- Click **Run!** to gather the current market prices of your items and calculate the total amount in USD and
|
|
75
|
+
- Click **Run!** to gather the current market prices of your items and calculate the total amount in USD and your selected currency.
|
|
73
76
|
- The generated Excel sheet can be saved by right-clicking and then selecting **Save Sheet**.
|
|
74
77
|
- Use **Edit Config** to specify the numbers of items owned in the configuration.
|
|
75
78
|
- Click **Show History** to see a price chart consisting of past calculations.
|
|
@@ -93,14 +96,31 @@ This will open the config editor where you can change any setting by double clic
|
|
|
93
96
|
- Enable **Proxy Requests** to prevent your requests from being rate limited by the steamcommunity server.
|
|
94
97
|
- You need to register for a free API key on [Crawlbase](crawlbase.com) and enter it into the `proxy_api_key` field in the config `User Settings`.
|
|
95
98
|
|
|
99
|
+
## FAQ
|
|
100
|
+
|
|
101
|
+
**Q: Is it safe to login with my Steam account?**
|
|
102
|
+
|
|
103
|
+
**A:** Yes, the program uses the [SteamUser](https://github.com/DoctorMcKay/node-steam-user?tab=readme-ov-file#methods-) and [Globaloffensive](https://github.com/DoctorMcKay/node-globaloffensive) libraries to sign in and import your Storage Units (the same method is used by [casemove](https://github.com/nombersDev/casemove)) and all of the login-related code is transparently available in [this file](cs2tracker/data/get_inventory.js).
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
**Q: Do I have to login with my Steam account?**
|
|
107
|
+
|
|
108
|
+
**A:** No, you can also manually specify the number of items you own in the config editor.
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
**Q: Can I get VAC-banned for using this program?**
|
|
112
|
+
|
|
113
|
+
**A:** No, this program does not interact with the game in any way and only reads your Storage Units.
|
|
114
|
+
|
|
96
115
|
## Contributing
|
|
97
116
|
|
|
98
117
|
Please feel free to submit a pull request or open an issue. See [issues](https://github.com/ashiven/cs2tracker/issues) and [pull requests](https://github.com/ashiven/cs2tracker/pulls) for current work.
|
|
99
118
|
|
|
100
119
|
1. Fork the repository
|
|
101
|
-
2. Create a new branch
|
|
120
|
+
2. Create a new branch: `git checkout -b feature-name`.
|
|
102
121
|
3. Make your changes
|
|
103
|
-
4.
|
|
122
|
+
4. Push your branch: `git push origin feature-name`.
|
|
123
|
+
5. Submit a PR
|
|
104
124
|
|
|
105
125
|
## License
|
|
106
126
|
|
|
@@ -15,6 +15,7 @@ from cs2tracker.logs import PriceLogs
|
|
|
15
15
|
from cs2tracker.scraper.background_task import BackgroundTask
|
|
16
16
|
from cs2tracker.scraper.scraper import Scraper
|
|
17
17
|
from cs2tracker.util.currency_conversion import CURRENCY_SYMBOLS
|
|
18
|
+
from cs2tracker.util.padded_console import get_console
|
|
18
19
|
from cs2tracker.util.tkinter import centered, fix_sv_ttk, size_info
|
|
19
20
|
|
|
20
21
|
APPLICATION_NAME = "CS2Tracker"
|
|
@@ -31,6 +32,7 @@ PRICE_HISTORY_TITLE = "Price History"
|
|
|
31
32
|
PRICE_HISTORY_SIZE = "900x700"
|
|
32
33
|
|
|
33
34
|
config = get_config()
|
|
35
|
+
console = get_console()
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
class Application:
|
|
@@ -48,6 +50,7 @@ class Application:
|
|
|
48
50
|
|
|
49
51
|
fix_sv_ttk(ttk.Style())
|
|
50
52
|
|
|
53
|
+
window.focus_force()
|
|
51
54
|
window.mainloop()
|
|
52
55
|
|
|
53
56
|
def _configure_window(self):
|
|
@@ -76,6 +79,10 @@ class MainFrame(ttk.Frame):
|
|
|
76
79
|
super().__init__(parent, padding=15)
|
|
77
80
|
self.parent = parent
|
|
78
81
|
self.scraper = scraper
|
|
82
|
+
|
|
83
|
+
self.scraper_window = None
|
|
84
|
+
self.config_editor_window = None
|
|
85
|
+
self.price_history_window = None
|
|
79
86
|
self._add_widgets()
|
|
80
87
|
|
|
81
88
|
def _add_widgets(self):
|
|
@@ -181,13 +188,21 @@ class MainFrame(ttk.Frame):
|
|
|
181
188
|
"""Scrape prices from the configured sources, print the total, and save the
|
|
182
189
|
results to a file.
|
|
183
190
|
"""
|
|
184
|
-
scraper_window
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
if self.scraper_window is None or not self.scraper_window.winfo_exists():
|
|
192
|
+
self._open_scraper_window()
|
|
193
|
+
else:
|
|
194
|
+
self.scraper_window.lift()
|
|
195
|
+
self.scraper_window.focus_set()
|
|
196
|
+
|
|
197
|
+
def _open_scraper_window(self):
|
|
198
|
+
"""Open a new window with the scraper GUI."""
|
|
199
|
+
self.scraper_window = tk.Toplevel(self.parent)
|
|
200
|
+
self.scraper_window.geometry(centered(self.scraper_window, SCRAPER_WINDOW_SIZE))
|
|
201
|
+
self.scraper_window.minsize(*size_info(SCRAPER_WINDOW_SIZE))
|
|
202
|
+
self.scraper_window.title(SCRAPER_WINDOW_TITLE)
|
|
188
203
|
|
|
189
204
|
run_frame = ScraperFrame(
|
|
190
|
-
scraper_window,
|
|
205
|
+
self.scraper_window,
|
|
191
206
|
self.scraper,
|
|
192
207
|
sheet_size=SCRAPER_WINDOW_SIZE,
|
|
193
208
|
dark_theme=self.dark_theme_checkbox_value.get(),
|
|
@@ -196,26 +211,42 @@ class MainFrame(ttk.Frame):
|
|
|
196
211
|
run_frame.start()
|
|
197
212
|
|
|
198
213
|
def _edit_config(self):
|
|
214
|
+
"""Open a new window with a config editor GUI or lift the existing one."""
|
|
215
|
+
if self.config_editor_window is None or not self.config_editor_window.winfo_exists():
|
|
216
|
+
self._open_config_editor()
|
|
217
|
+
else:
|
|
218
|
+
self.config_editor_window.lift()
|
|
219
|
+
self.config_editor_window.focus_set()
|
|
220
|
+
|
|
221
|
+
def _open_config_editor(self):
|
|
199
222
|
"""Open a new window with a config editor GUI."""
|
|
200
|
-
config_editor_window = tk.Toplevel(self.parent)
|
|
201
|
-
config_editor_window.geometry(centered(config_editor_window, CONFIG_EDITOR_SIZE))
|
|
202
|
-
config_editor_window.minsize(*size_info(CONFIG_EDITOR_SIZE))
|
|
203
|
-
config_editor_window.title(CONFIG_EDITOR_TITLE)
|
|
223
|
+
self.config_editor_window = tk.Toplevel(self.parent)
|
|
224
|
+
self.config_editor_window.geometry(centered(self.config_editor_window, CONFIG_EDITOR_SIZE))
|
|
225
|
+
self.config_editor_window.minsize(*size_info(CONFIG_EDITOR_SIZE))
|
|
226
|
+
self.config_editor_window.title(CONFIG_EDITOR_TITLE)
|
|
204
227
|
|
|
205
|
-
editor_frame = ConfigEditorFrame(config_editor_window)
|
|
228
|
+
editor_frame = ConfigEditorFrame(self.config_editor_window)
|
|
206
229
|
editor_frame.pack(expand=True, fill="both")
|
|
207
230
|
|
|
208
231
|
def _show_history(self):
|
|
209
232
|
"""Show a chart consisting of past calculations."""
|
|
233
|
+
if self.price_history_window is None or not self.price_history_window.winfo_exists():
|
|
234
|
+
self._open_history_window()
|
|
235
|
+
else:
|
|
236
|
+
self.price_history_window.lift()
|
|
237
|
+
self.price_history_window.focus_set()
|
|
238
|
+
|
|
239
|
+
def _open_history_window(self):
|
|
240
|
+
"""Open a new window with a price history GUI."""
|
|
210
241
|
if PriceLogs.empty():
|
|
211
242
|
return
|
|
212
243
|
|
|
213
|
-
price_history_window = tk.Toplevel(self.parent)
|
|
214
|
-
price_history_window.geometry(centered(price_history_window, PRICE_HISTORY_SIZE))
|
|
215
|
-
price_history_window.minsize(*size_info(PRICE_HISTORY_SIZE))
|
|
216
|
-
price_history_window.title(PRICE_HISTORY_TITLE)
|
|
244
|
+
self.price_history_window = tk.Toplevel(self.parent)
|
|
245
|
+
self.price_history_window.geometry(centered(self.price_history_window, PRICE_HISTORY_SIZE))
|
|
246
|
+
self.price_history_window.minsize(*size_info(PRICE_HISTORY_SIZE))
|
|
247
|
+
self.price_history_window.title(PRICE_HISTORY_TITLE)
|
|
217
248
|
|
|
218
|
-
history_frame = PriceHistoryFrame(price_history_window)
|
|
249
|
+
history_frame = PriceHistoryFrame(self.price_history_window)
|
|
219
250
|
history_frame.pack(expand=True, fill="both")
|
|
220
251
|
|
|
221
252
|
def _export_log_file(self):
|
|
@@ -239,8 +270,10 @@ class MainFrame(ttk.Frame):
|
|
|
239
270
|
filetypes=[("CSV files", "*.csv")],
|
|
240
271
|
)
|
|
241
272
|
if not PriceLogs.validate_file(import_path):
|
|
273
|
+
console.error("Invalid log file format.")
|
|
242
274
|
return
|
|
243
275
|
copy(import_path, OUTPUT_FILE)
|
|
276
|
+
console.info("Log file imported successfully.")
|
|
244
277
|
|
|
245
278
|
def _toggle_background_task(self, enabled: bool):
|
|
246
279
|
"""Toggle whether a daily price calculation should run in the background."""
|
|
@@ -253,6 +253,8 @@ class ConfigEditorButtonFrame(ttk.Frame):
|
|
|
253
253
|
self.editor_frame = editor_frame
|
|
254
254
|
self.custom_item_dialog = None
|
|
255
255
|
|
|
256
|
+
self.custom_item_window = None
|
|
257
|
+
self.steam_inventory_window = None
|
|
256
258
|
self._add_widgets()
|
|
257
259
|
|
|
258
260
|
def _add_widgets(self):
|
|
@@ -285,37 +287,59 @@ class ConfigEditorButtonFrame(ttk.Frame):
|
|
|
285
287
|
self.editor_frame.tree.focus_set()
|
|
286
288
|
|
|
287
289
|
def _add_custom_item(self):
|
|
290
|
+
"""Open a window to add a new custom item or lift the existing one if it is
|
|
291
|
+
already open.
|
|
292
|
+
"""
|
|
293
|
+
if self.custom_item_window is None or not self.custom_item_window.winfo_exists():
|
|
294
|
+
self._open_custom_item_window()
|
|
295
|
+
else:
|
|
296
|
+
self.custom_item_window.lift()
|
|
297
|
+
self.custom_item_window.focus_set()
|
|
298
|
+
|
|
299
|
+
def _open_custom_item_window(self):
|
|
288
300
|
"""Open a window to add a new custom item."""
|
|
289
|
-
custom_item_window = tk.Toplevel(self.editor_frame)
|
|
290
|
-
custom_item_window.title(ADD_CUSTOM_ITEM_TITLE)
|
|
291
|
-
custom_item_window.geometry(centered(custom_item_window, ADD_CUSTOM_ITEM_SIZE))
|
|
292
|
-
custom_item_window.minsize(*size_info(ADD_CUSTOM_ITEM_SIZE))
|
|
293
|
-
custom_item_window.focus_set()
|
|
301
|
+
self.custom_item_window = tk.Toplevel(self.editor_frame)
|
|
302
|
+
self.custom_item_window.title(ADD_CUSTOM_ITEM_TITLE)
|
|
303
|
+
self.custom_item_window.geometry(centered(self.custom_item_window, ADD_CUSTOM_ITEM_SIZE))
|
|
304
|
+
self.custom_item_window.minsize(*size_info(ADD_CUSTOM_ITEM_SIZE))
|
|
305
|
+
self.custom_item_window.focus_set()
|
|
294
306
|
|
|
295
307
|
def on_close():
|
|
296
|
-
custom_item_window.destroy()
|
|
308
|
+
self.custom_item_window.destroy() # type: ignore
|
|
297
309
|
self.editor_frame.tree.focus_set()
|
|
298
310
|
|
|
299
|
-
custom_item_window.protocol("WM_DELETE_WINDOW", on_close)
|
|
311
|
+
self.custom_item_window.protocol("WM_DELETE_WINDOW", on_close)
|
|
300
312
|
|
|
301
|
-
custom_item_frame = CustomItemFrame(custom_item_window, self.editor_frame)
|
|
313
|
+
custom_item_frame = CustomItemFrame(self.custom_item_window, self.editor_frame)
|
|
302
314
|
custom_item_frame.pack(expand=True, fill="both", padx=15, pady=15)
|
|
303
315
|
|
|
304
316
|
def _import_steam_inventory(self):
|
|
317
|
+
"""Open a window to import the user's Steam inventory or lift the existing one
|
|
318
|
+
if it is already open.
|
|
319
|
+
"""
|
|
320
|
+
if self.steam_inventory_window is None or not self.steam_inventory_window.winfo_exists():
|
|
321
|
+
self._open_steam_inventory_window()
|
|
322
|
+
else:
|
|
323
|
+
self.steam_inventory_window.lift()
|
|
324
|
+
self.steam_inventory_window.focus_set()
|
|
325
|
+
|
|
326
|
+
def _open_steam_inventory_window(self):
|
|
305
327
|
"""Open a window to import the user's Steam inventory."""
|
|
306
|
-
steam_inventory_window = tk.Toplevel(self.editor_frame)
|
|
307
|
-
steam_inventory_window.title(IMPORT_INVENTORY_TITLE)
|
|
308
|
-
steam_inventory_window.geometry(
|
|
309
|
-
|
|
310
|
-
|
|
328
|
+
self.steam_inventory_window = tk.Toplevel(self.editor_frame)
|
|
329
|
+
self.steam_inventory_window.title(IMPORT_INVENTORY_TITLE)
|
|
330
|
+
self.steam_inventory_window.geometry(
|
|
331
|
+
centered(self.steam_inventory_window, IMPORT_INVENTORY_SIZE)
|
|
332
|
+
)
|
|
333
|
+
self.steam_inventory_window.minsize(*size_info(IMPORT_INVENTORY_SIZE))
|
|
334
|
+
self.steam_inventory_window.focus_set()
|
|
311
335
|
|
|
312
336
|
def on_close():
|
|
313
|
-
steam_inventory_window.destroy()
|
|
337
|
+
self.steam_inventory_window.destroy() # type: ignore
|
|
314
338
|
self.editor_frame.tree.focus_set()
|
|
315
339
|
|
|
316
|
-
steam_inventory_window.protocol("WM_DELETE_WINDOW", on_close)
|
|
340
|
+
self.steam_inventory_window.protocol("WM_DELETE_WINDOW", on_close)
|
|
317
341
|
|
|
318
|
-
steam_inventory_frame = InventoryImportFrame(steam_inventory_window, self.editor_frame)
|
|
342
|
+
steam_inventory_frame = InventoryImportFrame(self.steam_inventory_window, self.editor_frame)
|
|
319
343
|
steam_inventory_frame.pack(expand=True, fill="both", padx=15, pady=15)
|
|
320
344
|
|
|
321
345
|
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import ctypes
|
|
1
2
|
import enum
|
|
2
3
|
import os
|
|
3
4
|
import sys
|
|
5
|
+
import tkinter as tk
|
|
4
6
|
from datetime import datetime
|
|
5
|
-
from shutil import copy
|
|
7
|
+
from shutil import copy, copytree
|
|
6
8
|
from subprocess import DEVNULL
|
|
9
|
+
from tkinter import ttk
|
|
7
10
|
|
|
11
|
+
import sv_ttk
|
|
8
12
|
from nodejs import npm
|
|
9
13
|
|
|
14
|
+
from cs2tracker.util.tkinter import centered
|
|
15
|
+
|
|
10
16
|
try:
|
|
11
17
|
from cs2tracker._version import version # pylint: disable=E0611
|
|
12
18
|
|
|
@@ -18,46 +24,116 @@ except ImportError:
|
|
|
18
24
|
class OSType(enum.Enum):
|
|
19
25
|
WINDOWS = "Windows"
|
|
20
26
|
LINUX = "Linux"
|
|
27
|
+
MACOS = "MacOS"
|
|
21
28
|
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
if sys.platform.startswith("win"):
|
|
31
|
+
OS = OSType.WINDOWS
|
|
32
|
+
elif sys.platform.startswith("linux"):
|
|
33
|
+
OS = OSType.LINUX
|
|
34
|
+
elif sys.platform.startswith("darwin"):
|
|
35
|
+
OS = OSType.MACOS
|
|
36
|
+
else:
|
|
37
|
+
raise NotImplementedError(f"Unsupported OS: {sys.platform}")
|
|
25
38
|
|
|
26
39
|
|
|
40
|
+
PYTHON_EXECUTABLE = sys.executable
|
|
27
41
|
RUNNING_IN_EXE = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
|
|
28
42
|
|
|
29
43
|
if RUNNING_IN_EXE:
|
|
30
44
|
MEIPASS_DIR = sys._MEIPASS # type: ignore pylint: disable=protected-access
|
|
31
45
|
MODULE_DIR = MEIPASS_DIR
|
|
32
46
|
PROJECT_DIR = MEIPASS_DIR
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
|
|
48
|
+
if OS == OSType.WINDOWS:
|
|
49
|
+
APP_DATA_DIR = os.path.join(os.path.expanduser("~"), "AppData", "Local")
|
|
50
|
+
elif OS == OSType.LINUX:
|
|
51
|
+
APP_DATA_DIR = os.environ.get(
|
|
52
|
+
"XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local", "share")
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
raise NotImplementedError(f"Unsupported OS: {OS}")
|
|
36
56
|
|
|
37
57
|
CONFIG_FILE_SOURCE = os.path.join(MODULE_DIR, "data", "config.ini")
|
|
38
58
|
OUTPUT_FILE_SOURCE = os.path.join(MODULE_DIR, "data", "output.csv")
|
|
39
|
-
|
|
59
|
+
INVENTORY_CONVERT_SCRIPT_SOURCE = os.path.join(MODULE_DIR, "data", "convert_inventory.js")
|
|
40
60
|
INVENTORY_IMPORT_SCRIPT_SOURCE = os.path.join(MODULE_DIR, "data", "get_inventory.js")
|
|
61
|
+
NODE_MODULES_SOURCE = os.path.join(MODULE_DIR, "data", "node_modules")
|
|
41
62
|
|
|
63
|
+
DATA_DIR = os.path.join(APP_DATA_DIR, "cs2tracker", "data")
|
|
42
64
|
CONFIG_FILE = os.path.join(DATA_DIR, "config.ini")
|
|
43
65
|
CONFIG_FILE_BACKUP = os.path.join(DATA_DIR, "config.ini.bak")
|
|
44
66
|
OUTPUT_FILE = os.path.join(DATA_DIR, "output.csv")
|
|
45
|
-
|
|
67
|
+
INVENTORY_CONVERT_SCRIPT = os.path.join(DATA_DIR, "convert_inventory.js")
|
|
46
68
|
INVENTORY_IMPORT_SCRIPT = os.path.join(DATA_DIR, "get_inventory.js")
|
|
69
|
+
NODE_MODULES = os.path.join(DATA_DIR, "node_modules")
|
|
70
|
+
|
|
71
|
+
ICON_FILE = os.path.join(PROJECT_DIR, "assets", "icon.png")
|
|
72
|
+
BATCH_FILE = os.path.join(DATA_DIR, "cs2tracker_scraper.bat")
|
|
73
|
+
INVENTORY_IMPORT_FILE = os.path.join(DATA_DIR, "inventory.json")
|
|
74
|
+
INVENTORY_IMPORT_SCRIPT_DEPENDENCIES = [
|
|
75
|
+
"steam-user",
|
|
76
|
+
"globaloffensive",
|
|
77
|
+
"@node-steam/vdf",
|
|
78
|
+
"axios",
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
def show_temp_popup():
|
|
82
|
+
"""Show a temporary popup window while copying initial files."""
|
|
83
|
+
popup = tk.Tk()
|
|
84
|
+
popup.title("Please wait")
|
|
85
|
+
popup.geometry(centered(popup, "300x80"))
|
|
86
|
+
popup.resizable(False, False)
|
|
87
|
+
if OS == OSType.WINDOWS:
|
|
88
|
+
app_id = "cs2tracker.unique.id"
|
|
89
|
+
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(app_id)
|
|
90
|
+
icon = tk.PhotoImage(file=ICON_FILE)
|
|
91
|
+
popup.wm_iconphoto(True, icon)
|
|
92
|
+
|
|
93
|
+
label = ttk.Label(popup, text="Setting up the application. Please wait...")
|
|
94
|
+
label.pack(pady=20)
|
|
95
|
+
|
|
96
|
+
sv_ttk.use_dark_theme()
|
|
97
|
+
|
|
98
|
+
popup.update()
|
|
99
|
+
return popup
|
|
100
|
+
|
|
101
|
+
def copy_initial_files_with_popup():
|
|
102
|
+
"""Copy initial files to the user data directory with a temporary popup."""
|
|
103
|
+
popup = show_temp_popup()
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
if not os.path.exists(DATA_DIR):
|
|
107
|
+
os.makedirs(DATA_DIR)
|
|
108
|
+
if not os.path.exists(OUTPUT_FILE):
|
|
109
|
+
copy(OUTPUT_FILE_SOURCE, OUTPUT_FILE)
|
|
110
|
+
if not os.path.exists(CONFIG_FILE):
|
|
111
|
+
copy(CONFIG_FILE_SOURCE, CONFIG_FILE)
|
|
112
|
+
if not os.path.exists(INVENTORY_CONVERT_SCRIPT):
|
|
113
|
+
copy(INVENTORY_CONVERT_SCRIPT_SOURCE, INVENTORY_CONVERT_SCRIPT)
|
|
114
|
+
if not os.path.exists(INVENTORY_IMPORT_SCRIPT):
|
|
115
|
+
copy(INVENTORY_IMPORT_SCRIPT_SOURCE, INVENTORY_IMPORT_SCRIPT)
|
|
116
|
+
if not os.path.exists(NODE_MODULES):
|
|
117
|
+
copytree(NODE_MODULES_SOURCE, NODE_MODULES)
|
|
118
|
+
finally:
|
|
119
|
+
popup.destroy()
|
|
120
|
+
|
|
121
|
+
# pylint: disable=too-many-boolean-expressions
|
|
122
|
+
if (
|
|
123
|
+
not os.path.exists(DATA_DIR)
|
|
124
|
+
or not os.path.exists(OUTPUT_FILE)
|
|
125
|
+
or not os.path.exists(CONFIG_FILE)
|
|
126
|
+
or not os.path.exists(INVENTORY_CONVERT_SCRIPT)
|
|
127
|
+
or not os.path.exists(INVENTORY_IMPORT_SCRIPT)
|
|
128
|
+
or not os.path.exists(NODE_MODULES)
|
|
129
|
+
):
|
|
130
|
+
copy_initial_files_with_popup()
|
|
47
131
|
|
|
48
132
|
# Always copy the source config into the user data directory as a backup
|
|
49
133
|
# and overwrite the existing backup if it exists
|
|
50
134
|
# (This is to ensure that no outdated config backup remains in the user data directory)
|
|
51
135
|
copy(CONFIG_FILE_SOURCE, CONFIG_FILE_BACKUP)
|
|
52
136
|
|
|
53
|
-
if not os.path.exists(OUTPUT_FILE):
|
|
54
|
-
copy(OUTPUT_FILE_SOURCE, OUTPUT_FILE)
|
|
55
|
-
if not os.path.exists(CONFIG_FILE):
|
|
56
|
-
copy(CONFIG_FILE_SOURCE, CONFIG_FILE)
|
|
57
|
-
if not os.path.exists(IVENTORY_CONVERT_SCRIPT):
|
|
58
|
-
copy(IVENTORY_CONVERT_SCRIPT_SOURCE, IVENTORY_CONVERT_SCRIPT)
|
|
59
|
-
if not os.path.exists(INVENTORY_IMPORT_SCRIPT):
|
|
60
|
-
copy(INVENTORY_IMPORT_SCRIPT_SOURCE, INVENTORY_IMPORT_SCRIPT)
|
|
61
137
|
|
|
62
138
|
else:
|
|
63
139
|
MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -69,24 +145,25 @@ else:
|
|
|
69
145
|
OUTPUT_FILE = os.path.join(DATA_DIR, "output.csv")
|
|
70
146
|
INVENTORY_CONVERT_SCRIPT = os.path.join(DATA_DIR, "convert_inventory.js")
|
|
71
147
|
INVENTORY_IMPORT_SCRIPT = os.path.join(DATA_DIR, "get_inventory.js")
|
|
148
|
+
NODE_MODULES = os.path.join(DATA_DIR, "node_modules")
|
|
149
|
+
|
|
150
|
+
ICON_FILE = os.path.join(PROJECT_DIR, "assets", "icon.png")
|
|
151
|
+
BATCH_FILE = os.path.join(DATA_DIR, "cs2tracker_scraper.bat")
|
|
152
|
+
INVENTORY_IMPORT_FILE = os.path.join(DATA_DIR, "inventory.json")
|
|
153
|
+
INVENTORY_IMPORT_SCRIPT_DEPENDENCIES = [
|
|
154
|
+
"steam-user",
|
|
155
|
+
"globaloffensive",
|
|
156
|
+
"@node-steam/vdf",
|
|
157
|
+
"axios",
|
|
158
|
+
]
|
|
72
159
|
|
|
73
160
|
if not os.path.exists(CONFIG_FILE_BACKUP):
|
|
74
161
|
copy(CONFIG_FILE, CONFIG_FILE_BACKUP)
|
|
75
162
|
|
|
76
163
|
|
|
77
|
-
ICON_FILE = os.path.join(PROJECT_DIR, "assets", "icon.png")
|
|
78
|
-
BATCH_FILE = os.path.join(DATA_DIR, "cs2tracker_scraper.bat")
|
|
79
|
-
INVENTORY_IMPORT_FILE = os.path.join(DATA_DIR, "inventory.json")
|
|
80
|
-
INVENTORY_IMPORT_SCRIPT_DEPENDENCIES = [
|
|
81
|
-
"steam-user",
|
|
82
|
-
"globaloffensive",
|
|
83
|
-
"@node-steam/vdf",
|
|
84
|
-
"axios",
|
|
85
|
-
]
|
|
86
|
-
|
|
87
164
|
# Ensures that the necessary node modules are installed if a user wants
|
|
88
165
|
# to import their steam inventory via the cs2tracker/data/get_inventory.js Node.js script.
|
|
89
|
-
if not os.path.exists(
|
|
166
|
+
if not os.path.exists(NODE_MODULES):
|
|
90
167
|
npm.Popen(
|
|
91
168
|
["install", "-g", "--prefix", DATA_DIR] + INVENTORY_IMPORT_SCRIPT_DEPENDENCIES,
|
|
92
169
|
stdout=DEVNULL,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from subprocess import DEVNULL, call
|
|
2
|
+
from subprocess import DEVNULL, STDOUT, CalledProcessError, call, check_output, run
|
|
3
3
|
|
|
4
4
|
from cs2tracker.constants import (
|
|
5
5
|
BATCH_FILE,
|
|
@@ -18,6 +18,18 @@ WIN_BACKGROUND_TASK_CMD = (
|
|
|
18
18
|
f"powershell -WindowStyle Hidden -Command \"Start-Process '{BATCH_FILE}' -WindowStyle Hidden\""
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
+
LINUX_BACKGROUND_TASK_SCHEDULE = "0 12 * * *"
|
|
22
|
+
LINUX_BACKGROUND_TASK_CMD = (
|
|
23
|
+
f"bash -c 'cd {PROJECT_DIR} && {PYTHON_EXECUTABLE} -m cs2tracker --only-scrape'"
|
|
24
|
+
)
|
|
25
|
+
LINUX_BACKGROUND_TASK_CMD_EXE = f"bash -c 'cd {PROJECT_DIR} && {PYTHON_EXECUTABLE} --only-scrape'"
|
|
26
|
+
|
|
27
|
+
if RUNNING_IN_EXE:
|
|
28
|
+
LINUX_CRON_JOB = f"{LINUX_BACKGROUND_TASK_SCHEDULE} {LINUX_BACKGROUND_TASK_CMD_EXE}"
|
|
29
|
+
else:
|
|
30
|
+
LINUX_CRON_JOB = f"{LINUX_BACKGROUND_TASK_SCHEDULE} {LINUX_BACKGROUND_TASK_CMD}"
|
|
31
|
+
|
|
32
|
+
|
|
21
33
|
console = get_console()
|
|
22
34
|
|
|
23
35
|
|
|
@@ -34,8 +46,17 @@ class BackgroundTask:
|
|
|
34
46
|
return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
|
|
35
47
|
found = return_code == 0
|
|
36
48
|
return found
|
|
49
|
+
elif OS == OSType.LINUX:
|
|
50
|
+
try:
|
|
51
|
+
existing_jobs = (
|
|
52
|
+
check_output(["crontab", "-l"], stderr=STDOUT).decode("utf-8").strip()
|
|
53
|
+
)
|
|
54
|
+
except CalledProcessError:
|
|
55
|
+
existing_jobs = ""
|
|
56
|
+
|
|
57
|
+
found = LINUX_CRON_JOB in existing_jobs.splitlines()
|
|
58
|
+
return found
|
|
37
59
|
else:
|
|
38
|
-
# TODO: implement finder for cron jobs
|
|
39
60
|
return False
|
|
40
61
|
|
|
41
62
|
@classmethod
|
|
@@ -83,17 +104,69 @@ class BackgroundTask:
|
|
|
83
104
|
]
|
|
84
105
|
return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
|
|
85
106
|
if return_code == 0:
|
|
86
|
-
console.
|
|
107
|
+
console.info("Background task enabled.")
|
|
87
108
|
else:
|
|
88
109
|
console.error("Failed to enable background task.")
|
|
89
110
|
else:
|
|
90
111
|
cmd = ["schtasks", "/delete", "/tn", WIN_BACKGROUND_TASK_NAME, "/f"]
|
|
91
112
|
return_code = call(cmd, stdout=DEVNULL, stderr=DEVNULL)
|
|
92
113
|
if return_code == 0:
|
|
93
|
-
console.
|
|
114
|
+
console.info("Background task disabled.")
|
|
94
115
|
else:
|
|
95
116
|
console.error("Failed to disable background task.")
|
|
96
117
|
|
|
118
|
+
@classmethod
|
|
119
|
+
def _toggle_linux(cls, enabled: bool):
|
|
120
|
+
"""
|
|
121
|
+
Create or delete a daily background task that runs the scraper on Linux.
|
|
122
|
+
|
|
123
|
+
:param enabled: If True, the task will be created; if False, the task will be
|
|
124
|
+
deleted.
|
|
125
|
+
"""
|
|
126
|
+
try:
|
|
127
|
+
existing_jobs = check_output(["crontab", "-l"], stderr=STDOUT).decode("utf-8").strip()
|
|
128
|
+
except CalledProcessError:
|
|
129
|
+
existing_jobs = ""
|
|
130
|
+
|
|
131
|
+
cron_lines = existing_jobs.splitlines()
|
|
132
|
+
|
|
133
|
+
if enabled and LINUX_CRON_JOB not in cron_lines:
|
|
134
|
+
updated_jobs = (
|
|
135
|
+
existing_jobs + "\n" + LINUX_CRON_JOB + "\n"
|
|
136
|
+
if existing_jobs
|
|
137
|
+
else LINUX_CRON_JOB + "\n"
|
|
138
|
+
)
|
|
139
|
+
try:
|
|
140
|
+
run(
|
|
141
|
+
["crontab", "-"],
|
|
142
|
+
input=updated_jobs.encode("utf-8"),
|
|
143
|
+
stdout=DEVNULL,
|
|
144
|
+
stderr=DEVNULL,
|
|
145
|
+
check=True,
|
|
146
|
+
)
|
|
147
|
+
console.info("Background task enabled.")
|
|
148
|
+
except CalledProcessError:
|
|
149
|
+
console.error("Failed to enable background task.")
|
|
150
|
+
|
|
151
|
+
elif not enabled and LINUX_CRON_JOB in cron_lines:
|
|
152
|
+
updated_jobs = "\n".join(
|
|
153
|
+
line for line in cron_lines if line.strip() != LINUX_CRON_JOB
|
|
154
|
+
).strip()
|
|
155
|
+
try:
|
|
156
|
+
if updated_jobs:
|
|
157
|
+
run(
|
|
158
|
+
["crontab", "-"],
|
|
159
|
+
input=(updated_jobs + "\n").encode("utf-8"),
|
|
160
|
+
stdout=DEVNULL,
|
|
161
|
+
stderr=DEVNULL,
|
|
162
|
+
check=True,
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
run(["crontab", "-r"], stdout=DEVNULL, stderr=DEVNULL, check=True)
|
|
166
|
+
console.info("Background task disabled.")
|
|
167
|
+
except CalledProcessError:
|
|
168
|
+
console.error("Failed to disable background task.")
|
|
169
|
+
|
|
97
170
|
@classmethod
|
|
98
171
|
def toggle(cls, enabled: bool):
|
|
99
172
|
"""
|
|
@@ -104,6 +177,7 @@ class BackgroundTask:
|
|
|
104
177
|
"""
|
|
105
178
|
if OS == OSType.WINDOWS:
|
|
106
179
|
cls._toggle_windows(enabled)
|
|
180
|
+
elif OS == OSType.LINUX:
|
|
181
|
+
cls._toggle_linux(enabled)
|
|
107
182
|
else:
|
|
108
|
-
# TODO: implement toggle for cron jobs
|
|
109
183
|
pass
|
|
@@ -127,7 +127,7 @@ class CSGOTraderParser(BaseParser):
|
|
|
127
127
|
CSGOTRADER_PRICE_LIST = "https://prices.csgotrader.app/latest/{}.json"
|
|
128
128
|
PRICE_INFO = "Owned: {:<10} {:<10}: ${:<10} Total: ${:<10}"
|
|
129
129
|
NEEDS_TIMEOUT = False
|
|
130
|
-
SOURCES = [PriceSource.STEAM, PriceSource.BUFF163, PriceSource.
|
|
130
|
+
SOURCES = [PriceSource.STEAM, PriceSource.BUFF163, PriceSource.CSFLOAT]
|
|
131
131
|
|
|
132
132
|
@classmethod
|
|
133
133
|
def get_item_page_url(cls, item_href, source=PriceSource.STEAM):
|
|
@@ -176,6 +176,12 @@ class CSGOTraderParser(BaseParser):
|
|
|
176
176
|
raise ValueError(
|
|
177
177
|
f"CSGOTrader: Could not find recent youpin898 price: {url_decoded_name}"
|
|
178
178
|
)
|
|
179
|
+
elif source == PriceSource.CSFLOAT:
|
|
180
|
+
price = price_info.get("price")
|
|
181
|
+
if not price:
|
|
182
|
+
raise ValueError(
|
|
183
|
+
f"CSGOTrader: Could not find recent csfloat price: {url_decoded_name}"
|
|
184
|
+
)
|
|
179
185
|
else:
|
|
180
186
|
price = price_info.get("starting_at")
|
|
181
187
|
if not price:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cs2tracker
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.17
|
|
4
4
|
Summary: Tracking the steam market prices of CS2 items
|
|
5
5
|
Home-page: https://github.com/ashiven/cs2tracker
|
|
6
6
|
Author: Jannik Novak
|
|
@@ -49,18 +49,19 @@ Dynamic: license-file
|
|
|
49
49
|
|
|
50
50
|
- [Features](#features)
|
|
51
51
|
- [Getting Started](#getting-started)
|
|
52
|
-
- [Prerequisites](#prerequisites)
|
|
53
52
|
- [Installation](#installation)
|
|
53
|
+
- [Additional Setup](#additional-setup)
|
|
54
54
|
- [Usage](#usage)
|
|
55
55
|
- [Configuration](#configuration)
|
|
56
56
|
- [Advanced Features](#advanced-features)
|
|
57
|
+
- [FAQ](#faq)
|
|
57
58
|
- [Contributing](#contributing)
|
|
58
59
|
- [License](#license)
|
|
59
60
|
|
|
60
61
|
## Features
|
|
61
62
|
|
|
62
63
|
- ⚡ Rapidly import your Storage Units
|
|
63
|
-
- 🔍 Track prices on Steam, Buff163,
|
|
64
|
+
- 🔍 Track prices on Steam, Buff163, CSFloat
|
|
64
65
|
- 📈 View investment price history
|
|
65
66
|
- 🧾 Export/Import history data
|
|
66
67
|
- 📤 Discord notifications on updates
|
|
@@ -69,19 +70,16 @@ Dynamic: license-file
|
|
|
69
70
|
|
|
70
71
|
## Getting Started
|
|
71
72
|
|
|
72
|
-
### Prerequisites
|
|
73
|
-
|
|
74
|
-
- Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required on Linux)
|
|
75
|
-
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
76
|
-
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
77
|
-
|
|
78
73
|
### Installation
|
|
79
74
|
|
|
80
|
-
####
|
|
75
|
+
#### Method 1: Executable
|
|
76
|
+
|
|
77
|
+
Simply download the program and run it:
|
|
81
78
|
|
|
82
|
-
-
|
|
79
|
+
- [Windows](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-windows.zip)
|
|
80
|
+
- [Linux](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-linux.zip)
|
|
83
81
|
|
|
84
|
-
####
|
|
82
|
+
#### Method 2: Install via Pip
|
|
85
83
|
|
|
86
84
|
1. Install the program:
|
|
87
85
|
|
|
@@ -95,9 +93,14 @@ Dynamic: license-file
|
|
|
95
93
|
cs2tracker
|
|
96
94
|
```
|
|
97
95
|
|
|
96
|
+
### Additional Setup
|
|
97
|
+
|
|
98
|
+
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
99
|
+
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
100
|
+
|
|
98
101
|
## Usage
|
|
99
102
|
|
|
100
|
-
- Click **Run!** to gather the current market prices of your items and calculate the total amount in USD and
|
|
103
|
+
- Click **Run!** to gather the current market prices of your items and calculate the total amount in USD and your selected currency.
|
|
101
104
|
- The generated Excel sheet can be saved by right-clicking and then selecting **Save Sheet**.
|
|
102
105
|
- Use **Edit Config** to specify the numbers of items owned in the configuration.
|
|
103
106
|
- Click **Show History** to see a price chart consisting of past calculations.
|
|
@@ -121,14 +124,31 @@ This will open the config editor where you can change any setting by double clic
|
|
|
121
124
|
- Enable **Proxy Requests** to prevent your requests from being rate limited by the steamcommunity server.
|
|
122
125
|
- You need to register for a free API key on [Crawlbase](crawlbase.com) and enter it into the `proxy_api_key` field in the config `User Settings`.
|
|
123
126
|
|
|
127
|
+
## FAQ
|
|
128
|
+
|
|
129
|
+
**Q: Is it safe to login with my Steam account?**
|
|
130
|
+
|
|
131
|
+
**A:** Yes, the program uses the [SteamUser](https://github.com/DoctorMcKay/node-steam-user?tab=readme-ov-file#methods-) and [Globaloffensive](https://github.com/DoctorMcKay/node-globaloffensive) libraries to sign in and import your Storage Units (the same method is used by [casemove](https://github.com/nombersDev/casemove)) and all of the login-related code is transparently available in [this file](cs2tracker/data/get_inventory.js).
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
**Q: Do I have to login with my Steam account?**
|
|
135
|
+
|
|
136
|
+
**A:** No, you can also manually specify the number of items you own in the config editor.
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
**Q: Can I get VAC-banned for using this program?**
|
|
140
|
+
|
|
141
|
+
**A:** No, this program does not interact with the game in any way and only reads your Storage Units.
|
|
142
|
+
|
|
124
143
|
## Contributing
|
|
125
144
|
|
|
126
145
|
Please feel free to submit a pull request or open an issue. See [issues](https://github.com/ashiven/cs2tracker/issues) and [pull requests](https://github.com/ashiven/cs2tracker/pulls) for current work.
|
|
127
146
|
|
|
128
147
|
1. Fork the repository
|
|
129
|
-
2. Create a new branch
|
|
148
|
+
2. Create a new branch: `git checkout -b feature-name`.
|
|
130
149
|
3. Make your changes
|
|
131
|
-
4.
|
|
150
|
+
4. Push your branch: `git push origin feature-name`.
|
|
151
|
+
5. Submit a PR
|
|
132
152
|
|
|
133
153
|
## License
|
|
134
154
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|