absfuyu 5.5.0__py3-none-any.whl → 5.6.1__py3-none-any.whl

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 absfuyu might be problematic. Click here for more details.

Files changed (75) hide show
  1. absfuyu/__init__.py +1 -1
  2. absfuyu/__main__.py +2 -2
  3. absfuyu/cli/__init__.py +2 -2
  4. absfuyu/cli/color.py +2 -2
  5. absfuyu/cli/config_group.py +2 -2
  6. absfuyu/cli/do_group.py +2 -2
  7. absfuyu/cli/game_group.py +2 -2
  8. absfuyu/cli/tool_group.py +2 -2
  9. absfuyu/config/__init__.py +2 -2
  10. absfuyu/core/__init__.py +2 -2
  11. absfuyu/core/baseclass.py +9 -3
  12. absfuyu/core/baseclass2.py +2 -2
  13. absfuyu/core/decorator.py +2 -2
  14. absfuyu/core/docstring.py +2 -2
  15. absfuyu/core/dummy_cli.py +2 -2
  16. absfuyu/core/dummy_func.py +2 -2
  17. absfuyu/dxt/__init__.py +2 -2
  18. absfuyu/dxt/dictext.py +2 -2
  19. absfuyu/dxt/dxt_support.py +2 -2
  20. absfuyu/dxt/intext.py +2 -2
  21. absfuyu/dxt/listext.py +2 -2
  22. absfuyu/dxt/strext.py +3 -3
  23. absfuyu/extra/__init__.py +2 -2
  24. absfuyu/extra/beautiful.py +2 -2
  25. absfuyu/extra/da/__init__.py +3 -3
  26. absfuyu/extra/da/dadf.py +2 -2
  27. absfuyu/extra/da/dadf_base.py +2 -2
  28. absfuyu/extra/da/df_func.py +2 -2
  29. absfuyu/extra/da/mplt.py +2 -2
  30. absfuyu/extra/data_analysis.py +2 -2
  31. absfuyu/extra/pdf.py +89 -0
  32. absfuyu/fun/__init__.py +2 -2
  33. absfuyu/fun/rubik.py +2 -2
  34. absfuyu/fun/tarot.py +2 -2
  35. absfuyu/game/__init__.py +2 -2
  36. absfuyu/game/game_stat.py +2 -2
  37. absfuyu/game/sudoku.py +2 -2
  38. absfuyu/game/tictactoe.py +2 -2
  39. absfuyu/game/wordle.py +2 -2
  40. absfuyu/general/__init__.py +2 -2
  41. absfuyu/general/content.py +2 -2
  42. absfuyu/general/human.py +2 -2
  43. absfuyu/general/shape.py +2 -2
  44. absfuyu/logger.py +2 -2
  45. absfuyu/pkg_data/__init__.py +2 -2
  46. absfuyu/pkg_data/deprecated.py +2 -2
  47. absfuyu/sort.py +2 -2
  48. absfuyu/tools/__init__.py +2 -2
  49. absfuyu/tools/checksum.py +2 -2
  50. absfuyu/tools/converter.py +2 -2
  51. absfuyu/tools/generator.py +2 -2
  52. absfuyu/tools/inspector.py +5 -7
  53. absfuyu/tools/keygen.py +2 -2
  54. absfuyu/tools/obfuscator.py +2 -2
  55. absfuyu/tools/passwordlib.py +2 -2
  56. absfuyu/tools/shutdownizer.py +2 -2
  57. absfuyu/tools/sw.py +515 -0
  58. absfuyu/tools/web.py +2 -2
  59. absfuyu/typings.py +2 -2
  60. absfuyu/util/__init__.py +14 -3
  61. absfuyu/util/api.py +2 -2
  62. absfuyu/util/json_method.py +2 -2
  63. absfuyu/util/lunar.py +2 -2
  64. absfuyu/util/path.py +38 -2
  65. absfuyu/util/performance.py +2 -2
  66. absfuyu/util/shorten_number.py +2 -2
  67. absfuyu/util/text_table.py +2 -2
  68. absfuyu/util/zipped.py +2 -2
  69. absfuyu/version.py +2 -2
  70. {absfuyu-5.5.0.dist-info → absfuyu-5.6.1.dist-info}/METADATA +6 -1
  71. absfuyu-5.6.1.dist-info/RECORD +79 -0
  72. absfuyu-5.5.0.dist-info/RECORD +0 -77
  73. {absfuyu-5.5.0.dist-info → absfuyu-5.6.1.dist-info}/WHEEL +0 -0
  74. {absfuyu-5.5.0.dist-info → absfuyu-5.6.1.dist-info}/entry_points.txt +0 -0
  75. {absfuyu-5.5.0.dist-info → absfuyu-5.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -3,8 +3,8 @@ Absfuyu: Package data
3
3
  ---------------------
4
4
  Deprecated (but might have some use)
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Library
absfuyu/sort.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Sort
3
3
  -------------
4
4
  Sort Module
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/tools/__init__.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Tools
3
3
  --------------
4
4
  Some useful tools
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/tools/checksum.py CHANGED
@@ -3,8 +3,8 @@ Absufyu: Checksum
3
3
  -----------------
4
4
  Check MD5, SHA256, ...
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absufyu: Converter
3
3
  ------------------
4
4
  Convert stuff
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
 
9
9
  Feature:
10
10
  --------
@@ -3,8 +3,8 @@ Absfuyu: Generator
3
3
  ------------------
4
4
  This generate stuff (Not python's ``generator``)
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
 
9
9
  Features:
10
10
  ---------
@@ -3,8 +3,8 @@ Absfuyu: Inspector
3
3
  ------------------
4
4
  Inspector
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -183,8 +183,6 @@ class InspectComponents(Protocol):
183
183
  def make_output(self) -> list[str]: ...
184
184
  @overload
185
185
  def make_output(self, *, width: int = ...) -> list[str]: ...
186
- @overload
187
- def make_output(self, *args, **kwargs) -> list[str]: ...
188
186
  def make_output(self, *args, **kwargs) -> list[str]: ...
189
187
 
190
188
 
@@ -634,7 +632,7 @@ class Inspector(AutoREPRMixin):
634
632
  # -----------------------------------------------------------
635
633
  # @deprecated
636
634
  def _make_output(self) -> OneColumnTableMaker:
637
- table = OneColumnTableMaker(self._linelength, style=self._style)
635
+ table = OneColumnTableMaker(self._linelength, style=self._style) # type: ignore
638
636
  body: list[str] = []
639
637
 
640
638
  # Signature
@@ -706,7 +704,7 @@ class Inspector(AutoREPRMixin):
706
704
  # self.obj_mro,
707
705
  # self.obj_member,
708
706
  # ]
709
- table = OneColumnTableMaker(self._linelength, style=self._style)
707
+ table = OneColumnTableMaker(self._linelength, style=self._style) # type: ignore
710
708
  body: list[str] = []
711
709
 
712
710
  # Signature
@@ -751,4 +749,4 @@ class Inspector(AutoREPRMixin):
751
749
 
752
750
  # Partial
753
751
  # ---------------------------------------------------------------------------
754
- inspect_all = partial(Inspector, line_length=None, include_all=True)
752
+ inspect_all = partial(Inspector, line_length=None, include_all=True) # type: ignore
absfuyu/tools/keygen.py CHANGED
@@ -5,8 +5,8 @@ Mod7 product key generator (90's)
5
5
 
6
6
  This is for educational and informative purposes only.
7
7
 
8
- Version: 5.5.0
9
- Date updated: 23/04/2025 (dd/mm/yyyy)
8
+ Version: 5.6.1
9
+ Date updated: 12/09/2025 (dd/mm/yyyy)
10
10
  """
11
11
 
12
12
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Obfuscator
3
3
  -------------------
4
4
  Obfuscate code
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Passwordlib
3
3
  --------------------
4
4
  Password library
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Shutdownizer
3
3
  ---------------------
4
4
  This shutdowns
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/tools/sw.py ADDED
@@ -0,0 +1,515 @@
1
+ """
2
+ Absufyu: Software
3
+ -----------------
4
+ Software, pyinstaller related stuff
5
+
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = [
13
+ # Function
14
+ "get_system_info",
15
+ "get_pyinstaller_exe_dir",
16
+ "get_pyinstaller_resource_path",
17
+ "HWIDgen",
18
+ "LicenseKeySystem",
19
+ "BasicSoftwareProtection",
20
+ # Support
21
+ "SystemInfo",
22
+ "LicenseKey",
23
+ ]
24
+
25
+
26
+ # Library
27
+ # ---------------------------------------------------------------------------
28
+ import base64
29
+ import hashlib
30
+ import hmac
31
+ import json
32
+ import os
33
+ import platform
34
+ import socket
35
+ import subprocess
36
+ import sys
37
+ import uuid
38
+ from datetime import datetime
39
+ from pathlib import Path
40
+ from typing import Literal, NamedTuple, TypedDict
41
+
42
+ from absfuyu.core.baseclass import BaseClass
43
+ from absfuyu.core.docstring import versionchanged
44
+ from absfuyu.dxt import Text
45
+ from absfuyu.tools.converter import Base64EncodeDecode
46
+ from absfuyu.util import stop_after_day
47
+
48
+
49
+ # System Info
50
+ # ---------------------------------------------------------------------------
51
+ class SystemInfo(NamedTuple):
52
+ """System info"""
53
+
54
+ os_type: str | Literal["Windows", "Linux", "Darwin"]
55
+ os_kernel: str
56
+ os_arch: str
57
+ system_name: str
58
+
59
+
60
+ def get_system_info() -> SystemInfo:
61
+ """
62
+ Returns the current operating system info.
63
+
64
+ The function attempts to retrieve the computer name using `platform.node()`.
65
+ If that fails or returns an empty string, it falls back to environment variables
66
+ or `socket.gethostname()` as a last resort, depending on the OS.
67
+
68
+ Returns
69
+ -------
70
+ SystemInfo
71
+ A tuple containing:
72
+ - OS name (e.g., 'Windows', 'Linux', 'Darwin')
73
+ - OS kernel (version)
74
+ - OS arch
75
+ - Computer name (hostname)
76
+ """
77
+ os_name = platform.system()
78
+
79
+ # Get computer name
80
+ try:
81
+ computer_name = platform.node()
82
+ if not computer_name:
83
+ raise ValueError("Empty name")
84
+ except ValueError:
85
+ if os_name == "Windows":
86
+ computer_name = os.environ.get("COMPUTERNAME", "Unknown")
87
+ else:
88
+ computer_name = os.environ.get("HOSTNAME", socket.gethostname())
89
+
90
+ # Return
91
+ return SystemInfo(
92
+ os_name, platform.version(), platform.machine().lower(), computer_name
93
+ )
94
+
95
+
96
+ # Pyinstaller
97
+ # ---------------------------------------------------------------------------
98
+ @versionchanged("5.6.1", "Fixed behavior")
99
+ def get_pyinstaller_exe_dir() -> Path:
100
+ """
101
+ Returns the directory where the current script or executable resides.
102
+
103
+ This function is useful for locating resources relative to the running script or
104
+ bundled executable (e.g., when using PyInstaller). It checks if the script is
105
+ running in a "frozen" state (as an executable), and returns the appropriate
106
+ directory accordingly.
107
+
108
+ Returns
109
+ -------
110
+ Path
111
+ A `pathlib.Path` object representing the directory containing the
112
+ executable or the current script file.
113
+ """
114
+ if getattr(sys, "frozen", False):
115
+ return Path(sys.executable).parent
116
+ else:
117
+ return Path(sys.argv[0]).resolve().parent
118
+
119
+
120
+ def get_pyinstaller_resource_path(relative_path: str) -> Path:
121
+ r"""
122
+ Get the absolute path to a resource file, compatible with both development
123
+ environments and PyInstaller-packaged executables.
124
+
125
+ When running from a PyInstaller bundle, this function resolves the path relative
126
+ to the temporary `_MEIPASS` folder. During normal execution, it resolves the path
127
+ relative to the current script's directory.
128
+
129
+ Parameters
130
+ ----------
131
+ relative_path : str
132
+ Relative path to the resource file or directory.
133
+
134
+ Returns
135
+ -------
136
+ Path
137
+ A `pathlib.Path` object pointing to the absolute location of the resource.
138
+
139
+
140
+ Example:
141
+ --------
142
+ >>> get_pyinstaller_resource_path("assets/logo.png")
143
+ <path>\assets\logo.png
144
+ """
145
+ if hasattr(sys, "_MEIPASS"):
146
+ # PyInstaller temp folder
147
+ base_path = Path(getattr(sys, "_MEIPASS")) # type: ignore[attr-defined]
148
+ else:
149
+ base_path = Path(__file__).resolve().parent
150
+
151
+ return base_path / relative_path
152
+
153
+
154
+ # Key System
155
+ # ---------------------------------------------------------------------------
156
+ class HWIDgen(BaseClass):
157
+ """
158
+ Generate Hardware ID (HWID)
159
+
160
+
161
+ Example:
162
+ --------
163
+ >>> HWIDgen.generate()
164
+ """
165
+
166
+ def __init__(self) -> None:
167
+ pass
168
+
169
+ @classmethod
170
+ def generate(cls) -> str:
171
+ """Generate HWID for current system"""
172
+ os_type = platform.system().lower()
173
+ if os_type == "windows":
174
+ return cls._get_windows_hwid()
175
+ elif os_type == "linux":
176
+ return cls._get_linux_hwid()
177
+ else:
178
+ return cls._get_hwid_mac()
179
+
180
+ @staticmethod
181
+ def _get_hwid_mac() -> str:
182
+ """HWID: MAC address"""
183
+ mac = uuid.getnode() # 48-bit MAC address
184
+ mac_str = ":".join(("%012X" % mac)[i : i + 2] for i in range(0, 12, 2))
185
+ hwid = hashlib.sha256(mac_str.encode()).hexdigest()
186
+ return hwid
187
+
188
+ @staticmethod
189
+ def _get_windows_hwid() -> str:
190
+ try:
191
+ # Get BIOS serial number
192
+ bios = (
193
+ subprocess.check_output("wmic bios get serialnumber", shell=True)
194
+ .decode()
195
+ .split("\n")[1]
196
+ .strip()
197
+ )
198
+
199
+ # Get Motherboard serial
200
+ board = (
201
+ subprocess.check_output("wmic baseboard get serialnumber", shell=True)
202
+ .decode()
203
+ .split("\n")[1]
204
+ .strip()
205
+ )
206
+
207
+ # Get Disk serial
208
+ disk = (
209
+ subprocess.check_output("wmic diskdrive get serialnumber", shell=True)
210
+ .decode()
211
+ .split("\n")[1]
212
+ .strip()
213
+ )
214
+
215
+ raw = bios + board + disk
216
+ hwid = hashlib.sha256(raw.encode()).hexdigest()
217
+ return hwid
218
+ except Exception as e:
219
+ return f"Error getting HWID: {e}"
220
+
221
+ @staticmethod
222
+ def _get_linux_hwid() -> str:
223
+ try:
224
+ disk_info = subprocess.check_output(
225
+ "udevadm info --query=all --name=/dev/sda", shell=True
226
+ ).decode()
227
+ serial = ""
228
+ for line in disk_info.splitlines():
229
+ if "ID_SERIAL_SHORT" in line:
230
+ serial = line.split("=")[1]
231
+ break
232
+
233
+ mac = uuid.getnode()
234
+ mac_str = ":".join(("%012X" % mac)[i : i + 2] for i in range(0, 12, 2))
235
+
236
+ raw = serial + mac_str
237
+ hwid = hashlib.sha256(raw.encode()).hexdigest()
238
+ return hwid
239
+ except Exception as e:
240
+ return f"Error getting HWID: {e}"
241
+
242
+
243
+ class LicenseKey(TypedDict):
244
+ name: str
245
+ expiry: str
246
+ signature: str
247
+
248
+
249
+ class LicenseKeySystem(BaseClass):
250
+ def __init__(self, name: str, expiry: str, secret_key: str) -> None:
251
+ """
252
+ License Key implementation
253
+
254
+ Parameters
255
+ ----------
256
+ name : str
257
+ Name of license holder
258
+
259
+ expiry : str
260
+ Expiry date (in yyyy-mm-dd format)
261
+
262
+ secret_key : str
263
+ Secret key to make license key
264
+ """
265
+ self._name = name
266
+ self._expiry = expiry
267
+ self._secret = secret_key.encode("utf-8")
268
+
269
+ # Make key
270
+ def make_key(self, *, hwid_overwrite: str | None = None) -> str:
271
+ """
272
+ Make a license key in these following steps:
273
+ 1. Generate HWID
274
+ 2. Combine name, expiry, HWID -> sign it
275
+ 3. Base64-encode the signature
276
+ 4. Store name, expiry, signature in JSON -> Base64 -> Hex
277
+ 5. Divide by 30 chars per line
278
+ 6. Wrap in BEGIN/END KEY
279
+
280
+ Parameters
281
+ ----------
282
+ hwid_overwrite : str | None, optional
283
+ Overwrite the HWID, by default None
284
+
285
+ Returns
286
+ -------
287
+ str
288
+ License key
289
+ """
290
+ # Prepare
291
+ hwid = (
292
+ HWIDgen.generate() if hwid_overwrite is None else hwid_overwrite
293
+ ) # Get HWID
294
+ msg_data = f"{self._name}|{self._expiry}|{hwid}" # Make msg
295
+ signature = hmac.new(self._secret, msg_data.encode(), hashlib.sha256).digest()
296
+ encoded_sig = base64.urlsafe_b64encode(signature).decode()
297
+
298
+ # Key format
299
+ key: LicenseKey = {
300
+ "name": self._name,
301
+ "expiry": self._expiry,
302
+ "signature": encoded_sig,
303
+ }
304
+ # This convert to .json -> Base64 -> Hex
305
+ encoded_key = Text(Base64EncodeDecode.encode(json.dumps(key))).to_hex(raw=True)
306
+ output_key = (
307
+ "BEGIN KEY".center(30, "=")
308
+ + "\n"
309
+ + "\n".join(Text(encoded_key).divide(30))
310
+ + "\n"
311
+ + "END KEY".center(30, "=")
312
+ )
313
+ return output_key
314
+
315
+ # Check key
316
+ @staticmethod
317
+ def _parse_license_key(license_key: str) -> LicenseKey:
318
+ """
319
+ Parse a formatted license key
320
+
321
+ Parameters
322
+ ----------
323
+ license_key : str
324
+ License key
325
+
326
+ Returns
327
+ -------
328
+ LicenseKey
329
+ Parsed license key
330
+ """
331
+ try:
332
+ lines = license_key.strip().split("\n")[1:-1] # Remove BEGIN/END KEY lines
333
+ raw_hex = "".join(lines)
334
+ decoded_json = Base64EncodeDecode.decode(
335
+ bytes.fromhex(raw_hex).decode("utf-8")
336
+ )
337
+ return json.loads(decoded_json)
338
+ except Exception as e:
339
+ raise ValueError("Invalid license key format") from e
340
+
341
+ @classmethod
342
+ def verify_license(
343
+ cls,
344
+ license_key: str,
345
+ secret_key: str | None = None,
346
+ hwid_overwrite: str | None = None,
347
+ ) -> bool:
348
+ """
349
+ Verify a license key
350
+
351
+ Parameters
352
+ ----------
353
+ license_key : str
354
+ License key
355
+
356
+ secret_key : str | None, optional
357
+ Secret key, by default None
358
+
359
+ hwid_overwrite : str | None, optional
360
+ HWID, by default None
361
+
362
+ Returns
363
+ -------
364
+ bool
365
+ _description_
366
+ """
367
+
368
+ # Prep
369
+ secret = "" if secret_key is None else secret_key
370
+ hwid = HWIDgen.generate() if hwid_overwrite is None else hwid_overwrite
371
+ parsed_license_key = cls._parse_license_key(license_key)
372
+
373
+ try:
374
+ msg_data = (
375
+ f"{parsed_license_key['name']}|{parsed_license_key['expiry']}|{hwid}"
376
+ )
377
+
378
+ expected_sig = hmac.new(
379
+ secret.encode("utf-8"), msg_data.encode(), hashlib.sha256
380
+ ).digest()
381
+ expected_encoded_sig = base64.urlsafe_b64encode(expected_sig).decode()
382
+
383
+ return parsed_license_key["signature"] == expected_encoded_sig
384
+ except Exception:
385
+ return False
386
+
387
+
388
+ # Software
389
+ # ---------------------------------------------------------------------------
390
+ class BasicSoftwareProtection(BaseClass):
391
+ """
392
+ Basic software protection
393
+
394
+ This check valid license before run any app. Recommended to put at start of the code
395
+
396
+ Usage:
397
+ ------
398
+ >>> t = BasicSoftwareProtection(get_pyinstaller_exe_dir())
399
+ >>> t.add_secret("Test Key")
400
+ >>> t.check_valid_license()
401
+ """
402
+
403
+ def __init__(
404
+ self,
405
+ cwd: str | Path,
406
+ name: str | None = None,
407
+ version: str | None = None,
408
+ author: str | None = None,
409
+ author_email: str | None = None,
410
+ ) -> None:
411
+ """
412
+ Basic software protection.
413
+
414
+ Parameters
415
+ ----------
416
+ cwd : str | Path
417
+ Current working directory
418
+
419
+ name : str | None, optional
420
+ Name of the software, by default None
421
+
422
+ version : str | None, optional
423
+ Version of the software, by default None
424
+
425
+ author : str | None, optional
426
+ Author of the software, by default None
427
+
428
+ author_email : str | None, optional
429
+ Author's email of the software, by default None
430
+ """
431
+ self._cwd = Path(cwd)
432
+ self._author = "" if author is None else author
433
+ self._author_email = "" if author_email is None else author_email
434
+ self._software_name = "" if name is None else name
435
+ self._software_version = "" if version is None else version
436
+ self._secret = ""
437
+
438
+ # Metadata
439
+ @property
440
+ def cwd(self) -> Path:
441
+ """Current working directory"""
442
+ return self._cwd
443
+
444
+ @property
445
+ def software_name(self) -> str:
446
+ """Name of the software"""
447
+ return self._software_name
448
+
449
+ @software_name.setter
450
+ def software_name(self, value: str) -> None:
451
+ # Logic to validate name
452
+ self._software_name = value
453
+
454
+ @property
455
+ def author(self) -> str:
456
+ """Author of the software"""
457
+ return self._author
458
+
459
+ @property
460
+ def version(self) -> str:
461
+ """Version of the software"""
462
+ return self._software_version
463
+
464
+ # Protection
465
+ def add_secret(self, secret: str) -> None:
466
+ """
467
+ Add secret
468
+
469
+ Parameters
470
+ ----------
471
+ secret : str
472
+ secret
473
+ """
474
+ self._secret = secret
475
+
476
+ def check_valid_license(self, generate_helper: bool = True) -> None:
477
+ try:
478
+ # Get license file
479
+ license_file = list(self._cwd.glob("*.zlic"))[0]
480
+ with license_file.open() as f:
481
+ # Load data
482
+ data = "".join(f.readlines())
483
+ except IndexError:
484
+ if generate_helper:
485
+ self.generate_license_helper()
486
+ raise SystemExit("License file not found!")
487
+ except ValueError:
488
+ raise SystemExit("Invalid license key format!")
489
+ else:
490
+ # Verify license
491
+ if LicenseKeySystem.verify_license(data, secret_key=self._secret):
492
+ parsed_date = datetime.strptime(
493
+ LicenseKeySystem._parse_license_key(data)["expiry"], "%Y-%m-%d"
494
+ )
495
+ stop_after_day(
496
+ parsed_date.year,
497
+ parsed_date.month,
498
+ parsed_date.day,
499
+ custom_msg="License expired!",
500
+ )
501
+ else: # Invalid license
502
+ raise SystemExit("Invalid license!")
503
+
504
+ # Make key
505
+ def _make_key(self, name: str, expiry: str, secret: str):
506
+ path = self._cwd.joinpath("license.zlic")
507
+ engine = LicenseKeySystem(name, expiry, secret)
508
+ with path.open("w", encoding="utf-8") as f:
509
+ f.write(engine.make_key())
510
+
511
+ def generate_license_helper(self):
512
+ """Gather HWID and make it into a file"""
513
+ path = self._cwd.joinpath("license.helper")
514
+ with path.open("w", encoding="utf-8") as f:
515
+ f.write(HWIDgen.generate())
absfuyu/tools/web.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Web
3
3
  ------------
4
4
  Web, ``request``, ``BeautifulSoup`` stuff
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Library
absfuyu/typings.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Pre-defined typing
5
5
 
6
- Version: 5.5.0
7
- Date updated: 23/04/2025 (dd/mm/yyyy)
6
+ Version: 5.6.1
7
+ Date updated: 12/09/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package