absfuyu 4.1.1__py3-none-any.whl → 5.0.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 (76) hide show
  1. absfuyu/__init__.py +4 -4
  2. absfuyu/__main__.py +13 -1
  3. absfuyu/cli/__init__.py +4 -2
  4. absfuyu/cli/color.py +7 -0
  5. absfuyu/cli/do_group.py +9 -91
  6. absfuyu/cli/tool_group.py +136 -0
  7. absfuyu/config/__init__.py +17 -34
  8. absfuyu/core/__init__.py +49 -0
  9. absfuyu/core/baseclass.py +299 -0
  10. absfuyu/core/baseclass2.py +165 -0
  11. absfuyu/core/decorator.py +67 -0
  12. absfuyu/core/docstring.py +163 -0
  13. absfuyu/core/dummy_cli.py +67 -0
  14. absfuyu/core/dummy_func.py +47 -0
  15. absfuyu/dxt/__init__.py +42 -0
  16. absfuyu/dxt/dictext.py +201 -0
  17. absfuyu/dxt/dxt_support.py +79 -0
  18. absfuyu/dxt/intext.py +586 -0
  19. absfuyu/dxt/listext.py +508 -0
  20. absfuyu/dxt/strext.py +530 -0
  21. absfuyu/{extensions → extra}/__init__.py +3 -2
  22. absfuyu/extra/beautiful.py +251 -0
  23. absfuyu/{extensions/extra → extra}/data_analysis.py +51 -82
  24. absfuyu/fun/__init__.py +110 -135
  25. absfuyu/fun/tarot.py +9 -17
  26. absfuyu/game/__init__.py +6 -0
  27. absfuyu/game/game_stat.py +6 -0
  28. absfuyu/game/sudoku.py +7 -1
  29. absfuyu/game/tictactoe.py +12 -5
  30. absfuyu/game/wordle.py +14 -8
  31. absfuyu/general/__init__.py +6 -79
  32. absfuyu/general/content.py +22 -36
  33. absfuyu/general/generator.py +17 -42
  34. absfuyu/general/human.py +108 -228
  35. absfuyu/general/shape.py +1334 -0
  36. absfuyu/logger.py +8 -13
  37. absfuyu/pkg_data/__init__.py +137 -99
  38. absfuyu/pkg_data/deprecated.py +133 -0
  39. absfuyu/pkg_data/passwordlib_lzma.pkl +0 -0
  40. absfuyu/sort.py +6 -130
  41. absfuyu/tools/__init__.py +2 -2
  42. absfuyu/tools/checksum.py +44 -22
  43. absfuyu/tools/converter.py +82 -50
  44. absfuyu/tools/keygen.py +25 -30
  45. absfuyu/tools/obfuscator.py +246 -112
  46. absfuyu/tools/passwordlib.py +330 -0
  47. absfuyu/tools/shutdownizer.py +287 -0
  48. absfuyu/tools/web.py +2 -9
  49. absfuyu/util/__init__.py +15 -15
  50. absfuyu/util/api.py +10 -15
  51. absfuyu/util/json_method.py +7 -24
  52. absfuyu/util/lunar.py +3 -9
  53. absfuyu/util/path.py +22 -27
  54. absfuyu/util/performance.py +43 -67
  55. absfuyu/util/shorten_number.py +65 -14
  56. absfuyu/util/zipped.py +9 -15
  57. absfuyu-5.0.0.dist-info/METADATA +143 -0
  58. absfuyu-5.0.0.dist-info/RECORD +68 -0
  59. absfuyu/core.py +0 -57
  60. absfuyu/everything.py +0 -32
  61. absfuyu/extensions/beautiful.py +0 -188
  62. absfuyu/extensions/dev/__init__.py +0 -244
  63. absfuyu/extensions/dev/password_hash.py +0 -80
  64. absfuyu/extensions/dev/passwordlib.py +0 -258
  65. absfuyu/extensions/dev/project_starter.py +0 -60
  66. absfuyu/extensions/dev/shutdownizer.py +0 -156
  67. absfuyu/extensions/extra/__init__.py +0 -24
  68. absfuyu/fun/WGS.py +0 -134
  69. absfuyu/general/data_extension.py +0 -1796
  70. absfuyu/tools/stats.py +0 -226
  71. absfuyu/util/pkl.py +0 -67
  72. absfuyu-4.1.1.dist-info/METADATA +0 -121
  73. absfuyu-4.1.1.dist-info/RECORD +0 -61
  74. {absfuyu-4.1.1.dist-info → absfuyu-5.0.0.dist-info}/WHEEL +0 -0
  75. {absfuyu-4.1.1.dist-info → absfuyu-5.0.0.dist-info}/entry_points.txt +0 -0
  76. {absfuyu-4.1.1.dist-info → absfuyu-5.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,330 @@
1
+ """
2
+ Absfuyu: Passwordlib
3
+ --------------------
4
+ Password library
5
+
6
+ Version: 5.0.0
7
+ Date updated: 19/02/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["PasswordGenerator", "TOTP"]
13
+
14
+
15
+ # Library
16
+ # ---------------------------------------------------------------------------
17
+ import hashlib
18
+ import os
19
+ import random
20
+ import re
21
+ from typing import ClassVar, Literal, NamedTuple
22
+ from urllib.parse import quote, urlencode
23
+
24
+ from absfuyu.core import BaseClass, deprecated, versionadded
25
+ from absfuyu.dxt import DictExt, Text
26
+ from absfuyu.general.generator import Charset, Generator
27
+ from absfuyu.logger import logger
28
+ from absfuyu.pkg_data import DataList, DataLoader
29
+ from absfuyu.util import set_min
30
+
31
+
32
+ # Function
33
+ # ---------------------------------------------------------------------------
34
+ @deprecated("5.0.0")
35
+ @versionadded("4.2.0")
36
+ def _password_check(password: str) -> bool:
37
+ """
38
+ Verify the strength of ``password``.
39
+ A password is considered strong if:
40
+
41
+ - 8 characters length or more
42
+ - 1 digit or more
43
+ - 1 symbol or more
44
+ - 1 uppercase letter or more
45
+ - 1 lowercase letter or more
46
+
47
+ :param password: Password want to be checked
48
+ :type password: str
49
+ :rtype: bool
50
+ """
51
+
52
+ # calculating the length
53
+ length_error = len(password) < 8
54
+
55
+ # searching for digits
56
+ digit_error = re.search(r"\d", password) is None
57
+
58
+ # searching for uppercase
59
+ uppercase_error = re.search(r"[A-Z]", password) is None
60
+
61
+ # searching for lowercase
62
+ lowercase_error = re.search(r"[a-z]", password) is None
63
+
64
+ # searching for symbols
65
+ symbols = re.compile(r"[ !#$%&'()*+,-./[\\\]^_`{|}~" + r'"]')
66
+ symbol_error = symbols.search(password) is None
67
+
68
+ detail = {
69
+ "length_error": length_error,
70
+ "digit_error": digit_error,
71
+ "uppercase_error": uppercase_error,
72
+ "lowercase_error": lowercase_error,
73
+ "symbol_error": symbol_error,
74
+ }
75
+ logger.debug(f"Password error summary: {detail}")
76
+
77
+ return not any(
78
+ [
79
+ length_error,
80
+ digit_error,
81
+ uppercase_error,
82
+ lowercase_error,
83
+ symbol_error,
84
+ ]
85
+ )
86
+
87
+
88
+ # Class
89
+ # ---------------------------------------------------------------------------
90
+ class PasswordHash(NamedTuple):
91
+ salt: bytes
92
+ key: bytes
93
+
94
+
95
+ @versionadded("4.2.0")
96
+ class PasswordGenerator(BaseClass):
97
+ """Password Generator"""
98
+
99
+ def __str__(self) -> str:
100
+ return f"{self.__class__.__name__}()"
101
+
102
+ @staticmethod
103
+ def password_hash(password: str) -> PasswordHash:
104
+ """
105
+ Generate hash for password
106
+ """
107
+ salt = os.urandom(32)
108
+ key = hashlib.pbkdf2_hmac(
109
+ hash_name="sha256",
110
+ password=password.encode("utf-8"),
111
+ salt=salt,
112
+ iterations=100000,
113
+ )
114
+ out = PasswordHash(salt, key)
115
+ return out
116
+
117
+ @staticmethod
118
+ def password_check(password: str) -> dict:
119
+ """Check password's characteristic"""
120
+ data = Text(password).analyze()
121
+ data = DictExt(data).apply(lambda x: True if x > 0 else False) # type: ignore
122
+ data.__setitem__("length", len(password))
123
+ return dict(data)
124
+
125
+ # Password generator
126
+ @staticmethod
127
+ def generate_password(
128
+ length: int = 8,
129
+ include_uppercase: bool = True,
130
+ include_number: bool = True,
131
+ include_special: bool = True,
132
+ ) -> str:
133
+ r"""
134
+ Generate a random password
135
+
136
+ Parameters
137
+ ----------
138
+ length : int
139
+ | Length of the password.
140
+ | Minimum value: ``8``
141
+ | (Default: ``8``)
142
+
143
+ include_uppercase : bool
144
+ Include uppercase character in the password (Default: ``True``)
145
+
146
+ include_number : bool
147
+ Include digit character in the password (Default: ``True``)
148
+
149
+ include_special : bool
150
+ Include special character in the password (Default: ``True``)
151
+
152
+ Returns
153
+ -------
154
+ str
155
+ Generated password
156
+
157
+
158
+ Example:
159
+ --------
160
+ >>> Password.generate_password()
161
+ [T&b@mq2
162
+ """
163
+ charset = Charset.LOWERCASE
164
+ check = 0
165
+
166
+ if include_uppercase:
167
+ charset += Charset.UPPERCASE
168
+ check += 1
169
+
170
+ if include_number:
171
+ charset += Charset.DIGIT
172
+ check += 1
173
+
174
+ if include_special:
175
+ charset += r"[!#$%&'()*+,-./]^_`{|}~\""
176
+ check += 1
177
+
178
+ while True:
179
+ pwd = Generator.generate_string(
180
+ charset=charset,
181
+ size=set_min(length, min_value=8), # type: ignore
182
+ times=1,
183
+ string_type_if_1=True,
184
+ )
185
+
186
+ analyze = Text(pwd).analyze() # Count each type of char
187
+
188
+ s = sum([1 for x in analyze.values() if x > 0]) # type: ignore
189
+ if s > check: # Break loop if each type of char has atleast 1
190
+ break
191
+ return pwd # type: ignore
192
+
193
+ @staticmethod
194
+ def generate_passphrase(
195
+ num_of_blocks: int = 5,
196
+ block_divider: str | None = None,
197
+ first_letter_cap: bool = True,
198
+ include_number: bool = True,
199
+ *,
200
+ custom_word_list: list[str] | None = None,
201
+ ) -> str:
202
+ """
203
+ Generate a random passphrase
204
+
205
+ Parameters
206
+ ----------
207
+ num_of_blocks : int
208
+ Number of word used (Default: ``5``)
209
+
210
+ block_divider : str
211
+ Character symbol that between each word (Default: ``"-"``)
212
+
213
+ first_letter_cap : bool
214
+ Capitalize first character of each word (Default: ``True``)
215
+
216
+ include_number : bool
217
+ Add number to the end of each word (Default: ``True``)
218
+
219
+ custom_word_list : list[str] | None
220
+ Custom word list for passphrase generation, by default uses a list of 360K+ words
221
+
222
+ Returns
223
+ -------
224
+ str
225
+ Generated passphrase
226
+
227
+
228
+ Example:
229
+ --------
230
+ >>> print(Password().generate_passphrase())
231
+ Myomectomies7-Sully4-Torpedomen7-Netful2-Begaud8
232
+ """
233
+ words: list[str] = (
234
+ DataLoader(DataList.PASSWORDLIB).load().decode().split(",")
235
+ if not custom_word_list
236
+ else custom_word_list
237
+ )
238
+
239
+ if block_divider is None:
240
+ block_divider = "-"
241
+
242
+ dat = [random.choice(words) for _ in range(num_of_blocks)]
243
+
244
+ if first_letter_cap:
245
+ dat = list(map(lambda x: x.title(), dat))
246
+
247
+ if include_number:
248
+ idx = random.choice(range(num_of_blocks))
249
+ dat[idx] += str(random.choice(range(10)))
250
+
251
+ return block_divider.join(dat)
252
+
253
+
254
+ @versionadded("5.0.0")
255
+ class TOTP(BaseClass):
256
+ """
257
+ A class to represent a Time-based One-Time Password (TOTP) generator.
258
+ """
259
+
260
+ URL_SCHEME: ClassVar[str] = "otpauth://totp/"
261
+
262
+ def __init__(
263
+ self,
264
+ secret: str,
265
+ name: str | None = None,
266
+ issuer: str | None = None,
267
+ algorithm: Literal["SHA1", "SHA256", "SHA512"] = "SHA1",
268
+ digit: int = 6,
269
+ period: int = 30,
270
+ ) -> None:
271
+ """
272
+ Initializes a TOTP instance.
273
+
274
+ Parameters
275
+ ----------
276
+ secret : str
277
+ The shared secret key used to generate the TOTP.
278
+
279
+ name : str, optional
280
+ The name associated with the TOTP. If not provided, defaults to ``"None"``.
281
+
282
+ issuer : str, optional
283
+ The issuer of the TOTP.
284
+
285
+ algorithm : Literal["SHA1", "SHA256", "SHA512"], optional
286
+ The hashing algorithm used to generate the TOTP.
287
+ Must be one of ``"SHA1"``, ``"SHA256"``, or ``"SHA512"``.
288
+ Defaults to ``"SHA1"``.
289
+
290
+ digit : int, optional
291
+ The number of digits in the generated TOTP. Must be greater than 0.
292
+ Defaults to ``6``.
293
+
294
+ period : int, optional
295
+ The time step in seconds for TOTP generation. Must be greater than 0.
296
+ Defaults to ``30``.
297
+ """
298
+ self.secret = secret.upper()
299
+ self.name = name if name else "None"
300
+ self.issuer = issuer
301
+ self.algorithm = algorithm.upper()
302
+ self.digit = max(digit, 1) # digit must be larger than 0
303
+ self.period = max(period, 1) # period must be larger than 0
304
+
305
+ def to_url(self) -> str:
306
+ """
307
+ Generates a URL for the TOTP in the otpauth format.
308
+
309
+ The URL format is as follows:
310
+ ``otpauth://totp/<name>?secret=<secret>&issuer=<issuer>&algorithm=<algorithm>&digit=<digit>&period=<period>``
311
+
312
+ Returns
313
+ -------
314
+ str
315
+ A URL representing the TOTP in otpauth format.
316
+ """
317
+ params = {
318
+ "secret": self.secret,
319
+ "issuer": self.issuer,
320
+ "algorithm": self.algorithm,
321
+ "digit": self.digit,
322
+ "period": self.period,
323
+ }
324
+ # Filter out None values from the params dictionary
325
+ filtered_params = {k: v for k, v in params.items() if v is not None}
326
+ # filtered_params = {k: v for k, v in self.__dict__.items() if v is not None}
327
+
328
+ name = quote(self.name)
329
+ tail = urlencode(filtered_params, quote_via=quote)
330
+ return f"{self.URL_SCHEME}{name}?{tail}"
@@ -0,0 +1,287 @@
1
+ """
2
+ Absfuyu: Shutdownizer
3
+ ---------------------
4
+ This shutdowns
5
+
6
+ Version: 5.0.0
7
+ Date updated: 23/02/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["ShutDownizer", "ShutdownEngine"]
13
+
14
+
15
+ # Library
16
+ # ---------------------------------------------------------------------------
17
+ import os
18
+ import subprocess
19
+ import sys
20
+ from abc import ABC, abstractmethod
21
+ from datetime import datetime, time, timedelta
22
+ from pathlib import Path
23
+ from typing import Annotated
24
+
25
+ try:
26
+ from typing import override # type: ignore
27
+ except ImportError:
28
+ from absfuyu.core.decorator import dummy_decorator as override
29
+
30
+ from absfuyu.core import BaseClass, versionadded, versionchanged
31
+ from absfuyu.logger import logger
32
+
33
+ # TODO: Schedule shutdown, random time shutdown, test
34
+
35
+
36
+ # Class
37
+ # ---------------------------------------------------------------------------
38
+ @versionadded("4.2.0")
39
+ class ShutDownizer(BaseClass):
40
+ """
41
+ ShutDownizer
42
+
43
+ Shutdown tool because why not
44
+ """
45
+
46
+ __slots__ = ("os", "engine")
47
+
48
+ def __init__(self) -> None:
49
+ self.os: str = sys.platform
50
+ logger.debug(f"Current OS: {self.os}")
51
+
52
+ if self.os in ["win32", "cygwin"]: # Windows
53
+ self.engine = ShutdownEngineWin() # type: ignore
54
+ elif self.os == "darwin": # MacOS
55
+ self.engine = ShutdownEngineMac() # type: ignore
56
+ elif self.os == "linux": # Linux
57
+ self.engine = ShutdownEngineLinux() # type: ignore
58
+ else:
59
+ raise SystemError("OS not supported")
60
+
61
+ def __str__(self) -> str:
62
+ return f"{self.__class__.__name__}({self.os})"
63
+
64
+ def shutdown(self, *args, **kwargs) -> None:
65
+ """Shutdown"""
66
+ self.engine.shutdown(*args, **kwargs)
67
+
68
+ def restart(self, *args, **kwargs) -> None:
69
+ """Restart"""
70
+ self.engine.restart(*args, **kwargs)
71
+
72
+ def cancel(self) -> None:
73
+ """Cancel"""
74
+ self.engine.cancel()
75
+
76
+
77
+ class ShutdownEngine(ABC, BaseClass):
78
+ """
79
+ Abstract shutdown class for different type of OS
80
+ """
81
+
82
+ def __str__(self) -> str:
83
+ return f"{self.__class__.__name__}()"
84
+
85
+ def _execute_cmd(self, cmd: str | list) -> None:
86
+ """Execute the cmd"""
87
+ try:
88
+ if isinstance(cmd, str):
89
+ subprocess.run(cmd.split())
90
+ elif isinstance(cmd, list):
91
+ subprocess.run(cmd)
92
+ except (FileNotFoundError, Exception) as e:
93
+ logger.error(f'"{cmd}" failed to run: {e}')
94
+ raise ValueError(f'"{cmd}" failed to run') # noqa
95
+
96
+ def _execute_multiple_cmds(self, cmds: list) -> None:
97
+ if not isinstance(cmds, list):
98
+ raise ValueError("cmds must be a <list>")
99
+ for cmd in cmds:
100
+ try:
101
+ logger.debug(f"Executing: {cmd}")
102
+ self._execute_cmd(cmd)
103
+ break
104
+ except Exception as e:
105
+ logger.error(f'"{cmd}" failed to run: {e}')
106
+
107
+ @abstractmethod
108
+ def shutdown(self, *args, **kwargs) -> None:
109
+ """Shutdown"""
110
+ pass
111
+
112
+ @abstractmethod
113
+ def restart(self, *args, **kwargs) -> None:
114
+ """Restart"""
115
+ pass
116
+
117
+ @abstractmethod
118
+ def sleep(self, *args, **kwargs) -> None:
119
+ """Sleep"""
120
+ pass
121
+
122
+ @abstractmethod
123
+ def abort(self) -> None:
124
+ """Abort/Cancel"""
125
+ pass
126
+
127
+ def cancel(self) -> None:
128
+ """Abort/Cancel"""
129
+ self.abort()
130
+
131
+ def _calculate_time(
132
+ self,
133
+ h: Annotated[int, "positive"] = 0,
134
+ m: Annotated[int, "positive"] = 0,
135
+ aggregate: bool = True,
136
+ ) -> int:
137
+ """
138
+ Calculate time for scheduled shutdown.
139
+
140
+ Parameters
141
+ ----------
142
+ h : int, optional
143
+ Hours to add (24h format), by default ``0``
144
+
145
+ m : int, optional
146
+ Minutes to add (24h format), by default ``0``
147
+
148
+ aggregate : bool, optional
149
+ This add hours and and minutes to `time.now()`, by default ``True``
150
+ - ``True`` : Add hours and minutes to current time
151
+ - ``False``: Use ``h`` and ``m`` as fixed time point to shutdown
152
+
153
+ Returns
154
+ -------
155
+ int
156
+ Seconds left until shutdown.
157
+ """
158
+ h = max(0, h) # Force >= 0
159
+ m = max(0, m)
160
+ now = datetime.now()
161
+ if aggregate:
162
+ delta = timedelta(hours=h, minutes=m)
163
+ out = delta.seconds
164
+ else:
165
+ new_time = datetime.combine(now.date(), time(hour=h, minute=m))
166
+ diff = new_time - now
167
+ out = diff.seconds
168
+ return out
169
+
170
+
171
+ class ShutdownEngineWin(ShutdownEngine):
172
+ """ShutDownizer - Windows"""
173
+
174
+ @override
175
+ @versionchanged("5.0.0", "Scheduled shutdown")
176
+ def shutdown(
177
+ self,
178
+ h: Annotated[int, "positive"] = 0,
179
+ m: Annotated[int, "positive"] = 0,
180
+ aggregate: bool = True,
181
+ ) -> None:
182
+ time_until_sd = self._calculate_time(h=h, m=m, aggregate=aggregate)
183
+ cmds = [f"shutdown -f -s -t {time_until_sd}"]
184
+ self._execute_multiple_cmds(cmds)
185
+
186
+ @override
187
+ def restart(self, *args, **kwargs) -> None:
188
+ cmds = ["shutdown -r"]
189
+ self._execute_multiple_cmds(cmds)
190
+
191
+ @override
192
+ def sleep(self, *args, **kwargs) -> None:
193
+ cmds = ["rundll32.exe powrprof.dll,SetSuspendState 0,1,0"]
194
+ self._execute_multiple_cmds(cmds)
195
+
196
+ @override
197
+ def abort(self) -> None:
198
+ cmds = ["shutdown -a"]
199
+ self._execute_multiple_cmds(cmds)
200
+
201
+ def _punish(self, *, are_you_sure_about_this: bool = False) -> None:
202
+ """Create a `batch` script that shut down computer when boot up"""
203
+ if not are_you_sure_about_this:
204
+ return None
205
+ try:
206
+ startup_folder_win = Path(os.getenv("appdata")).joinpath( # type: ignore
207
+ "Microsoft", "Windows", "Start Menu", "Programs", "Startup"
208
+ )
209
+ with open(startup_folder_win.joinpath("system.bat"), "w") as f:
210
+ f.write("shutdown -f -s -t 0")
211
+ except Exception:
212
+ logger.error("Cannot write file to startup folder")
213
+
214
+
215
+ class ShutdownEngineMac(ShutdownEngine):
216
+ """ShutDownizer - MacOS"""
217
+
218
+ @override
219
+ def shutdown(self, *args, **kwargs) -> None:
220
+ cmds = [
221
+ ["osascript", "-e", 'tell application "System Events" to shut down'],
222
+ "pmset sleepnow",
223
+ "shutdown -h now",
224
+ "sudo shutdown -h now",
225
+ ]
226
+ self._execute_multiple_cmds(cmds)
227
+
228
+ @override
229
+ def restart(self, *args, **kwargs) -> None:
230
+ cmds = [
231
+ ["osascript", "-e", 'tell application "System Events" to restart'],
232
+ "shutdown -r now",
233
+ "sudo shutdown -r now",
234
+ ]
235
+ self._execute_multiple_cmds(cmds)
236
+
237
+ @override
238
+ def sleep(self, *args, **kwargs) -> None:
239
+ cmds = [
240
+ ["osascript", "-e", 'tell application "System Events" to sleep'],
241
+ "pmset sleepnow",
242
+ "shutdown -s now",
243
+ "sudo shutdown -s now",
244
+ ]
245
+ self._execute_multiple_cmds(cmds)
246
+
247
+ @override
248
+ def abort(self) -> None:
249
+ cmds = [
250
+ ["osascript", "-e", 'tell application "System Events" to cancel shutdown'],
251
+ "killall shutdown",
252
+ "shutdown -c",
253
+ "sudo shutdown -c",
254
+ ]
255
+ self._execute_multiple_cmds(cmds)
256
+
257
+
258
+ class ShutdownEngineLinux(ShutdownEngine):
259
+ """ShutDownizer - Linux"""
260
+
261
+ @override
262
+ def shutdown(self, *args, **kwargs) -> None:
263
+ cmds = [
264
+ "gnome-session-quit --power-off",
265
+ "systemctl --user poweroff",
266
+ "sudo shutdown -h now",
267
+ ]
268
+ self._execute_multiple_cmds(cmds)
269
+
270
+ @override
271
+ def restart(self, *args, **kwargs) -> None:
272
+ cmds = [
273
+ "gnome-session-quit --reboot",
274
+ "systemctl reboot",
275
+ "sudo shutdown -r now",
276
+ ]
277
+ self._execute_multiple_cmds(cmds)
278
+
279
+ @override
280
+ def sleep(self, *args, **kwargs) -> None:
281
+ cmds = ["systemctl suspend", "sudo shutdown -s now"]
282
+ self._execute_multiple_cmds(cmds)
283
+
284
+ @override
285
+ def abort(self) -> None:
286
+ cmds = ["sudo shutdown -c"]
287
+ self._execute_multiple_cmds(cmds)
absfuyu/tools/web.py CHANGED
@@ -8,7 +8,7 @@ Date updated: 05/04/2024 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Library
11
- ###########################################################################
11
+ # ---------------------------------------------------------------------------
12
12
  import requests
13
13
  from bs4 import BeautifulSoup
14
14
 
@@ -16,7 +16,7 @@ from absfuyu.logger import logger
16
16
 
17
17
 
18
18
  # Function
19
- ###########################################################################
19
+ # ---------------------------------------------------------------------------
20
20
  def soup_link(link: str) -> BeautifulSoup:
21
21
  """
22
22
  ``BeautifulSoup`` the link
@@ -53,10 +53,3 @@ def gen_random_commit_msg() -> str:
53
53
  out = soup_link("https://whatthecommit.com/").get_text()[34:-20]
54
54
  logger.debug(out)
55
55
  return out # type: ignore
56
-
57
-
58
- # Run
59
- ###########################################################################
60
- if __name__ == "__main__":
61
- logger.setLevel(10)
62
- gen_random_commit_msg()