serverwatcher 5.23.2__tar.gz → 5.24__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.
Files changed (22) hide show
  1. {serverwatcher-5.23.2/src/serverwatcher.egg-info → serverwatcher-5.24}/PKG-INFO +1 -1
  2. {serverwatcher-5.23.2 → serverwatcher-5.24}/pyproject.toml +1 -1
  3. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/__main__.py +1 -1
  4. serverwatcher-5.24/src/serverwatcher/validator.py +161 -0
  5. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/watcher.py +15 -9
  6. {serverwatcher-5.23.2 → serverwatcher-5.24/src/serverwatcher.egg-info}/PKG-INFO +1 -1
  7. serverwatcher-5.23.2/src/serverwatcher/validator.py +0 -218
  8. {serverwatcher-5.23.2 → serverwatcher-5.24}/LICENSE +0 -0
  9. {serverwatcher-5.23.2 → serverwatcher-5.24}/README.md +0 -0
  10. {serverwatcher-5.23.2 → serverwatcher-5.24}/setup.cfg +0 -0
  11. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/__init__.py +0 -0
  12. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/configclasses/__init__.py +0 -0
  13. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/configclasses/config.py +0 -0
  14. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/configclasses/messages.py +0 -0
  15. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/configclasses/watcher.py +0 -0
  16. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/defaultconfigs/config.yaml +0 -0
  17. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/defaultconfigs/messages.yaml +0 -0
  18. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher/defaultconfigs/watcher.yaml +0 -0
  19. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher.egg-info/SOURCES.txt +0 -0
  20. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher.egg-info/dependency_links.txt +0 -0
  21. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher.egg-info/requires.txt +0 -0
  22. {serverwatcher-5.23.2 → serverwatcher-5.24}/src/serverwatcher.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serverwatcher
3
- Version: 5.23.2
3
+ Version: 5.24
4
4
  Summary: A HungerLib-powered Minecraft server automation engine.
5
5
  Author: iFamished
6
6
  License: GPL-3.0
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "serverwatcher"
10
- version = "5.23.2"
10
+ version = "5.24"
11
11
  description = "A HungerLib-powered Minecraft server automation engine."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.14"
@@ -1,5 +1,5 @@
1
1
  from .watcher import ServerWatcher
2
- from serverwatcher.validator import validate_all
2
+ from .validator import validate_all
3
3
 
4
4
  def main():
5
5
  validate_all()
@@ -0,0 +1,161 @@
1
+ import sys
2
+ from dataclasses import fields
3
+
4
+ from hungerlib import utils, loadConfig, Validator
5
+
6
+ from serverwatcher.configclasses.config import GlobalConfig
7
+ from serverwatcher.configclasses.messages import MessagesConfig
8
+ from serverwatcher.configclasses.watcher import WatcherConfig
9
+
10
+
11
+ utils.clearTerminal()
12
+ v = Validator()
13
+
14
+
15
+ def validate_global_config(config):
16
+ c = config
17
+
18
+ # timezone
19
+ v.check_field(c, "timezone")
20
+ if c.timezone == "":
21
+ v.errors.append('timezone: must not be empty')
22
+
23
+ # panel
24
+ v.check_field(c, "panel_name")
25
+ v.check_field(c, "panel_url", allow_fallback=False)
26
+ v.check_field(c, "panel_api_key", allow_fallback=False)
27
+
28
+ if c.panel_url and not (c.panel_url.startswith("http://") or c.panel_url.startswith("https://")):
29
+ v.errors.append(f'panel_url: must start with "http://" or "https://" (got "{c.panel_url}")')
30
+
31
+ if c.panel_api_key and not c.panel_api_key.startswith("ptlc_"):
32
+ v.errors.append(f'panel_api_key: must be a valid Pterodactyl client API key (got "{c.panel_api_key}")')
33
+ if c.panel_api_key and c.panel_api_key.startswith("plta_"):
34
+ v.errors.append(f'panel_api_key: should not be an application key (got "{c.panel_api_key}")')
35
+
36
+ # server
37
+ v.check_field(c, "server_name")
38
+ v.check_field(c, "server_id", allow_fallback=False)
39
+ v.check_field(c, "server_domain", allow_fallback=False)
40
+ v.check_field(c, "server_port")
41
+
42
+ if c.server_domain and (c.server_domain.startswith("http://") or c.server_domain.startswith("https://")):
43
+ v.errors.append(f'server_domain: must not contain protocol (got "{c.server_domain}")')
44
+
45
+ if c.server_port is not None and not (1 <= c.server_port <= 65535):
46
+ v.errors.append(f'server_port: must be 1–65535 (got "{c.server_port}")')
47
+
48
+ # tps_command
49
+ v.check_field(c, "tps_command")
50
+
51
+ # hungerbridge
52
+ v.check_field(c, "bridge_token", allow_fallback=False)
53
+ v.check_field(c, "bridge_port")
54
+ if c.bridge_port is not None and not (1 <= c.bridge_port <= 65535):
55
+ v.errors.append(f'bridge_port: must be 1–65535 (got "{c.bridge_port}")')
56
+
57
+ # logger
58
+ for key in [
59
+ "enable_logging", "logger_name", "log_path",
60
+ "info_prefix", "warn_prefix", "error_prefix"
61
+ ]:
62
+ v.check_field(c, key)
63
+
64
+ # terminal
65
+ v.check_field(c, "clear_terminal")
66
+ v.check_field(c, "handle_keyboard_interrupt")
67
+
68
+
69
+ def validate_watcher_config(watcherconfig):
70
+ c = watcherconfig
71
+ raw = c.raw
72
+ fb = c.fallbacks
73
+
74
+ if c.restart_wait_seconds <= 0:
75
+ v.errors.append(f'restart_wait_seconds: must be > 0 (got {c.restart_wait_seconds})')
76
+
77
+ if c.threshold_cpu <= 0:
78
+ v.errors.append(f'threshold_cpu: must be > 0 (got {c.threshold_cpu})')
79
+
80
+ if c.threshold_ram <= 0:
81
+ v.errors.append(f'threshold_ram: must be > 0 (got {c.threshold_ram})')
82
+
83
+ if c.threshold_tps <= 0 or c.threshold_tps > 20:
84
+ v.errors.append(f'threshold_tps: must be 1–20 (got "{c.threshold_tps}")')
85
+
86
+ if raw.snap_minutes is None:
87
+ v.warnings.append(f'snap_minutes: key does not exist, using fallback "{fb.snap_minutes}"')
88
+
89
+ if not isinstance(c.snap_minutes, list) or not c.snap_minutes:
90
+ v.errors.append(f'snap_minutes: must be a non-empty list of minute marks (got {c.snap_minutes!r})')
91
+ else:
92
+ bad = [m for m in c.snap_minutes if not isinstance(m, int) or not (0 <= m <= 59)]
93
+ if bad:
94
+ v.errors.append(f'snap_minutes: all values must be integers 0–59 (bad values: {bad})')
95
+
96
+
97
+ def validate_messages_config(messages):
98
+ pass
99
+ # for f in fields(MessagesConfig):
100
+ # if f.name.startswith("__"):
101
+ # continue
102
+ # value = getattr(messages, f.name)
103
+ # if not isinstance(value, str) or value.strip() == "":
104
+ # v.errors.append(f'MessagesConfig.{f.name}: must be a non-empty string')
105
+
106
+
107
+ def validate_all():
108
+ config = loadConfig(GlobalConfig)
109
+ messages = loadConfig(MessagesConfig)
110
+ watcherconfig = loadConfig(WatcherConfig)
111
+
112
+ # type checks
113
+ v.validate_key_types(config, GlobalConfig)
114
+ v.validate_key_types(messages, MessagesConfig)
115
+ v.validate_key_types(watcherconfig, WatcherConfig)
116
+
117
+ # semantic checks
118
+ validate_global_config(config)
119
+ validate_watcher_config(watcherconfig)
120
+ validate_messages_config(messages)
121
+
122
+ critical_default_keys = [
123
+ "panel_url",
124
+ "panel_api_key",
125
+ "server_id",
126
+ "server_domain",
127
+ "bridge_token",
128
+ ]
129
+ critical_defaults_used = [
130
+ d for d in v.defaults
131
+ if any(d.startswith(k) for k in critical_default_keys)
132
+ ]
133
+
134
+ if len(critical_defaults_used) >= 3:
135
+ print('❌ CONFIG VALIDATION FAILED:\nIt looks like you haven\'t configured this yet! Please change these defaults:')
136
+ for d in critical_defaults_used:
137
+ print(' -', d)
138
+ sys.exit(1)
139
+
140
+ if v.errors or v.defaults:
141
+ print('❌ CONFIG VALIDATION FAILED:')
142
+ for e in v.errors:
143
+ print(' -', e)
144
+ for d in v.defaults:
145
+ print(' -', d)
146
+ if v.warnings:
147
+ print('\nWarnings:')
148
+ for w in v.warnings:
149
+ print(' -', w)
150
+ sys.exit(1)
151
+
152
+ if v.warnings:
153
+ print('⚠️ CONFIG VALIDATION WARNINGS:')
154
+ for w in v.warnings:
155
+ print(' -', w)
156
+
157
+ print('✅ All configs are valid.')
158
+
159
+
160
+ if __name__ == '__main__':
161
+ validate_all()
@@ -52,39 +52,44 @@ class ServerWatcher:
52
52
 
53
53
  origin_maps=[
54
54
  maps.ascii_colors,
55
- maps.time_tk(self.config.timezone),
55
+ maps.time(self.config.timezone),
56
56
  self.config,
57
57
  self.messages,
58
- self.watcherconfig
58
+ self.watcherconfig,
59
+ maps.math,
59
60
  ],
60
61
 
61
62
  destination_maps=[
62
63
  maps.ascii_colors,
63
- maps.time_tk(self.config.timezone),
64
+ maps.time(self.config.timezone),
64
65
  self.config,
65
66
  self.messages,
66
- self.watcherconfig
67
+ self.watcherconfig,
68
+ maps.math,
67
69
  ],
68
70
 
69
71
  broadcast_maps=[
70
72
  maps.mc_colors,
71
- maps.time_tk(self.config.timezone),
73
+ maps.time(self.config.timezone),
72
74
  self.config,
73
75
  self.messages,
74
- self.watcherconfig
76
+ self.watcherconfig,
77
+ maps.math,
75
78
  ],
76
79
 
77
80
  file_maps=[
78
81
  maps.strip_colors,
79
- maps.time_tk(self.config.timezone),
82
+ maps.time(self.config.timezone),
80
83
  self.config,
81
84
  self.messages,
82
- self.watcherconfig
85
+ self.watcherconfig,
86
+ maps.math,
83
87
  ],
84
88
 
85
89
  prefix_maps=[
86
90
  maps.ascii_colors,
87
- maps.time_tk(self.config.timezone)
91
+ maps.time(self.config.timezone),
92
+ maps.math,
88
93
  ],
89
94
 
90
95
  info_prefix=self.config.info_prefix,
@@ -223,6 +228,7 @@ class ServerWatcher:
223
228
 
224
229
  if pro == 0:
225
230
  self.router.info(self.messages.no_restart)
231
+ # will add something here later
226
232
  return
227
233
 
228
234
  if pro > anti and snap.players == 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serverwatcher
3
- Version: 5.23.2
3
+ Version: 5.24
4
4
  Summary: A HungerLib-powered Minecraft server automation engine.
5
5
  Author: iFamished
6
6
  License: GPL-3.0
@@ -1,218 +0,0 @@
1
- import sys
2
- from dataclasses import fields
3
-
4
- from hungerlib import utils, loadConfig
5
-
6
- from serverwatcher.configclasses.config import GlobalConfig
7
- from serverwatcher.configclasses.messages import MessagesConfig
8
- from serverwatcher.configclasses.watcher import WatcherConfig
9
-
10
-
11
- utils.clearTerminal()
12
- errors = []
13
- warnings = []
14
- defaults = []
15
-
16
-
17
- def validate_key_types(config_obj, schema):
18
- for f in fields(schema):
19
- if f.name.startswith("__"):
20
- continue
21
-
22
- expected_type = f.type
23
- value = getattr(config_obj, f.name, None)
24
-
25
- # allow None (missing + no fallback) to be handled by other validators
26
- if value is None:
27
- continue
28
-
29
- if not isinstance(value, expected_type):
30
- errors.append(
31
- f'{schema.__name__}.{f.name}: expected {expected_type.__name__}, '
32
- f'got "{type(value).__name__}" ({value!r})'
33
- )
34
-
35
-
36
- def check_field(config_obj, name, allow_fallback=True):
37
- """
38
- Unified check for:
39
- - missing YAML key (config.raw.<name> is None)
40
- - fallback usage (config.<name> == config.fallbacks.<name>)
41
- - whether fallback is allowed or not
42
- """
43
- raw = config_obj.raw
44
- fb = config_obj.fallbacks
45
-
46
- val = getattr(config_obj, name)
47
- raw_val = getattr(raw, name)
48
- fb_val = getattr(fb, name)
49
-
50
- # 1) YAML key missing
51
- if raw_val is None:
52
- if allow_fallback:
53
- warnings.append(f'{name}: key does not exist, using fallback "{fb_val}"')
54
- else:
55
- errors.append(f'{name}: key does not exist and fallback is not allowed')
56
- return
57
-
58
- # 2) YAML key exists but value equals fallback
59
- if val == fb_val and not allow_fallback:
60
- defaults.append(f'{name}: must not be left default or empty (got "{val}")')
61
-
62
-
63
- def validate_global_config(config):
64
- c = config
65
-
66
- # Fallback policy:
67
- # - NOT allowed (must be set by user): panel_url, panel_api_key,
68
- # server_id, server_domain, bridge_token
69
- # - Allowed: everything else
70
-
71
- # timezone
72
- check_field(c, "timezone")
73
- if c.timezone == "":
74
- errors.append('timezone: must not be empty')
75
-
76
- # panel
77
- check_field(c, "panel_name")
78
- check_field(c, "panel_url", allow_fallback=False)
79
- check_field(c, "panel_api_key", allow_fallback=False)
80
-
81
- if c.panel_url and not (c.panel_url.startswith("http://") or c.panel_url.startswith("https://")):
82
- errors.append(f'panel_url: must start with "http://" or "https://" (got "{c.panel_url}")')
83
-
84
- if c.panel_api_key and not c.panel_api_key.startswith("ptlc_"):
85
- errors.append(f'panel_api_key: must be a valid Pterodactyl client API key (got "{c.panel_api_key}")')
86
- if c.panel_api_key and c.panel_api_key.startswith("plta_"):
87
- errors.append(f'panel_api_key: should not be an application key (got "{c.panel_api_key}")')
88
-
89
- # server
90
- check_field(c, "server_name")
91
- check_field(c, "server_id", allow_fallback=False)
92
- check_field(c, "server_domain", allow_fallback=False)
93
- check_field(c, "server_port")
94
- if c.server_domain and (c.server_domain.startswith("http://") or c.server_domain.startswith("https://")):
95
- errors.append(f'server_domain: must not contain protocol (got "{c.server_domain}")')
96
- if c.server_port is not None and not (1 <= c.server_port <= 65535):
97
- errors.append(f'server_port: must be 1–65535 (got "{c.server_port}")')
98
-
99
- # tps_command
100
- check_field(c, "tps_command")
101
-
102
- # hungerbridge
103
- check_field(c, "bridge_token", allow_fallback=False)
104
- check_field(c, "bridge_port")
105
- if c.bridge_port is not None and not (1 <= c.bridge_port <= 65535):
106
- errors.append(f'bridge_port: must be 1–65535 (got "{c.bridge_port}")')
107
-
108
- # logger
109
- check_field(c, "enable_logging")
110
- check_field(c, "logger_name")
111
- check_field(c, "log_path")
112
- check_field(c, "info_prefix")
113
- check_field(c, "warn_prefix")
114
- check_field(c, "error_prefix")
115
-
116
- # terminal
117
- check_field(c, "clear_terminal")
118
- check_field(c, "handle_keyboard_interrupt")
119
-
120
-
121
- def validate_watcher_config(watcherconfig):
122
- c = watcherconfig
123
- raw = c.raw
124
- fb = c.fallbacks
125
-
126
- # basic numeric sanity
127
- if c.restart_wait_seconds <= 0:
128
- errors.append(f'restart_wait_seconds: must be > 0 (got {c.restart_wait_seconds})')
129
-
130
- if c.threshold_cpu <= 0:
131
- errors.append(f'threshold_cpu: must be > 0 (got {c.threshold_cpu})')
132
-
133
- if c.threshold_ram <= 0:
134
- errors.append(f'threshold_ram: must be > 0 (got {c.threshold_ram})')
135
-
136
- if c.threshold_tps <= 0 or c.threshold_tps > 20:
137
- errors.append(f'threshold_tps: must be 1–20 (got {c.threshold_tps})')
138
-
139
- # snap_minutes: must be a non-empty list of ints 0–59
140
- if raw.snap_minutes is None:
141
- warnings.append(f'snap_minutes: key does not exist, using fallback "{fb.snap_minutes}"')
142
-
143
- if not isinstance(c.snap_minutes, list) or not c.snap_minutes:
144
- errors.append(f'snap_minutes: must be a non-empty list of minute marks (got {c.snap_minutes!r})')
145
- else:
146
- bad = [m for m in c.snap_minutes if not isinstance(m, int) or not (0 <= m <= 59)]
147
- if bad:
148
- errors.append(f'snap_minutes: all values must be integers 0–59 (bad values: {bad})')
149
-
150
-
151
- def validate_messages_config(messages):
152
- pass
153
- # m = messages
154
-
155
- # for f in fields(MessagesConfig):
156
- # if f.name.startswith("__"):
157
- # continue
158
- # value = getattr(m, f.name)
159
- # if not isinstance(value, str) or value.strip() == "":
160
- # errors.append(f'MessagesConfig.{f.name}: must be a non-empty string')
161
-
162
-
163
- def validate_all():
164
- config = loadConfig(GlobalConfig)
165
- messages = loadConfig(MessagesConfig)
166
- watcherconfig = loadConfig(WatcherConfig)
167
-
168
- # type checks
169
- validate_key_types(config, GlobalConfig)
170
- validate_key_types(messages, MessagesConfig)
171
- validate_key_types(watcherconfig, WatcherConfig)
172
-
173
- # semantic checks
174
- validate_global_config(config)
175
- validate_watcher_config(watcherconfig)
176
- validate_messages_config(messages)
177
-
178
- # if too many critical defaults, assume "not configured at all"
179
- critical_default_keys = [
180
- "panel_url",
181
- "panel_api_key",
182
- "server_id",
183
- "server_domain",
184
- "bridge_token",
185
- ]
186
- critical_defaults_used = [
187
- d for d in defaults
188
- if any(d.startswith(k) for k in critical_default_keys)
189
- ]
190
-
191
- if len(critical_defaults_used) >= 3:
192
- print('❌ CONFIG VALIDATION FAILED:\nIt looks like you haven\'t configured this yet! Please change these defaults:')
193
- for d in critical_defaults_used:
194
- print(' -', d)
195
- sys.exit(1)
196
-
197
- if errors or defaults:
198
- print('❌ CONFIG VALIDATION FAILED:')
199
- for e in errors:
200
- print(' -', e)
201
- for d in defaults:
202
- print(' -', d)
203
- if warnings:
204
- print('\nWarnings:')
205
- for w in warnings:
206
- print(' -', w)
207
- sys.exit(1)
208
-
209
- if warnings:
210
- print('⚠️ CONFIG VALIDATION WARNINGS:')
211
- for w in warnings:
212
- print(' -', w)
213
-
214
- print('✅ All configs are valid.')
215
-
216
-
217
- if __name__ == '__main__':
218
- validate_all()
File without changes
File without changes
File without changes