cs2tracker 2.1.7__tar.gz → 2.1.8__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.7 → cs2tracker-2.1.8}/PKG-INFO +4 -2
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/README.md +3 -1
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/_version.py +2 -2
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/application.py +3 -3
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/data/config.ini +3 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/main.py +1 -1
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/scraper.py +82 -43
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/PKG-INFO +4 -2
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.flake8 +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.gitignore +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.isort.cfg +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.pre-commit-config.yaml +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.pylintrc +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/LICENSE.md +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/MANIFEST.in +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/assets/icon.png +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/__init__.py +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/__main__.py +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/constants.py +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/data/output.csv +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/padded_console.py +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/SOURCES.txt +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/dependency_links.txt +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/entry_points.txt +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/requires.txt +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/top_level.txt +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/pyproject.toml +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/requirements.txt +0 -0
- {cs2tracker-2.1.7 → cs2tracker-2.1.8}/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.8
|
|
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
|
|
@@ -42,6 +42,7 @@ Dynamic: license-file
|
|
|
42
42
|
|
|
43
43
|
- Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required on Linux)
|
|
44
44
|
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
45
|
+
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
45
46
|
|
|
46
47
|
### Setup
|
|
47
48
|
|
|
@@ -58,6 +59,7 @@ Dynamic: license-file
|
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
2. Run it:
|
|
62
|
+
|
|
61
63
|
```bash
|
|
62
64
|
cs2tracker
|
|
63
65
|
```
|
|
@@ -65,7 +67,7 @@ Dynamic: license-file
|
|
|
65
67
|
### Options
|
|
66
68
|
|
|
67
69
|
- `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
|
|
68
|
-
- `Edit Config` to
|
|
70
|
+
- `Edit Config` to specify the numbers of items owned in the config file. You can also add items other than cases and sticker capsules following the format in the `Custom Items` section. (item_name = item_owned item_page)
|
|
69
71
|
- `Show History` to see a price chart consisting of past calculations. A new data point is generated once a day upon running the program.
|
|
70
72
|
- `Daily Background Calculations` to automatically run a daily calculation of your investment in the background and save the results such that they can later be viewed via `Show History`.
|
|
71
73
|
- `Receive Discord Notifications` to receive a notification on your Discord server when the program has finished calculating your investment. You need to set up a [webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) in your Discord server and enter the webhook url into the `discord_webhook_url` field in the config file.
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
- Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required on Linux)
|
|
21
21
|
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
22
|
+
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
22
23
|
|
|
23
24
|
### Setup
|
|
24
25
|
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
2. Run it:
|
|
39
|
+
|
|
38
40
|
```bash
|
|
39
41
|
cs2tracker
|
|
40
42
|
```
|
|
@@ -42,7 +44,7 @@
|
|
|
42
44
|
### Options
|
|
43
45
|
|
|
44
46
|
- `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
|
|
45
|
-
- `Edit Config` to
|
|
47
|
+
- `Edit Config` to specify the numbers of items owned in the config file. You can also add items other than cases and sticker capsules following the format in the `Custom Items` section. (item_name = item_owned item_page)
|
|
46
48
|
- `Show History` to see a price chart consisting of past calculations. A new data point is generated once a day upon running the program.
|
|
47
49
|
- `Daily Background Calculations` to automatically run a daily calculation of your investment in the background and save the results such that they can later be viewed via `Show History`.
|
|
48
50
|
- `Receive Discord Notifications` to receive a notification on your Discord server when the program has finished calculating your investment. You need to set up a [webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) in your Discord server and enter the webhook url into the `discord_webhook_url` field in the config file.
|
|
@@ -118,7 +118,7 @@ class Application:
|
|
|
118
118
|
lambda: self._toggle_background_task(background_checkbox_value.get()),
|
|
119
119
|
)
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
discord_webhook_checkbox_value = tk.BooleanVar(
|
|
122
122
|
value=self.scraper.config.getboolean(
|
|
123
123
|
"App Settings", "discord_notifications", fallback=False
|
|
124
124
|
)
|
|
@@ -126,8 +126,8 @@ class Application:
|
|
|
126
126
|
self._add_checkbox(
|
|
127
127
|
checkbox_frame,
|
|
128
128
|
"Receive Discord Notifications",
|
|
129
|
-
|
|
130
|
-
lambda: self._toggle_discord_webhook(
|
|
129
|
+
discord_webhook_checkbox_value,
|
|
130
|
+
lambda: self._toggle_discord_webhook(discord_webhook_checkbox_value.get()),
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
use_proxy_checkbox_value = tk.BooleanVar(
|
|
@@ -6,6 +6,9 @@ discord_webhook_url = None
|
|
|
6
6
|
use_proxy = False
|
|
7
7
|
discord_notifications = False
|
|
8
8
|
|
|
9
|
+
[Custom Items]
|
|
10
|
+
copenhagen_flames_gold_2022 = 0 https://steamcommunity.com/market/listings/730/Sticker%20%7C%20Copenhagen%20Flames%20%28Gold%29%20%7C%20Antwerp%202022
|
|
11
|
+
|
|
9
12
|
[Cases]
|
|
10
13
|
revolution_case = 0
|
|
11
14
|
recoil_case = 0
|
|
@@ -20,7 +20,7 @@ def main():
|
|
|
20
20
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
21
21
|
|
|
22
22
|
# Set output encoding to UTF-8 with BOM for Windows compatibility
|
|
23
|
-
if OS == OSType.WINDOWS:
|
|
23
|
+
if OS == OSType.WINDOWS and sys.stdout is not None:
|
|
24
24
|
sys.stdout.reconfigure(encoding="utf-8-sig") # type: ignore
|
|
25
25
|
|
|
26
26
|
console = PaddedConsole()
|
|
@@ -4,6 +4,7 @@ import time
|
|
|
4
4
|
from configparser import ConfigParser
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from subprocess import DEVNULL, call
|
|
7
|
+
from urllib.parse import unquote
|
|
7
8
|
|
|
8
9
|
from bs4 import BeautifulSoup
|
|
9
10
|
from bs4.element import Tag
|
|
@@ -60,7 +61,7 @@ class Scraper:
|
|
|
60
61
|
|
|
61
62
|
def parse_config(self):
|
|
62
63
|
"""Parse the configuration file to read settings and user-owned items."""
|
|
63
|
-
self.config = ConfigParser()
|
|
64
|
+
self.config = ConfigParser(interpolation=None)
|
|
64
65
|
self.config.read(CONFIG_FILE)
|
|
65
66
|
|
|
66
67
|
def _start_session(self):
|
|
@@ -95,8 +96,17 @@ class Scraper:
|
|
|
95
96
|
"[bold red][!] Failed to scrape case prices. (Consider using proxies to prevent rate limiting)\n"
|
|
96
97
|
)
|
|
97
98
|
|
|
99
|
+
custom_item_usd_total = 0
|
|
100
|
+
try:
|
|
101
|
+
custom_item_usd_total = self._scrape_custom_item_prices()
|
|
102
|
+
except (RequestException, AttributeError, RetryError, ValueError):
|
|
103
|
+
self.console.print(
|
|
104
|
+
"[bold red][!] Failed to scrape custom item prices. (Consider using proxies to prevent rate limiting)\n"
|
|
105
|
+
)
|
|
106
|
+
|
|
98
107
|
self.usd_total += capsule_usd_total
|
|
99
108
|
self.usd_total += case_usd_total
|
|
109
|
+
self.usd_total += custom_item_usd_total
|
|
100
110
|
self.eur_total = CurrencyConverter().convert(self.usd_total, "USD", "EUR")
|
|
101
111
|
|
|
102
112
|
self._print_total()
|
|
@@ -292,26 +302,26 @@ class Scraper:
|
|
|
292
302
|
|
|
293
303
|
return page
|
|
294
304
|
|
|
295
|
-
def
|
|
305
|
+
def _parse_item_price(self, item_page, item_href):
|
|
296
306
|
"""
|
|
297
|
-
Parse the price of
|
|
307
|
+
Parse the price of an item from the given steamcommunity market page and item
|
|
308
|
+
href.
|
|
298
309
|
|
|
299
|
-
:param
|
|
300
|
-
|
|
301
|
-
:
|
|
302
|
-
:
|
|
303
|
-
:raises ValueError: If the capsule listing or price span cannot be found.
|
|
310
|
+
:param item_page: The HTTP response object containing the item page content.
|
|
311
|
+
:param item_href: The href of the item listing to find the price for.
|
|
312
|
+
:return: The price of the item as a float.
|
|
313
|
+
:raises ValueError: If the item listing or price span cannot be found.
|
|
304
314
|
"""
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if not isinstance(
|
|
308
|
-
raise ValueError(f"Failed to find
|
|
315
|
+
item_soup = BeautifulSoup(item_page.content, "html.parser")
|
|
316
|
+
item_listing = item_soup.find("a", attrs={"href": f"{item_href}"})
|
|
317
|
+
if not isinstance(item_listing, Tag):
|
|
318
|
+
raise ValueError(f"Failed to find item listing: {item_href}")
|
|
309
319
|
|
|
310
|
-
|
|
311
|
-
if not isinstance(
|
|
312
|
-
raise ValueError(f"Failed to find price span in
|
|
320
|
+
item_price_span = item_listing.find("span", attrs={"class": "normal_price"})
|
|
321
|
+
if not isinstance(item_price_span, Tag):
|
|
322
|
+
raise ValueError(f"Failed to find price span in item listing: {item_href}")
|
|
313
323
|
|
|
314
|
-
price_str =
|
|
324
|
+
price_str = item_price_span.text.split()[2]
|
|
315
325
|
price = float(price_str.replace("$", ""))
|
|
316
326
|
|
|
317
327
|
return price
|
|
@@ -330,7 +340,7 @@ class Scraper:
|
|
|
330
340
|
hrefs, and names.
|
|
331
341
|
"""
|
|
332
342
|
capsule_title = capsule_section.center(MAX_LINE_LEN, SEPARATOR)
|
|
333
|
-
self.console.print(f"[bold magenta]{capsule_title}")
|
|
343
|
+
self.console.print(f"[bold magenta]{capsule_title}\n")
|
|
334
344
|
|
|
335
345
|
capsule_usd_total = 0
|
|
336
346
|
capsule_page = self._get_page(capsule_info["page"])
|
|
@@ -340,7 +350,7 @@ class Scraper:
|
|
|
340
350
|
if owned == 0:
|
|
341
351
|
continue
|
|
342
352
|
|
|
343
|
-
price_usd = self.
|
|
353
|
+
price_usd = self._parse_item_price(capsule_page, capsule_href)
|
|
344
354
|
price_usd_owned = round(float(owned * price_usd), 2)
|
|
345
355
|
|
|
346
356
|
self.console.print(f"[bold deep_sky_blue4]{capsule_name}")
|
|
@@ -359,29 +369,6 @@ class Scraper:
|
|
|
359
369
|
|
|
360
370
|
return capsule_usd_total
|
|
361
371
|
|
|
362
|
-
def _parse_case_price(self, case_page, case_href):
|
|
363
|
-
"""
|
|
364
|
-
Parse the price of a case from the given page and href.
|
|
365
|
-
|
|
366
|
-
:param case_page: The HTTP response object containing the case page content.
|
|
367
|
-
:param case_href: The href of the case listing to find the price for.
|
|
368
|
-
:return: The price of the case as a float.
|
|
369
|
-
:raises ValueError: If the case listing or price span cannot be found.
|
|
370
|
-
"""
|
|
371
|
-
case_soup = BeautifulSoup(case_page.content, "html.parser")
|
|
372
|
-
case_listing = case_soup.find("a", attrs={"href": case_href})
|
|
373
|
-
if not isinstance(case_listing, Tag):
|
|
374
|
-
raise ValueError(f"Failed to find case listing: {case_href}")
|
|
375
|
-
|
|
376
|
-
price_class = case_listing.find("span", attrs={"class": "normal_price"})
|
|
377
|
-
if not isinstance(price_class, Tag):
|
|
378
|
-
raise ValueError(f"Failed to find price class in case listing: {case_href}")
|
|
379
|
-
|
|
380
|
-
price_str = price_class.text.split()[2]
|
|
381
|
-
price = float(price_str.replace("$", ""))
|
|
382
|
-
|
|
383
|
-
return price
|
|
384
|
-
|
|
385
372
|
def _scrape_case_prices(self):
|
|
386
373
|
"""
|
|
387
374
|
Scrape prices for all cases defined in the configuration.
|
|
@@ -396,10 +383,10 @@ class Scraper:
|
|
|
396
383
|
|
|
397
384
|
case_name = config_case_name.replace("_", " ").title()
|
|
398
385
|
case_title = case_name.center(MAX_LINE_LEN, SEPARATOR)
|
|
399
|
-
self.console.print(f"[bold magenta]{case_title}")
|
|
386
|
+
self.console.print(f"[bold magenta]{case_title}\n")
|
|
400
387
|
|
|
401
388
|
case_page = self._get_page(CASE_PAGES[case_index])
|
|
402
|
-
price_usd = self.
|
|
389
|
+
price_usd = self._parse_item_price(case_page, CASE_HREFS[case_index])
|
|
403
390
|
price_usd_owned = round(float(int(owned) * price_usd), 2)
|
|
404
391
|
|
|
405
392
|
self.console.print(PRICE_INFO.format(owned, price_usd, price_usd_owned))
|
|
@@ -410,6 +397,58 @@ class Scraper:
|
|
|
410
397
|
|
|
411
398
|
return case_usd_total
|
|
412
399
|
|
|
400
|
+
def _market_page_from_href(self, item_href):
|
|
401
|
+
"""
|
|
402
|
+
Convert an href of a Steam Community Market item to a market page URL. This is
|
|
403
|
+
done by decoding the URL-encoded item name and formatting it into a search URL.
|
|
404
|
+
|
|
405
|
+
:param item_href: The href of the item listing, typically ending with the item's
|
|
406
|
+
name.
|
|
407
|
+
:return: A URL string for the Steam Community Market page of the item.
|
|
408
|
+
"""
|
|
409
|
+
url_encoded_name = item_href.split("/")[-1]
|
|
410
|
+
decoded_name = unquote(url_encoded_name)
|
|
411
|
+
decoded_name_query = decoded_name.lower().replace(" ", "+")
|
|
412
|
+
page_url = f"https://steamcommunity.com/market/search?q={decoded_name_query}"
|
|
413
|
+
|
|
414
|
+
return page_url
|
|
415
|
+
|
|
416
|
+
def _scrape_custom_item_prices(self):
|
|
417
|
+
"""
|
|
418
|
+
Scrape prices for custom items defined in the configuration.
|
|
419
|
+
|
|
420
|
+
For each custom item, it prints the item name, owned count, price per item, and
|
|
421
|
+
total price for owned items.
|
|
422
|
+
"""
|
|
423
|
+
custom_item_usd_total = 0
|
|
424
|
+
for config_custom_item_name, owned_and_href in self.config.items("Custom Items"):
|
|
425
|
+
if " " not in owned_and_href:
|
|
426
|
+
self.console.print(
|
|
427
|
+
"[bold red][!] Invalid custom item format (<item_name> = <owned_count> <item_url>)\n"
|
|
428
|
+
)
|
|
429
|
+
continue
|
|
430
|
+
|
|
431
|
+
owned, custom_item_href = owned_and_href.split(" ", 1)
|
|
432
|
+
if int(owned) == 0:
|
|
433
|
+
continue
|
|
434
|
+
|
|
435
|
+
custom_item_name = config_custom_item_name.replace("_", " ").title()
|
|
436
|
+
custom_item_title = custom_item_name.center(MAX_LINE_LEN, SEPARATOR)
|
|
437
|
+
self.console.print(f"[bold magenta]{custom_item_title}\n")
|
|
438
|
+
|
|
439
|
+
custom_item_page_url = self._market_page_from_href(custom_item_href)
|
|
440
|
+
custom_item_page = self._get_page(custom_item_page_url)
|
|
441
|
+
price_usd = self._parse_item_price(custom_item_page, custom_item_href)
|
|
442
|
+
price_usd_owned = round(float(int(owned) * price_usd), 2)
|
|
443
|
+
|
|
444
|
+
self.console.print(PRICE_INFO.format(owned, price_usd, price_usd_owned))
|
|
445
|
+
custom_item_usd_total += price_usd_owned
|
|
446
|
+
|
|
447
|
+
if not self.config.getboolean("App Settings", "use_proxy", fallback=False):
|
|
448
|
+
time.sleep(1)
|
|
449
|
+
|
|
450
|
+
return custom_item_usd_total
|
|
451
|
+
|
|
413
452
|
def identify_background_task(self):
|
|
414
453
|
"""
|
|
415
454
|
Search the OS for a daily background task that runs the scraper.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cs2tracker
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.8
|
|
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
|
|
@@ -42,6 +42,7 @@ Dynamic: license-file
|
|
|
42
42
|
|
|
43
43
|
- Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required on Linux)
|
|
44
44
|
- Register for the [Crawlbase Smart Proxy API](https://crawlbase.com/) and retrieve your API key. (Optional)
|
|
45
|
+
- Create a [Discord Webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to be notified about recent price updates. (Optional)
|
|
45
46
|
|
|
46
47
|
### Setup
|
|
47
48
|
|
|
@@ -58,6 +59,7 @@ Dynamic: license-file
|
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
2. Run it:
|
|
62
|
+
|
|
61
63
|
```bash
|
|
62
64
|
cs2tracker
|
|
63
65
|
```
|
|
@@ -65,7 +67,7 @@ Dynamic: license-file
|
|
|
65
67
|
### Options
|
|
66
68
|
|
|
67
69
|
- `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
|
|
68
|
-
- `Edit Config` to
|
|
70
|
+
- `Edit Config` to specify the numbers of items owned in the config file. You can also add items other than cases and sticker capsules following the format in the `Custom Items` section. (item_name = item_owned item_page)
|
|
69
71
|
- `Show History` to see a price chart consisting of past calculations. A new data point is generated once a day upon running the program.
|
|
70
72
|
- `Daily Background Calculations` to automatically run a daily calculation of your investment in the background and save the results such that they can later be viewed via `Show History`.
|
|
71
73
|
- `Receive Discord Notifications` to receive a notification on your Discord server when the program has finished calculating your investment. You need to set up a [webhook](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) in your Discord server and enter the webhook url into the `discord_webhook_url` field in the config file.
|
|
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
|