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