cs2tracker 2.1.6__tar.gz → 2.1.7__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 (30) hide show
  1. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/.pylintrc +2 -1
  2. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/PKG-INFO +6 -5
  3. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/README.md +5 -4
  4. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/_version.py +2 -2
  5. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/application.py +68 -22
  6. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/constants.py +6 -2
  7. cs2tracker-2.1.7/cs2tracker/data/config.ini +202 -0
  8. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/main.py +5 -1
  9. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/scraper.py +118 -3
  10. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker.egg-info/PKG-INFO +6 -5
  11. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker.egg-info/SOURCES.txt +1 -1
  12. cs2tracker-2.1.6/cs2tracker/data/config.ini +0 -198
  13. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/.flake8 +0 -0
  14. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/.gitignore +0 -0
  15. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/.isort.cfg +0 -0
  16. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/.pre-commit-config.yaml +0 -0
  17. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/LICENSE.md +0 -0
  18. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/MANIFEST.in +0 -0
  19. /cs2tracker-2.1.6/assets/icons8-counter-strike-bubbles-96.png → /cs2tracker-2.1.7/assets/icon.png +0 -0
  20. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/__init__.py +0 -0
  21. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/__main__.py +0 -0
  22. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/data/output.csv +0 -0
  23. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker/padded_console.py +0 -0
  24. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker.egg-info/dependency_links.txt +0 -0
  25. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker.egg-info/entry_points.txt +0 -0
  26. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker.egg-info/requires.txt +0 -0
  27. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/cs2tracker.egg-info/top_level.txt +0 -0
  28. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/pyproject.toml +0 -0
  29. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/requirements.txt +0 -0
  30. {cs2tracker-2.1.6 → cs2tracker-2.1.7}/setup.cfg +0 -0
@@ -6,4 +6,5 @@ disable=
6
6
  missing-class-docstring,
7
7
  consider-using-with,
8
8
  import-error,
9
- no-else-return
9
+ no-else-return,
10
+ broad-exception-caught
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cs2tracker
3
- Version: 2.1.6
3
+ Version: 2.1.7
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
@@ -40,12 +40,12 @@ Dynamic: license-file
40
40
 
41
41
  ### Prerequisites
42
42
 
43
- - Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required for Linux)
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
45
 
46
46
  ### Setup
47
47
 
48
- #### Windows Executable *(no color support)*
48
+ #### Windows Executable _(no color support)_
49
49
 
50
50
  - Simply [download the latest executable](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-windows.zip) and run it.
51
51
 
@@ -67,8 +67,9 @@ Dynamic: license-file
67
67
  - `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
68
68
  - `Edit Config` to change the specific numbers of each item you own and then save the config file.
69
69
  - `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
- - `Daily Background Calculation` 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
- - If you want to prevent your requests from being rate limited by the steamcommunity server, register for an API key on [Crawlbase](crawlbase.com) and enter it into the `API_Key` field in the config file. This will route every request through a different proxy server.
70
+ - `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
+ - `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.
72
+ - `Proxy Requests` to prevent your requests from being rate limited by the steamcommunity server. You need to register for a free API key on [Crawlbase](crawlbase.com) and enter it into the `api_key` field in the config file.
72
73
 
73
74
  ---
74
75
 
@@ -17,12 +17,12 @@
17
17
 
18
18
  ### Prerequisites
19
19
 
20
- - Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required for Linux)
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
22
 
23
23
  ### Setup
24
24
 
25
- #### Windows Executable *(no color support)*
25
+ #### Windows Executable _(no color support)_
26
26
 
27
27
  - Simply [download the latest executable](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-windows.zip) and run it.
28
28
 
@@ -44,8 +44,9 @@
44
44
  - `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
45
45
  - `Edit Config` to change the specific numbers of each item you own and then save the config file.
46
46
  - `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
- - `Daily Background Calculation` 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
- - If you want to prevent your requests from being rate limited by the steamcommunity server, register for an API key on [Crawlbase](crawlbase.com) and enter it into the `API_Key` field in the config file. This will route every request through a different proxy server.
47
+ - `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
+ - `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.
49
+ - `Proxy Requests` to prevent your requests from being rate limited by the steamcommunity server. You need to register for a free API key on [Crawlbase](crawlbase.com) and enter it into the `api_key` field in the config file.
49
50
 
50
51
  ---
51
52
 
@@ -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.6'
21
- __version_tuple__ = version_tuple = (2, 1, 6)
20
+ __version__ = version = '2.1.7'
21
+ __version_tuple__ = version_tuple = (2, 1, 7)
@@ -1,3 +1,4 @@
1
+ import ctypes
1
2
  import tkinter as tk
2
3
  from subprocess import Popen
3
4
  from threading import Thread
@@ -9,6 +10,7 @@ from matplotlib.dates import DateFormatter
9
10
 
10
11
  from cs2tracker.constants import (
11
12
  CONFIG_FILE,
13
+ ICON_FILE,
12
14
  OS,
13
15
  OUTPUT_FILE,
14
16
  PYTHON_EXECUTABLE,
@@ -18,8 +20,9 @@ from cs2tracker.constants import (
18
20
  )
19
21
  from cs2tracker.scraper import Scraper
20
22
 
21
- WINDOW_TITLE = "CS2Tracker"
22
- WINDOW_SIZE = "450x380"
23
+ APPLICATION_NAME = "CS2Tracker"
24
+
25
+ WINDOW_SIZE = "500x450"
23
26
  BACKGROUND_COLOR = "#1e1e1e"
24
27
  BUTTON_COLOR = "#3c3f41"
25
28
  BUTTON_HOVER_COLOR = "#505354"
@@ -27,9 +30,8 @@ BUTTON_ACTIVE_COLOR = "#5c5f61"
27
30
  FONT_STYLE = "Segoe UI"
28
31
  FONT_COLOR = "white"
29
32
 
30
- SCRAPER_WINDOW_TITLE = "CS2Tracker"
31
33
  SCRAPER_WINDOW_HEIGHT = 40
32
- SCRAPER_WINDOW_WIDTH = 100
34
+ SCRAPER_WINDOW_WIDTH = 120
33
35
  SCRAPER_WINDOW_BACKGROUND_COLOR = "Black"
34
36
 
35
37
 
@@ -58,21 +60,42 @@ class Application:
58
60
  button.bind("<Leave>", lambda _: button.config(bg=BUTTON_COLOR))
59
61
  return button
60
62
 
63
+ def _add_checkbox(self, frame, text, variable, command):
64
+ checkbox = tk.Checkbutton(
65
+ frame,
66
+ text=text,
67
+ variable=variable,
68
+ command=command,
69
+ bg=BACKGROUND_COLOR,
70
+ fg=FONT_COLOR,
71
+ selectcolor=BUTTON_COLOR,
72
+ activebackground=BACKGROUND_COLOR,
73
+ font=(FONT_STYLE, 10),
74
+ anchor="w",
75
+ padx=20,
76
+ )
77
+ checkbox.pack(fill="x", anchor="w", pady=2)
78
+
61
79
  def _configure_window(self):
62
80
  """Configure the main application window UI and add buttons for the main
63
81
  functionalities.
64
82
  """
65
83
  window = tk.Tk()
66
- window.title(WINDOW_TITLE)
84
+ window.title(APPLICATION_NAME)
67
85
  window.geometry(WINDOW_SIZE)
68
86
  window.configure(bg=BACKGROUND_COLOR)
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
+ window.wm_iconphoto(False, icon)
69
92
 
70
93
  frame = tk.Frame(window, bg=BACKGROUND_COLOR, padx=30, pady=30)
71
94
  frame.pack(expand=True, fill="both")
72
95
 
73
96
  label = tk.Label(
74
97
  frame,
75
- text=f"Welcome to {WINDOW_TITLE}!",
98
+ text=f"Welcome to {APPLICATION_NAME}!",
76
99
  font=(FONT_STYLE, 16, "bold"),
77
100
  fg=FONT_COLOR,
78
101
  bg=BACKGROUND_COLOR,
@@ -84,30 +107,46 @@ class Application:
84
107
  self._add_button(frame, "Show History (Chart)", self._draw_plot)
85
108
  self._add_button(frame, "Show History (File)", self._edit_log_file)
86
109
 
110
+ checkbox_frame = tk.Frame(frame, bg=BACKGROUND_COLOR)
111
+ checkbox_frame.pack(pady=(20, 0), fill="x")
112
+
87
113
  background_checkbox_value = tk.BooleanVar(value=self.scraper.identify_background_task())
88
- background_checkbox = tk.Checkbutton(
89
- frame,
90
- text="Daily Background Calculation",
91
- variable=background_checkbox_value,
92
- command=lambda: self._toggle_background_task(background_checkbox_value.get()),
93
- bg=BACKGROUND_COLOR,
94
- fg=FONT_COLOR,
95
- selectcolor=BUTTON_COLOR,
96
- activebackground=BACKGROUND_COLOR,
97
- font=(FONT_STYLE, 10),
114
+ self._add_checkbox(
115
+ checkbox_frame,
116
+ "Daily Background Calculations",
117
+ background_checkbox_value,
118
+ lambda: self._toggle_background_task(background_checkbox_value.get()),
119
+ )
120
+
121
+ discord_webhook_value = tk.BooleanVar(
122
+ value=self.scraper.config.getboolean(
123
+ "App Settings", "discord_notifications", fallback=False
124
+ )
125
+ )
126
+ self._add_checkbox(
127
+ checkbox_frame,
128
+ "Receive Discord Notifications",
129
+ discord_webhook_value,
130
+ lambda: self._toggle_discord_webhook(discord_webhook_value.get()),
131
+ )
132
+
133
+ use_proxy_checkbox_value = tk.BooleanVar(
134
+ value=self.scraper.config.getboolean("App Settings", "use_proxy", fallback=False)
135
+ )
136
+ self._add_checkbox(
137
+ checkbox_frame,
138
+ "Proxy Requests",
139
+ use_proxy_checkbox_value,
140
+ lambda: self._toggle_use_proxy(use_proxy_checkbox_value.get()),
98
141
  )
99
- background_checkbox.pack(pady=20)
100
142
 
101
143
  return window
102
144
 
103
145
  def _construct_scraper_command_windows(self):
104
146
  """Construct the command to run the scraper in a new window for Windows."""
105
- set_utf8_encoding = (
106
- "[Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.Encoding]::UTF8;"
107
- )
108
147
  get_size = "$size = $Host.UI.RawUI.WindowSize;"
109
148
  set_size = "$Host.UI.RawUI.WindowSize = $size;"
110
- set_window_title = f"$Host.UI.RawUI.WindowTitle = '{SCRAPER_WINDOW_TITLE}';"
149
+ set_window_title = f"$Host.UI.RawUI.WindowTitle = '{APPLICATION_NAME}';"
111
150
  set_window_width = (
112
151
  f"$size.Width = [Math]::Min({SCRAPER_WINDOW_WIDTH}, $Host.UI.RawUI.BufferSize.Width);"
113
152
  )
@@ -125,7 +164,6 @@ class Application:
125
164
 
126
165
  cmd = (
127
166
  'start powershell -NoExit -Command "& {'
128
- + set_utf8_encoding
129
167
  + set_window_title
130
168
  + get_size
131
169
  + set_window_width
@@ -189,6 +227,14 @@ class Application:
189
227
  """Toggle whether a daily price calculation should run in the background."""
190
228
  self.scraper.toggle_background_task(enabled)
191
229
 
230
+ def _toggle_use_proxy(self, enabled: bool):
231
+ """Toggle whether the scraper should use proxy servers for requests."""
232
+ self.scraper.toggle_use_proxy(enabled)
233
+
234
+ def _toggle_discord_webhook(self, enabled: bool):
235
+ """Toggle whether the scraper should send notifications to a Discord webhook."""
236
+ self.scraper.toggle_discord_webhook(enabled)
237
+
192
238
 
193
239
  def _popen_and_call(popen_args, callback):
194
240
  """
@@ -6,8 +6,10 @@ from shutil import copy
6
6
 
7
7
  try:
8
8
  from cs2tracker._version import version # pylint: disable=E0611
9
+
10
+ VERSION = f"v{version}"
9
11
  except ImportError:
10
- version = "0.0.0"
12
+ VERSION = "latest"
11
13
 
12
14
 
13
15
  class OSType(enum.Enum):
@@ -22,6 +24,7 @@ PYTHON_EXECUTABLE = sys.executable
22
24
 
23
25
  MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
24
26
  PROJECT_DIR = os.path.dirname(MODULE_DIR)
27
+ ICON_FILE = os.path.join(PROJECT_DIR, "assets", "icon.png")
25
28
  OUTPUT_FILE = os.path.join(MODULE_DIR, "data", "output.csv")
26
29
  CONFIG_FILE = os.path.join(MODULE_DIR, "data", "config.ini")
27
30
  BATCH_FILE = os.path.join(MODULE_DIR, "data", "cs2tracker_scraper.bat")
@@ -33,6 +36,7 @@ if RUNNING_IN_EXE:
33
36
  MEIPASS_DIR = sys._MEIPASS # type: ignore pylint: disable=protected-access
34
37
  MODULE_DIR = MEIPASS_DIR
35
38
  PROJECT_DIR = MEIPASS_DIR
39
+ ICON_FILE = os.path.join(PROJECT_DIR, "assets", "icon.png")
36
40
  CONFIG_FILE_SOURCE = os.path.join(MODULE_DIR, "data", "config.ini")
37
41
  OUTPUT_FILE_SOURCE = os.path.join(MODULE_DIR, "data", "output.csv")
38
42
 
@@ -60,7 +64,7 @@ BANNER = """
60
64
 
61
65
  """
62
66
  AUTHOR_STRING = (
63
- f"Version: v{version} - {datetime.today().strftime('%Y/%m/%d')} - Jannik Novak @ashiven\n"
67
+ f"Version: {VERSION} - {datetime.today().strftime('%Y/%m/%d')} - Jannik Novak @ashiven\n"
64
68
  )
65
69
 
66
70
 
@@ -0,0 +1,202 @@
1
+ [User Settings]
2
+ api_key = None
3
+ discord_webhook_url = None
4
+
5
+ [App Settings]
6
+ use_proxy = False
7
+ discord_notifications = False
8
+
9
+ [Cases]
10
+ revolution_case = 0
11
+ recoil_case = 0
12
+ dreams_and_nightmares_case = 0
13
+ operation_riptide_case = 0
14
+ snakebite_case = 0
15
+ operation_broken_fang_case = 0
16
+ fracture_case = 0
17
+ chroma_case = 0
18
+ chroma_2_case = 0
19
+ chroma_3_case = 0
20
+ clutch_case = 0
21
+ csgo_weapon_case = 0
22
+ csgo_weapon_case_2 = 0
23
+ csgo_weapon_case_3 = 0
24
+ cs20_case = 0
25
+ danger_zone_case = 0
26
+ esports_2013_case = 0
27
+ esports_2013_winter_case = 0
28
+ esports_2014_summer_case = 0
29
+ falchion_case = 0
30
+ gamma_case = 0
31
+ gamma_2_case = 0
32
+ glove_case = 0
33
+ horizon_case = 0
34
+ huntsman_case = 0
35
+ operation_bravo_case = 0
36
+ operation_breakout_case = 0
37
+ operation_hydra_case = 0
38
+ operation_phoenix_case = 0
39
+ operation_vanguard_case = 0
40
+ operation_wildfire_case = 0
41
+ prisma_case = 0
42
+ prisma_2_case = 0
43
+ revolver_case = 0
44
+ shadow_case = 0
45
+ shattered_web_case = 0
46
+ spectrum_case = 0
47
+ spectrum_2_case = 0
48
+ winter_offensive_case = 0
49
+ kilowatt_case = 0
50
+ gallery_case = 0
51
+ fever_case = 0
52
+
53
+ [Katowice 2014 Sticker Capsule]
54
+ katowice_legends = 0
55
+ katowice_challengers = 0
56
+
57
+ [Cologne 2014 Sticker Capsule]
58
+ cologne_legends = 0
59
+ cologne_challengers = 0
60
+
61
+ [DreamHack 2014 Sticker Capsule]
62
+ dreamhack_legends = 0
63
+
64
+ [Katowice 2015 Sticker Capsule]
65
+ katowice_legends = 0
66
+ katowice_challengers = 0
67
+
68
+ [Cologne 2015 Sticker Capsule]
69
+ cologne_legends = 0
70
+ cologne_challengers = 0
71
+
72
+ [Cluj-Napoca 2015 Sticker Capsule]
73
+ cluj_napoca_legends = 0
74
+ cluj_napoca_challengers = 0
75
+ cluj_napoca_legends_autographs = 0
76
+ cluj_napoca_challengers_autographs = 0
77
+
78
+ [Columbus 2016 Sticker Capsule]
79
+ columbus_legends = 0
80
+ columbus_challengers = 0
81
+ columbus_legends_autographs = 0
82
+ columbus_challengers_autographs = 0
83
+
84
+ [Cologne 2016 Sticker Capsule]
85
+ cologne_legends = 0
86
+ cologne_challengers = 0
87
+ cologne_legends_autographs = 0
88
+ cologne_challengers_autographs = 0
89
+
90
+ [Atlanta 2017 Sticker Capsule]
91
+ atlanta_legends = 0
92
+ atlanta_challengers = 0
93
+ atlanta_legends_autographs = 0
94
+ atlanta_challengers_autographs = 0
95
+
96
+ [Krakow 2017 Sticker Capsule]
97
+ krakow_legends = 0
98
+ krakow_challengers = 0
99
+ krakow_legends_autographs = 0
100
+ krakow_challengers_autographs = 0
101
+
102
+ [Boston 2018 Sticker Capsule]
103
+ boston_legends = 0
104
+ boston_minor_challengers = 0
105
+ boston_returning_challengers = 0
106
+ boston_attending_legends = 0
107
+ boston_minor_challengers_with_flash_gaming = 0
108
+ boston_legends_autographs = 0
109
+ boston_minor_challengers_autographs = 0
110
+ boston_returning_challengers_autographs = 0
111
+ boston_attending_legends_autographs = 0
112
+ boston_minor_challengers_with_flash_gaming_autographs = 0
113
+
114
+ [London 2018 Sticker Capsule]
115
+ london_legends = 0
116
+ london_minor_challengers = 0
117
+ london_returning_challengers = 0
118
+ london_legends_autographs = 0
119
+ london_minor_challengers_autographs = 0
120
+ london_returning_challengers_autographs = 0
121
+
122
+ [Katowice 2019 Sticker Capsule]
123
+ katowice_legends = 0
124
+ katowice_minor_challengers = 0
125
+ katowice_returning_challengers = 0
126
+ katowice_legends_autographs = 0
127
+ katowice_minor_challengers_autographs = 0
128
+ katowice_returning_challengers_autographs = 0
129
+
130
+ [Berlin 2019 Sticker Capsule]
131
+ berlin_legends = 0
132
+ berlin_minor_challengers = 0
133
+ berlin_returning_challengers = 0
134
+ berlin_legends_autographs = 0
135
+ berlin_minor_challengers_autographs = 0
136
+ berlin_returning_challengers_autographs = 0
137
+
138
+ [2020 RMR Sticker Capsule]
139
+ rmr_legends = 0
140
+ rmr_challengers = 0
141
+ rmr_contenders = 0
142
+
143
+ [Stockholm 2021 Sticker Capsule]
144
+ stockholm_legends = 0
145
+ stockholm_challengers = 0
146
+ stockholm_contenders = 0
147
+ stockholm_champions_autographs = 0
148
+ stockholm_finalists_autographs = 0
149
+
150
+ [Antwerp 2022 Sticker Capsule]
151
+ antwerp_legends = 0
152
+ antwerp_challengers = 0
153
+ antwerp_contenders = 0
154
+ antwerp_champions_autographs = 0
155
+ antwerp_challengers_autographs = 0
156
+ antwerp_legends_autographs = 0
157
+ antwerp_contenders_autographs = 0
158
+
159
+ [Rio 2022 Sticker Capsule]
160
+ rio_legends = 0
161
+ rio_challengers = 0
162
+ rio_contenders = 0
163
+ rio_champions_autographs = 0
164
+ rio_challengers_autographs = 0
165
+ rio_legends_autographs = 0
166
+ rio_contenders_autographs = 0
167
+
168
+ [Paris 2023 Sticker Capsule]
169
+ paris_legends = 0
170
+ paris_challengers = 0
171
+ paris_contenders = 0
172
+ paris_champions_autographs = 0
173
+ paris_challengers_autographs = 0
174
+ paris_legends_autographs = 0
175
+ paris_contenders_autographs = 0
176
+
177
+ [Copenhagen 2024 Sticker Capsule]
178
+ copenhagen_legends = 0
179
+ copenhagen_challengers = 0
180
+ copenhagen_contenders = 0
181
+ copenhagen_champions_autographs = 0
182
+ copenhagen_challengers_autographs = 0
183
+ copenhagen_legends_autographs = 0
184
+ copenhagen_contenders_autographs = 0
185
+
186
+ [Shanghai 2024 Sticker Capsule]
187
+ shanghai_legends = 0
188
+ shanghai_challengers = 0
189
+ shanghai_contenders = 0
190
+ shanghai_champions_autographs = 0
191
+ shanghai_challengers_autographs = 0
192
+ shanghai_legends_autographs = 0
193
+ shanghai_contenders_autographs = 0
194
+
195
+ [Austin 2025 Sticker Capsule]
196
+ austin_legends = 0
197
+ austin_challengers = 0
198
+ austin_contenders = 0
199
+ austin_champions_autographs = 0
200
+ austin_challengers_autographs = 0
201
+ austin_legends_autographs = 0
202
+ austin_contenders_autographs = 0
@@ -3,7 +3,7 @@ import sys
3
3
  import urllib3
4
4
 
5
5
  from cs2tracker.application import Application
6
- from cs2tracker.constants import AUTHOR_STRING, BANNER
6
+ from cs2tracker.constants import AUTHOR_STRING, BANNER, OS, OSType
7
7
  from cs2tracker.padded_console import PaddedConsole
8
8
  from cs2tracker.scraper import Scraper
9
9
 
@@ -19,6 +19,10 @@ def main():
19
19
  # Disable warnings for proxy requests
20
20
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
21
21
 
22
+ # Set output encoding to UTF-8 with BOM for Windows compatibility
23
+ if OS == OSType.WINDOWS:
24
+ sys.stdout.reconfigure(encoding="utf-8-sig") # type: ignore
25
+
22
26
  console = PaddedConsole()
23
27
  console.print(f"[bold yellow]{BANNER}\n{AUTHOR_STRING}\n")
24
28
 
@@ -36,6 +36,10 @@ PRICE_INFO = "Owned: {:<10} Steam market price: ${:<10} Total: ${:<10}\n"
36
36
  HTTP_PROXY_URL = "http://{}:@smartproxy.crawlbase.com:8012"
37
37
  HTTPS_PROXY_URL = "http://{}:@smartproxy.crawlbase.com:8012"
38
38
 
39
+ DC_WEBHOOK_USERNAME = "CS2Tracker"
40
+ DC_WEBHOOK_AVATAR_URL = "https://img.icons8.com/?size=100&id=uWQJp2tLXUH6&format=png&color=000000"
41
+ DC_RECENT_HISTORY_LIMIT = 5
42
+
39
43
  WIN_BACKGROUND_TASK_NAME = "CS2Tracker Daily Calculation"
40
44
  WIN_BACKGROUND_TASK_SCHEDULE = "DAILY"
41
45
  WIN_BACKGROUND_TASK_TIME = "12:00"
@@ -97,6 +101,7 @@ class Scraper:
97
101
 
98
102
  self._print_total()
99
103
  self._save_price_log()
104
+ self._send_discord_notification()
100
105
 
101
106
  # Reset totals for next run
102
107
  self.usd_total, self.eur_total = 0, 0
@@ -122,6 +127,9 @@ class Scraper:
122
127
 
123
128
  This will append a new entry to the output file if no entry has been made for
124
129
  today.
130
+
131
+ :raises FileNotFoundError: If the output file does not exist.
132
+ :raises IOError: If there is an error writing to the output file.
125
133
  """
126
134
  with open(OUTPUT_FILE, "r", encoding="utf-8") as price_logs:
127
135
  price_logs_reader = csv.reader(price_logs)
@@ -157,6 +165,8 @@ class Scraper:
157
165
  data is used for drawing the plot of past prices.
158
166
 
159
167
  :return: A tuple containing three lists: dates, dollar prices, and euro prices.
168
+ :raises FileNotFoundError: If the output file does not exist.
169
+ :raises IOError: If there is an error reading the output file.
160
170
  """
161
171
  dates, dollars, euros = [], [], []
162
172
  with open(OUTPUT_FILE, "r", encoding="utf-8") as price_logs:
@@ -173,6 +183,82 @@ class Scraper:
173
183
 
174
184
  return dates, dollars, euros
175
185
 
186
+ def _construct_recent_calculations_embeds(self):
187
+ """
188
+ Construct the embeds for the Discord message that will be sent after a price
189
+ calculation has been made.
190
+
191
+ :return: A list of embeds for the Discord message.
192
+ """
193
+ dates, usd_logs, eur_logs = self.read_price_log()
194
+ dates, usd_logs, eur_logs = reversed(dates), reversed(usd_logs), reversed(eur_logs)
195
+
196
+ date_history, usd_history, eur_history = [], [], []
197
+ for date, usd_log, eur_log in zip(dates, usd_logs, eur_logs):
198
+ if len(date_history) >= DC_RECENT_HISTORY_LIMIT:
199
+ break
200
+ date_history.append(date.strftime("%Y-%m-%d"))
201
+ usd_history.append(f"${usd_log:.2f}")
202
+ eur_history.append(f"€{eur_log:.2f}")
203
+
204
+ date_history = "\n".join(date_history)
205
+ usd_history = "\n".join(usd_history)
206
+ eur_history = "\n".join(eur_history)
207
+
208
+ embeds = [
209
+ {
210
+ "title": "📊 Recent Price History",
211
+ "color": 5814783,
212
+ "fields": [
213
+ {
214
+ "name": "Date",
215
+ "value": date_history,
216
+ "inline": True,
217
+ },
218
+ {
219
+ "name": "USD Total",
220
+ "value": usd_history,
221
+ "inline": True,
222
+ },
223
+ {
224
+ "name": "EUR Total",
225
+ "value": eur_history,
226
+ "inline": True,
227
+ },
228
+ ],
229
+ }
230
+ ]
231
+
232
+ return embeds
233
+
234
+ def _send_discord_notification(self):
235
+ """Send a message to a Discord webhook if notifications are enabled in the
236
+ config file and a webhook URL is provided.
237
+ """
238
+ discord_notifications = self.config.getboolean(
239
+ "App Settings", "discord_notifications", fallback=False
240
+ )
241
+ webhook_url = self.config.get("User Settings", "discord_webhook_url", fallback=None)
242
+ webhook_url = None if webhook_url in ("None", "") else webhook_url
243
+
244
+ if discord_notifications and webhook_url:
245
+ embeds = self._construct_recent_calculations_embeds()
246
+ try:
247
+ response = self.session.post(
248
+ url=webhook_url,
249
+ json={
250
+ "embeds": embeds,
251
+ "username": DC_WEBHOOK_USERNAME,
252
+ "avatar_url": DC_WEBHOOK_AVATAR_URL,
253
+ },
254
+ )
255
+ response.raise_for_status()
256
+ self.console.print("[bold steel_blue3][+] Discord notification sent.\n")
257
+ except RequestException as error:
258
+ self.console.print(f"[bold red][!] Failed to send Discord notification: {error}\n")
259
+ except Exception as error:
260
+ self.console.print(f"[bold red][!] An unexpected error occurred: {error}\n")
261
+
176
262
  @retry(stop=stop_after_attempt(10))
177
263
  def _get_page(self, url):
178
264
  """
@@ -184,8 +270,8 @@ class Scraper:
184
270
  :raises RequestException: If the request fails.
185
271
  :raises RetryError: If the retry limit is reached.
186
272
  """
187
- use_proxy = self.config.getboolean("Settings", "Use_Proxy", fallback=False)
188
- api_key = self.config.get("Settings", "API_Key", fallback=None)
273
+ use_proxy = self.config.getboolean("App Settings", "use_proxy", fallback=False)
274
+ api_key = self.config.get("User Settings", "api_key", fallback=None)
189
275
  api_key = None if api_key in ("None", "") else api_key
190
276
  if use_proxy and api_key:
191
277
  page = self.session.get(
@@ -319,7 +405,7 @@ class Scraper:
319
405
  self.console.print(PRICE_INFO.format(owned, price_usd, price_usd_owned))
320
406
  case_usd_total += price_usd_owned
321
407
 
322
- if not self.config.getboolean("Settings", "Use_Proxy", fallback=False):
408
+ if not self.config.getboolean("App Settings", "use_proxy", fallback=False):
323
409
  time.sleep(1)
324
410
 
325
411
  return case_usd_total
@@ -406,6 +492,35 @@ class Scraper:
406
492
  # TODO: implement toggle for cron jobs
407
493
  pass
408
494
 
495
+ def toggle_use_proxy(self, enabled: bool):
496
+ """
497
+ Toggle the use of proxies for requests. This will update the configuration file.
498
+
499
+ :param enabled: If True, proxies will be used; if False, they will not be used.
500
+ """
501
+ self.config.set("App Settings", "use_proxy", str(enabled))
502
+ with open(CONFIG_FILE, "w", encoding="utf-8") as config_file:
503
+ self.config.write(config_file)
504
+
505
+ self.console.print(
506
+ f"[bold green]{'[+] Enabled' if enabled else '[-] Disabled'} proxy usage for requests."
507
+ )
508
+
509
+ def toggle_discord_webhook(self, enabled: bool):
510
+ """
511
+ Toggle the use of a Discord webhook to notify users of price calculations.
512
+
513
+ :param enabled: If True, the webhook will be used; if False, it will not be
514
+ used.
515
+ """
516
+ self.config.set("App Settings", "discord_notifications", str(enabled))
517
+ with open(CONFIG_FILE, "w", encoding="utf-8") as config_file:
518
+ self.config.write(config_file)
519
+
520
+ self.console.print(
521
+ f"[bold green]{'[+] Enabled' if enabled else '[-] Disabled'} Discord webhook notifications."
522
+ )
523
+
409
524
 
410
525
  if __name__ == "__main__":
411
526
  scraper = Scraper()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cs2tracker
3
- Version: 2.1.6
3
+ Version: 2.1.7
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
@@ -40,12 +40,12 @@ Dynamic: license-file
40
40
 
41
41
  ### Prerequisites
42
42
 
43
- - Download and install the latest versions of [Python](https://www.python.org/downloads/) and [Pip](https://pypi.org/project/pip/). (Required for Linux)
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
45
 
46
46
  ### Setup
47
47
 
48
- #### Windows Executable *(no color support)*
48
+ #### Windows Executable _(no color support)_
49
49
 
50
50
  - Simply [download the latest executable](https://github.com/ashiven/cs2tracker/releases/latest/download/cs2tracker-windows.zip) and run it.
51
51
 
@@ -67,8 +67,9 @@ Dynamic: license-file
67
67
  - `Run!` to gather the current market prices of your items and calculate the total amount in USD and EUR.
68
68
  - `Edit Config` to change the specific numbers of each item you own and then save the config file.
69
69
  - `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
- - `Daily Background Calculation` 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
- - If you want to prevent your requests from being rate limited by the steamcommunity server, register for an API key on [Crawlbase](crawlbase.com) and enter it into the `API_Key` field in the config file. This will route every request through a different proxy server.
70
+ - `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
+ - `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.
72
+ - `Proxy Requests` to prevent your requests from being rate limited by the steamcommunity server. You need to register for a free API key on [Crawlbase](crawlbase.com) and enter it into the `api_key` field in the config file.
72
73
 
73
74
  ---
74
75
 
@@ -9,7 +9,7 @@ README.md
9
9
  pyproject.toml
10
10
  requirements.txt
11
11
  setup.cfg
12
- assets/icons8-counter-strike-bubbles-96.png
12
+ assets/icon.png
13
13
  cs2tracker/__init__.py
14
14
  cs2tracker/__main__.py
15
15
  cs2tracker/_version.py
@@ -1,198 +0,0 @@
1
- [Settings]
2
- Use_Proxy = False
3
- API_Key = None
4
-
5
- [Cases]
6
- Revolution_Case = 0
7
- Recoil_Case = 0
8
- Dreams_And_Nightmares_Case = 0
9
- Operation_Riptide_Case = 0
10
- Snakebite_Case = 0
11
- Operation_Broken_Fang_Case = 0
12
- Fracture_Case = 0
13
- Chroma_Case = 0
14
- Chroma_2_Case = 0
15
- Chroma_3_Case = 0
16
- Clutch_Case = 0
17
- CSGO_Weapon_Case = 0
18
- CSGO_Weapon_Case_2 = 0
19
- CSGO_Weapon_Case_3 = 0
20
- CS20_Case = 0
21
- Danger_Zone_Case = 0
22
- eSports_2013_Case = 0
23
- eSports_2013_Winter_Case = 0
24
- eSports_2014_Summer_Case = 0
25
- Falchion_Case = 0
26
- Gamma_Case = 0
27
- Gamma_2_Case = 0
28
- Glove_Case = 0
29
- Horizon_Case = 0
30
- Huntsman_Case = 0
31
- Operation_Bravo_Case = 0
32
- Operation_Breakout_Case = 0
33
- Operation_Hydra_Case = 0
34
- Operation_Phoenix_Case = 0
35
- Operation_Vanguard_Case = 0
36
- Operation_Wildfire_Case = 0
37
- Prisma_Case = 0
38
- Prisma_2_Case = 0
39
- Revolver_Case = 0
40
- Shadow_Case = 0
41
- Shattered_Web_Case = 0
42
- Spectrum_Case = 0
43
- Spectrum_2_Case = 0
44
- Winter_Offensive_Case = 0
45
- Kilowatt_Case = 0
46
- Gallery_Case = 0
47
- Fever_Case = 0
48
-
49
- [Katowice 2014 Sticker Capsule]
50
- Katowice_Legends = 0
51
- Katowice_Challengers = 0
52
-
53
- [Cologne 2014 Sticker Capsule]
54
- Cologne_Legends = 0
55
- Cologne_Challengers = 0
56
-
57
- [DreamHack 2014 Sticker Capsule]
58
- DreamHack_Legends = 0
59
-
60
- [Katowice 2015 Sticker Capsule]
61
- Katowice_Legends = 0
62
- Katowice_Challengers = 0
63
-
64
- [Cologne 2015 Sticker Capsule]
65
- Cologne_Legends = 0
66
- Cologne_Challengers = 0
67
-
68
- [Cluj-Napoca 2015 Sticker Capsule]
69
- Cluj_Napoca_Legends = 0
70
- Cluj_Napoca_Challengers = 0
71
- Cluj_Napoca_Legends_Autographs = 0
72
- Cluj_Napoca_Challengers_Autographs = 0
73
-
74
- [Columbus 2016 Sticker Capsule]
75
- Columbus_Legends = 0
76
- Columbus_Challengers = 0
77
- Columbus_Legends_Autographs = 0
78
- Columbus_Challengers_Autographs = 0
79
-
80
- [Cologne 2016 Sticker Capsule]
81
- Cologne_Legends = 0
82
- Cologne_Challengers = 0
83
- Cologne_Legends_Autographs = 0
84
- Cologne_Challengers_Autographs = 0
85
-
86
- [Atlanta 2017 Sticker Capsule]
87
- Atlanta_Legends = 0
88
- Atlanta_Challengers = 0
89
- Atlanta_Legends_Autographs = 0
90
- Atlanta_Challengers_Autographs = 0
91
-
92
- [Krakow 2017 Sticker Capsule]
93
- Krakow_Legends = 0
94
- Krakow_Challengers = 0
95
- Krakow_Legends_Autographs = 0
96
- Krakow_Challengers_Autographs = 0
97
-
98
- [Boston 2018 Sticker Capsule]
99
- Boston_Legends = 0
100
- Boston_Minor_Challengers = 0
101
- Boston_Returning_Challengers = 0
102
- Boston_Attending_Legends = 0
103
- Boston_Minor_Challengers_with_Flash_Gaming = 0
104
- Boston_Legends_Autographs = 0
105
- Boston_Minor_Challengers_Autographs = 0
106
- Boston_Returning_Challengers_Autographs = 0
107
- Boston_Attending_Legends_Autographs = 0
108
- Boston_Minor_Challengers_with_Flash_Gaming_Autographs = 0
109
-
110
- [London 2018 Sticker Capsule]
111
- London_Legends = 0
112
- London_Minor_Challengers = 0
113
- London_Returning_Challengers = 0
114
- London_Legends_Autographs = 0
115
- London_Minor_Challengers_Autographs = 0
116
- London_Returning_Challengers_Autographs = 0
117
-
118
- [Katowice 2019 Sticker Capsule]
119
- Katowice_Legends = 0
120
- Katowice_Minor_Challengers = 0
121
- Katowice_Returning_Challengers = 0
122
- Katowice_Legends_Autographs = 0
123
- Katowice_Minor_Challengers_Autographs = 0
124
- Katowice_Returning_Challengers_Autographs = 0
125
-
126
- [Berlin 2019 Sticker Capsule]
127
- Berlin_Legends = 0
128
- Berlin_Minor_Challengers = 0
129
- Berlin_Returning_Challengers = 0
130
- Berlin_Legends_Autographs = 0
131
- Berlin_Minor_Challengers_Autographs = 0
132
- Berlin_Returning_Challengers_Autographs = 0
133
-
134
- [2020 RMR Sticker Capsule]
135
- RMR_Legends = 0
136
- RMR_Challengers = 0
137
- RMR_Contenders = 0
138
-
139
- [Stockholm 2021 Sticker Capsule]
140
- Stockholm_Legends = 0
141
- Stockholm_Challengers = 0
142
- Stockholm_Contenders = 0
143
- Stockholm_Champions_Autographs = 0
144
- Stockholm_Finalists_Autographs = 0
145
-
146
- [Antwerp 2022 Sticker Capsule]
147
- Antwerp_Legends = 0
148
- Antwerp_Challengers = 0
149
- Antwerp_Contenders = 0
150
- Antwerp_Champions_Autographs = 0
151
- Antwerp_Challengers_Autographs = 0
152
- Antwerp_Legends_Autographs = 0
153
- Antwerp_Contenders_Autographs = 0
154
-
155
- [Rio 2022 Sticker Capsule]
156
- Rio_Legends = 0
157
- Rio_Challengers = 0
158
- Rio_Contenders = 0
159
- Rio_Champions_Autographs = 0
160
- Rio_Challengers_Autographs = 0
161
- Rio_Legends_Autographs = 0
162
- Rio_Contenders_Autographs = 0
163
-
164
- [Paris 2023 Sticker Capsule]
165
- Paris_Legends = 0
166
- Paris_Challengers = 0
167
- Paris_Contenders = 0
168
- Paris_Champions_Autographs = 0
169
- Paris_Challengers_Autographs = 0
170
- Paris_Legends_Autographs = 0
171
- Paris_Contenders_Autographs = 0
172
-
173
- [Copenhagen 2024 Sticker Capsule]
174
- Copenhagen_Legends = 0
175
- Copenhagen_Challengers = 0
176
- Copenhagen_Contenders = 0
177
- Copenhagen_Champions_Autographs = 0
178
- Copenhagen_Challengers_Autographs = 0
179
- Copenhagen_Legends_Autographs = 0
180
- Copenhagen_Contenders_Autographs = 0
181
-
182
- [Shanghai 2024 Sticker Capsule]
183
- Shanghai_Legends = 0
184
- Shanghai_Challengers = 0
185
- Shanghai_Contenders = 0
186
- Shanghai_Champions_Autographs = 0
187
- Shanghai_Challengers_Autographs = 0
188
- Shanghai_Legends_Autographs = 0
189
- Shanghai_Contenders_Autographs = 0
190
-
191
- [Austin 2025 Sticker Capsule]
192
- Austin_Legends = 0
193
- Austin_Challengers = 0
194
- Austin_Contenders = 0
195
- Austin_Champions_Autographs = 0
196
- Austin_Challengers_Autographs = 0
197
- Austin_Legends_Autographs = 0
198
- Austin_Contenders_Autographs = 0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes