serverwatcher 3.7.1__tar.gz → 3.8.1__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-3.7.1/src/serverwatcher.egg-info → serverwatcher-3.8.1}/PKG-INFO +1 -1
  2. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/pyproject.toml +1 -1
  3. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/config/messages.py +2 -0
  4. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/defaultconfigs/messages.yaml +2 -0
  5. serverwatcher-3.8.1/src/serverwatcher/helpers/__init__.py +5 -0
  6. serverwatcher-3.8.1/src/serverwatcher/helpers/loadconfigs.py +15 -0
  7. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/watcher.py +8 -41
  8. {serverwatcher-3.7.1 → serverwatcher-3.8.1/src/serverwatcher.egg-info}/PKG-INFO +1 -1
  9. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher.egg-info/SOURCES.txt +3 -2
  10. serverwatcher-3.7.1/src/serverwatcher/schema.py +0 -70
  11. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/LICENSE +0 -0
  12. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/README.md +0 -0
  13. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/setup.cfg +0 -0
  14. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/__init__.py +0 -0
  15. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/config/__init__.py +0 -0
  16. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/config/global_config.py +0 -0
  17. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/config/watcher.py +0 -0
  18. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/defaultconfigs/global.yaml +0 -0
  19. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher/defaultconfigs/watcher.yaml +0 -0
  20. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher.egg-info/dependency_links.txt +0 -0
  21. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/src/serverwatcher.egg-info/requires.txt +0 -0
  22. {serverwatcher-3.7.1 → serverwatcher-3.8.1}/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: 3.7.1
3
+ Version: 3.8.1
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 = "3.7.1"
10
+ version = "3.8.1"
11
11
  description = "A HungerLib-powered Minecraft server automation engine."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -29,8 +29,10 @@ class MessagesConfig:
29
29
 
30
30
  # logging
31
31
  log_start: str
32
+ log_status_check: str
32
33
  log_validation_fail: str
33
34
  log_validation_ok: str
35
+ log_shutdown: str
34
36
  log_immediate_restart: str
35
37
  log_no_restart: str
36
38
  log_scheduled: str
@@ -25,8 +25,10 @@ broadcast_second:
25
25
 
26
26
  logging:
27
27
  log_start: "ServerWatcher is running!"
28
+ log_status_check: "Checking server status..."
28
29
  log_validation_fail: "Validation FAILED."
29
30
  log_validation_ok: "All validation checks succeeded."
31
+ log_shutdown: "Shutting down ServerWatcher."
30
32
  log_immediate_restart: "Restarting immediately."
31
33
  log_no_restart: "The server does not need to restart."
32
34
  log_scheduled: "Restart needed, but anti-restart factors outweigh it."
@@ -0,0 +1,5 @@
1
+ from .loadconfigs import load_or_default
2
+
3
+ __all__ = [
4
+ 'load_or_default',
5
+ ]
@@ -0,0 +1,15 @@
1
+ import os
2
+
3
+ def load_or_default(path: str, default_path: str, schema):
4
+
5
+ abs_path = os.path.join(BASE_DIR, path)
6
+ abs_default = os.path.join(PACKAGE_DIR, default_path)
7
+
8
+ if not os.path.exists(abs_path):
9
+ os.makedirs(os.path.dirname(abs_path), exist_ok=True)
10
+ with open(abs_default, "r") as src, open(abs_path, "w") as dst:
11
+ dst.write(src.read())
12
+
13
+ raw = load_yaml(abs_path)
14
+ raw = flatten_nested(raw)
15
+ return map_to_dataclass(raw, schema)
@@ -17,27 +17,12 @@ from hungerlib.addons import (
17
17
 
18
18
  from serverwatcher.schema import flatten_nested
19
19
  from serverwatcher.config import GlobalConfig, MessagesConfig, WatcherConfig
20
+ from serverwatcher.helpers import load_or_default
20
21
 
21
22
  # Set directory
22
23
  BASE_DIR = os.getcwd()
23
24
  PACKAGE_DIR = os.path.dirname(__file__)
24
25
 
25
- # Load configs
26
- def load_or_default(path: str, default_path: str, schema):
27
-
28
- abs_path = os.path.join(BASE_DIR, path)
29
- abs_default = os.path.join(PACKAGE_DIR, default_path)
30
-
31
- if not os.path.exists(abs_path):
32
- os.makedirs(os.path.dirname(abs_path), exist_ok=True)
33
- with open(abs_default, "r") as src, open(abs_path, "w") as dst:
34
- dst.write(src.read())
35
-
36
- raw = load_yaml(abs_path)
37
- raw = flatten_nested(raw)
38
- return map_to_dataclass(raw, schema)
39
-
40
-
41
26
 
42
27
  # Main watcher
43
28
  class ServerWatcher:
@@ -47,34 +32,26 @@ class ServerWatcher:
47
32
  "defaultconfigs/global.yaml",
48
33
  GlobalConfig
49
34
  )
50
-
51
35
  self.messages: MessagesConfig = load_or_default(
52
36
  "config/messages.yaml",
53
37
  "defaultconfigs/messages.yaml",
54
38
  MessagesConfig
55
39
  )
56
-
57
40
  self.cfg: WatcherConfig = load_or_default(
58
41
  "config/watcher.yaml",
59
42
  "defaultconfigs/watcher.yaml",
60
43
  WatcherConfig
61
44
  )
62
-
63
- # panel
64
45
  self.panel = Panel(
65
46
  name=self.global_cfg.panel_name,
66
47
  url=self.global_cfg.panel_url,
67
48
  api_key=self.global_cfg.panel_api_key,
68
49
  )
69
-
70
-
71
- # origin
72
50
  self.origin = GenericServer(
73
51
  name="Origin",
74
52
  panel=self.panel,
75
53
  server_id=self.global_cfg.origin_server_id
76
54
  )
77
-
78
55
  self.server = MinecraftServer(
79
56
  name=self.global_cfg.server_name,
80
57
  panel=self.panel,
@@ -85,21 +62,15 @@ class ServerWatcher:
85
62
  rcon_password=self.global_cfg.rcon_password,
86
63
  tpsCommand=self.global_cfg.tps_command,
87
64
  )
88
-
89
-
90
- # logger
91
65
  logger_name = self.cfg.logger_name_template.format(
92
66
  server_name=self.global_cfg.server_name
93
67
  )
94
-
95
68
  self.log = HungerLogger(
96
69
  name=logger_name,
97
70
  server=self.server,
98
71
  log_path=self.cfg.log_path,
99
72
  console_backspaces=self.cfg.console_backspaces,
100
73
  )
101
-
102
- # timezone
103
74
  self.tz = ZoneInfo(self.cfg.timezone)
104
75
 
105
76
  # format messages with prefix
@@ -108,7 +79,7 @@ class ServerWatcher:
108
79
 
109
80
  # simple shutdown
110
81
  def shutdown(self):
111
- self.log.info("Shutting down ServerWatcher.")
82
+ self.log.info(f"{self.messages.log_shutdown}")
112
83
  raise SystemExit
113
84
 
114
85
  # restart logic
@@ -118,7 +89,7 @@ class ServerWatcher:
118
89
  self.log.info(f"{self.messages.restart_action_sent}")
119
90
  time.sleep(self.cfg.restart_wait_seconds)
120
91
 
121
- self.log.warn("Checking server status...")
92
+ self.log.warn(f"{self.messages.log_status_check}")
122
93
  alive = waitForOnline(
123
94
  self.server,
124
95
  timeout=self.cfg.restart_online_timeout,
@@ -127,9 +98,7 @@ class ServerWatcher:
127
98
 
128
99
  if alive:
129
100
  self.log.info(f"{self.messages.server_back_online}")
130
- self.server.sendBroadcast(
131
- f"{self.messages.server_back_online_broadcast}"
132
- )
101
+ self.server.sendBroadcast(f"{self.messages.server_back_online_broadcast}")
133
102
  self.origin.enableSchedule(self.cfg.origin_disable_schedule_id)
134
103
  else:
135
104
  self.log.error(f"{self.messages.server_failed_restart}")
@@ -143,9 +112,7 @@ class ServerWatcher:
143
112
  time_str = local_time.strftime("%I:%M %p")
144
113
 
145
114
  # Broadcast the scheduled restart time
146
- self.server.sendBroadcast(
147
- self.fmt(self.messages.broadcast_restart_at, time=time_str)
148
- )
115
+ self.server.sendBroadcast(self.fmt({self.messages.broadcast_restart_at, time=time_str}))
149
116
 
150
117
  # Collect all minute_* keys
151
118
  minute_keys = [
@@ -184,10 +151,10 @@ class ServerWatcher:
184
151
 
185
152
  # main evaluation logic
186
153
  def evaluate(self):
187
- self.log.info(self.messages.log_start)
154
+ self.log.info(f"{self.messages.log_start}")
188
155
 
189
156
  if not validateAll(self.panel, self.server):
190
- self.log.error(self.messages.log_validation_fail)
157
+ self.log.error(f"{self.messages.log_validation_fail}")
191
158
  self.shutdown()
192
159
 
193
160
  self.server.refresh()
@@ -255,7 +222,7 @@ class ServerWatcher:
255
222
  gap = abs(pro - anti)
256
223
 
257
224
  if pro == 0:
258
- self.log.info(self.messages.log_no_restart)
225
+ self.log.info(f"{self.messages.log_no_restart}")
259
226
  return
260
227
 
261
228
  if pro > anti and snap.players == 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: serverwatcher
3
- Version: 3.7.1
3
+ Version: 3.8.1
4
4
  Summary: A HungerLib-powered Minecraft server automation engine.
5
5
  Author: iFamished
6
6
  License: GPL-3.0
@@ -2,7 +2,6 @@ LICENSE
2
2
  README.md
3
3
  pyproject.toml
4
4
  src/serverwatcher/__init__.py
5
- src/serverwatcher/schema.py
6
5
  src/serverwatcher/watcher.py
7
6
  src/serverwatcher.egg-info/PKG-INFO
8
7
  src/serverwatcher.egg-info/SOURCES.txt
@@ -15,4 +14,6 @@ src/serverwatcher/config/messages.py
15
14
  src/serverwatcher/config/watcher.py
16
15
  src/serverwatcher/defaultconfigs/global.yaml
17
16
  src/serverwatcher/defaultconfigs/messages.yaml
18
- src/serverwatcher/defaultconfigs/watcher.yaml
17
+ src/serverwatcher/defaultconfigs/watcher.yaml
18
+ src/serverwatcher/helpers/__init__.py
19
+ src/serverwatcher/helpers/loadconfigs.py
@@ -1,70 +0,0 @@
1
- from typing import Dict, Any, List
2
-
3
- from hungerlib.addons import validate_required_keys
4
-
5
-
6
- def validate_config_schema(raw: Dict[str, Any]) -> list[str]:
7
- errors: List[str] = []
8
-
9
- # top-level sections
10
- errors += validate_required_keys(raw, ["panel", "origin", "server", "watcher"], "root")
11
-
12
- panel = raw.get("panel", {})
13
- origin = raw.get("origin", {})
14
- server = raw.get("server", {})
15
- watcher = raw.get("watcher", {})
16
-
17
- errors += validate_required_keys(panel, ["name", "url", "api_key"], "panel")
18
- errors += validate_required_keys(origin, ["server_id"], "origin")
19
- errors += validate_required_keys(
20
- server,
21
- ["name", "server_id", "domain", "port", "rcon_port", "rcon_password", "tps_command"],
22
- "server",
23
- )
24
-
25
- # simple sanity checks (optional but nice)
26
- if "port" in server and not isinstance(server["port"], int):
27
- errors.append("[server] 'port' must be an integer")
28
- if "rcon_port" in server and not isinstance(server["rcon_port"], int):
29
- errors.append("[server] 'rcon_port' must be an integer")
30
-
31
- # watcher numeric sanity
32
- if "ram_threshold" in watcher and watcher["ram_threshold"] <= 0:
33
- errors.append("[watcher] 'ram_threshold' must be > 0")
34
- if "cpu_threshold" in watcher and watcher["cpu_threshold"] <= 0:
35
- errors.append("[watcher] 'cpu_threshold' must be > 0")
36
-
37
- return errors
38
-
39
-
40
- def validate_messages_schema(raw: Dict[str, Any]) -> list[str]:
41
- # you can make this as strict as you want; for now just ensure prefix exists
42
- errors: List[str] = []
43
- if "prefix" not in raw:
44
- errors.append("[messages] Missing required key: 'prefix'")
45
- return errors
46
-
47
- def flatten_nested(raw: dict) -> dict:
48
- """
49
- Recursively flattens a nested YAML dict into a single-level dict.
50
- Section names are ignored; only leaf keys matter.
51
- """
52
- flat = {}
53
-
54
- def walk(node):
55
- if isinstance(node, dict):
56
- for v in node.values():
57
- walk(v)
58
- else:
59
- # ignore non-dict nodes at this level
60
- pass
61
-
62
- def collect(node):
63
- for key, value in node.items():
64
- if isinstance(value, dict):
65
- collect(value)
66
- else:
67
- flat[key] = value
68
-
69
- collect(raw)
70
- return flat
File without changes
File without changes
File without changes