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.

Files changed (29) hide show
  1. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/PKG-INFO +4 -2
  2. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/README.md +3 -1
  3. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/_version.py +2 -2
  4. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/application.py +3 -3
  5. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/data/config.ini +3 -0
  6. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/main.py +1 -1
  7. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/scraper.py +82 -43
  8. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/PKG-INFO +4 -2
  9. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.flake8 +0 -0
  10. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.gitignore +0 -0
  11. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.isort.cfg +0 -0
  12. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.pre-commit-config.yaml +0 -0
  13. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/.pylintrc +0 -0
  14. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/LICENSE.md +0 -0
  15. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/MANIFEST.in +0 -0
  16. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/assets/icon.png +0 -0
  17. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/__init__.py +0 -0
  18. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/__main__.py +0 -0
  19. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/constants.py +0 -0
  20. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/data/output.csv +0 -0
  21. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker/padded_console.py +0 -0
  22. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/SOURCES.txt +0 -0
  23. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/dependency_links.txt +0 -0
  24. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/entry_points.txt +0 -0
  25. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/requires.txt +0 -0
  26. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/cs2tracker.egg-info/top_level.txt +0 -0
  27. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/pyproject.toml +0 -0
  28. {cs2tracker-2.1.7 → cs2tracker-2.1.8}/requirements.txt +0 -0
  29. {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.7
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 change the specific numbers of each item you own and then save the config file.
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 change the specific numbers of each item you own and then save the config file.
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.
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.1.7'
21
- __version_tuple__ = version_tuple = (2, 1, 7)
20
+ __version__ = version = '2.1.8'
21
+ __version_tuple__ = version_tuple = (2, 1, 8)
@@ -118,7 +118,7 @@ class Application:
118
118
  lambda: self._toggle_background_task(background_checkbox_value.get()),
119
119
  )
120
120
 
121
- discord_webhook_value = tk.BooleanVar(
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
- discord_webhook_value,
130
- lambda: self._toggle_discord_webhook(discord_webhook_value.get()),
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 _parse_capsule_price(self, capsule_page, capsule_href):
305
+ def _parse_item_price(self, item_page, item_href):
296
306
  """
297
- Parse the price of a capsule from the given page and href.
307
+ Parse the price of an item from the given steamcommunity market page and item
308
+ href.
298
309
 
299
- :param capsule_page: The HTTP response object containing the capsule page
300
- content.
301
- :param capsule_href: The href of the capsule listing to find the price for.
302
- :return: The price of the capsule as a float.
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
- capsule_soup = BeautifulSoup(capsule_page.content, "html.parser")
306
- capsule_listing = capsule_soup.find("a", attrs={"href": f"{capsule_href}"})
307
- if not isinstance(capsule_listing, Tag):
308
- raise ValueError(f"Failed to find capsule listing: {capsule_href}")
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
- price_span = capsule_listing.find("span", attrs={"class": "normal_price"})
311
- if not isinstance(price_span, Tag):
312
- raise ValueError(f"Failed to find price span in capsule listing: {capsule_href}")
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 = price_span.text.split()[2]
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._parse_capsule_price(capsule_page, capsule_href)
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._parse_case_price(case_page, CASE_HREFS[case_index])
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.7
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 change the specific numbers of each item you own and then save the config file.
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