absfuyu 4.1.1__py3-none-any.whl → 4.2.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.
- absfuyu/__init__.py +1 -1
- absfuyu/cli/__init__.py +4 -2
- absfuyu/cli/do_group.py +14 -61
- absfuyu/cli/tool_group.py +136 -0
- absfuyu/core.py +3 -3
- absfuyu/extensions/__init__.py +1 -0
- absfuyu/pkg_data/__init__.py +2 -1
- absfuyu/pkg_data/passwordlib_lzma.pkl +0 -0
- absfuyu/tools/checksum.py +15 -4
- absfuyu/tools/converter.py +36 -7
- absfuyu/{extensions/dev → tools}/passwordlib.py +52 -50
- absfuyu/tools/shutdownizer.py +266 -0
- absfuyu/util/shorten_number.py +1 -1
- {absfuyu-4.1.1.dist-info → absfuyu-4.2.0.dist-info}/METADATA +13 -18
- {absfuyu-4.1.1.dist-info → absfuyu-4.2.0.dist-info}/RECORD +19 -21
- absfuyu/extensions/dev/__init__.py +0 -244
- absfuyu/extensions/dev/password_hash.py +0 -80
- absfuyu/extensions/dev/project_starter.py +0 -60
- absfuyu/extensions/dev/shutdownizer.py +0 -156
- absfuyu/extensions/extra/__init__.py +0 -24
- /absfuyu/extensions/{extra/data_analysis.py → data_analysis.py} +0 -0
- {absfuyu-4.1.1.dist-info → absfuyu-4.2.0.dist-info}/WHEEL +0 -0
- {absfuyu-4.1.1.dist-info → absfuyu-4.2.0.dist-info}/entry_points.txt +0 -0
- {absfuyu-4.1.1.dist-info → absfuyu-4.2.0.dist-info}/licenses/LICENSE +0 -0
absfuyu/__init__.py
CHANGED
absfuyu/cli/__init__.py
CHANGED
|
@@ -3,8 +3,8 @@ ABSFUYU
|
|
|
3
3
|
-------
|
|
4
4
|
COMMAND LINE INTERFACE
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.1.0
|
|
7
|
+
Date updated: 09/02/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
__all__ = ["cli"]
|
|
@@ -19,6 +19,7 @@ from absfuyu.cli.color import COLOR
|
|
|
19
19
|
from absfuyu.cli.config_group import config_group
|
|
20
20
|
from absfuyu.cli.do_group import do_group
|
|
21
21
|
from absfuyu.cli.game_group import game_group
|
|
22
|
+
from absfuyu.cli.tool_group import tool_group
|
|
22
23
|
|
|
23
24
|
# Color stuff
|
|
24
25
|
colorama.init(autoreset=True)
|
|
@@ -48,4 +49,5 @@ def cli() -> None:
|
|
|
48
49
|
cli.add_command(config_group)
|
|
49
50
|
cli.add_command(do_group)
|
|
50
51
|
cli.add_command(game_group)
|
|
52
|
+
cli.add_command(tool_group)
|
|
51
53
|
cli.add_command(version)
|
absfuyu/cli/do_group.py
CHANGED
|
@@ -3,14 +3,13 @@ ABSFUYU CLI
|
|
|
3
3
|
-----------
|
|
4
4
|
Do
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.4.0
|
|
7
|
+
Date updated: 07/02/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
__all__ = ["do_group"]
|
|
11
11
|
|
|
12
12
|
import subprocess
|
|
13
|
-
from typing import Literal
|
|
14
13
|
|
|
15
14
|
import click
|
|
16
15
|
|
|
@@ -18,7 +17,7 @@ from absfuyu import __title__
|
|
|
18
17
|
from absfuyu.cli.color import COLOR
|
|
19
18
|
from absfuyu.core import __package_feature__
|
|
20
19
|
from absfuyu.general.human import Human2
|
|
21
|
-
from absfuyu.tools.
|
|
20
|
+
from absfuyu.tools.shutdownizer import ShutDownizer
|
|
22
21
|
from absfuyu.util.zipped import Zipper
|
|
23
22
|
from absfuyu.version import PkgVersion
|
|
24
23
|
|
|
@@ -82,7 +81,11 @@ def fs(date: str, number_string: str) -> None:
|
|
|
82
81
|
@click.command(name="info")
|
|
83
82
|
@click.argument("date", type=str)
|
|
84
83
|
def info(date: str) -> None:
|
|
85
|
-
"""
|
|
84
|
+
"""
|
|
85
|
+
Day info, format: yyyymmdd
|
|
86
|
+
|
|
87
|
+
Remake this
|
|
88
|
+
"""
|
|
86
89
|
|
|
87
90
|
instance = Human2(date)
|
|
88
91
|
print(instance.info())
|
|
@@ -98,61 +101,11 @@ def unzip_files_in_dir(dir: str) -> None:
|
|
|
98
101
|
print("Done")
|
|
99
102
|
|
|
100
103
|
|
|
101
|
-
@click.command(name="
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"hash_mode",
|
|
107
|
-
type=click.Choice(["md5", "sha1", "sha256", "sha512"]),
|
|
108
|
-
default="sha256",
|
|
109
|
-
show_default=True,
|
|
110
|
-
help="Hash mode",
|
|
111
|
-
)
|
|
112
|
-
@click.option(
|
|
113
|
-
"--save-result",
|
|
114
|
-
"-s",
|
|
115
|
-
"save_result",
|
|
116
|
-
type=bool,
|
|
117
|
-
default=False,
|
|
118
|
-
is_flag=True,
|
|
119
|
-
show_default=True,
|
|
120
|
-
help="Save checksum result to file",
|
|
121
|
-
)
|
|
122
|
-
@click.option(
|
|
123
|
-
"--recursive",
|
|
124
|
-
"-r",
|
|
125
|
-
"recursive_mode",
|
|
126
|
-
type=bool,
|
|
127
|
-
default=False,
|
|
128
|
-
is_flag=True,
|
|
129
|
-
show_default=True,
|
|
130
|
-
help="Do checksum for every file in the folder (including child folder)",
|
|
131
|
-
)
|
|
132
|
-
@click.option(
|
|
133
|
-
"--compare",
|
|
134
|
-
"-c",
|
|
135
|
-
"hash_to_compare",
|
|
136
|
-
type=str,
|
|
137
|
-
default=None,
|
|
138
|
-
show_default=True,
|
|
139
|
-
help="Hash to compare",
|
|
140
|
-
)
|
|
141
|
-
def file_checksum(
|
|
142
|
-
file_path: str,
|
|
143
|
-
hash_mode: str | Literal["md5", "sha1", "sha256", "sha512"],
|
|
144
|
-
save_result: bool,
|
|
145
|
-
recursive_mode: bool,
|
|
146
|
-
hash_to_compare: str,
|
|
147
|
-
) -> None:
|
|
148
|
-
"""Checksum for file"""
|
|
149
|
-
# print(hash_mode, save_result, recursive_mode)
|
|
150
|
-
instance = Checksum(file_path, hash_mode=hash_mode, save_result_to_file=save_result)
|
|
151
|
-
res = instance.checksum(recursive=recursive_mode)
|
|
152
|
-
if hash_to_compare:
|
|
153
|
-
print(res == hash_to_compare)
|
|
154
|
-
else:
|
|
155
|
-
print(res)
|
|
104
|
+
@click.command(name="shutdown")
|
|
105
|
+
def os_shutdown() -> None:
|
|
106
|
+
"""Shutdown"""
|
|
107
|
+
engine = ShutDownizer()
|
|
108
|
+
engine.shutdown()
|
|
156
109
|
|
|
157
110
|
|
|
158
111
|
@click.group(name="do")
|
|
@@ -167,4 +120,4 @@ do_group.add_command(advice)
|
|
|
167
120
|
do_group.add_command(fs)
|
|
168
121
|
do_group.add_command(info)
|
|
169
122
|
do_group.add_command(unzip_files_in_dir)
|
|
170
|
-
do_group.add_command(
|
|
123
|
+
do_group.add_command(os_shutdown)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ABSFUYU CLI
|
|
3
|
+
-----------
|
|
4
|
+
Tool
|
|
5
|
+
|
|
6
|
+
Version: 1.0.0
|
|
7
|
+
Date updated: 10/02/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__all__ = ["tool_group"]
|
|
11
|
+
|
|
12
|
+
from typing import Literal
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
|
|
16
|
+
from absfuyu.tools.checksum import Checksum
|
|
17
|
+
from absfuyu.tools.converter import Base64EncodeDecode, Text2Chemistry
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command(name="checksum")
|
|
21
|
+
@click.argument("file_path", type=str)
|
|
22
|
+
@click.option(
|
|
23
|
+
"--hashmode",
|
|
24
|
+
"-m",
|
|
25
|
+
"hash_mode",
|
|
26
|
+
type=click.Choice(["md5", "sha1", "sha256", "sha512"]),
|
|
27
|
+
default="sha256",
|
|
28
|
+
show_default=True,
|
|
29
|
+
help="Hash mode",
|
|
30
|
+
)
|
|
31
|
+
@click.option(
|
|
32
|
+
"--save-result",
|
|
33
|
+
"-s",
|
|
34
|
+
"save_result",
|
|
35
|
+
type=bool,
|
|
36
|
+
default=False,
|
|
37
|
+
is_flag=True,
|
|
38
|
+
show_default=True,
|
|
39
|
+
help="Save checksum result to file",
|
|
40
|
+
)
|
|
41
|
+
@click.option(
|
|
42
|
+
"--recursive",
|
|
43
|
+
"-r",
|
|
44
|
+
"recursive_mode",
|
|
45
|
+
type=bool,
|
|
46
|
+
default=False,
|
|
47
|
+
is_flag=True,
|
|
48
|
+
show_default=True,
|
|
49
|
+
help="Do checksum for every file in the folder (including child folder)",
|
|
50
|
+
)
|
|
51
|
+
@click.option(
|
|
52
|
+
"--compare",
|
|
53
|
+
"-c",
|
|
54
|
+
"hash_to_compare",
|
|
55
|
+
type=str,
|
|
56
|
+
default=None,
|
|
57
|
+
show_default=True,
|
|
58
|
+
help="Hash to compare",
|
|
59
|
+
)
|
|
60
|
+
def file_checksum(
|
|
61
|
+
file_path: str,
|
|
62
|
+
hash_mode: str | Literal["md5", "sha1", "sha256", "sha512"],
|
|
63
|
+
save_result: bool,
|
|
64
|
+
recursive_mode: bool,
|
|
65
|
+
hash_to_compare: str,
|
|
66
|
+
) -> None:
|
|
67
|
+
"""Checksum for file/directory"""
|
|
68
|
+
# print(hash_mode, save_result, recursive_mode)
|
|
69
|
+
instance = Checksum(file_path, hash_mode=hash_mode, save_result_to_file=save_result)
|
|
70
|
+
res = instance.checksum(recursive=recursive_mode)
|
|
71
|
+
if hash_to_compare:
|
|
72
|
+
print(res == hash_to_compare)
|
|
73
|
+
else:
|
|
74
|
+
print(res)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@click.command(name="t2c")
|
|
78
|
+
@click.argument("text", type=str)
|
|
79
|
+
def text2chem(text: str) -> None:
|
|
80
|
+
"""Convert text into chemistry symbol"""
|
|
81
|
+
engine = Text2Chemistry()
|
|
82
|
+
out = engine.convert(text)
|
|
83
|
+
print(Text2Chemistry.beautify_result(out))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@click.command(name="e")
|
|
87
|
+
@click.argument("text", type=str)
|
|
88
|
+
def base64encode(text: str) -> None:
|
|
89
|
+
"""Convert text to base64"""
|
|
90
|
+
print(Base64EncodeDecode.encode(text))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@click.command(name="d")
|
|
94
|
+
@click.argument("text", type=str)
|
|
95
|
+
def base64decode(text: str) -> None:
|
|
96
|
+
"""Convert base64 to text"""
|
|
97
|
+
print(Base64EncodeDecode.decode(text))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@click.command(name="img")
|
|
101
|
+
@click.option(
|
|
102
|
+
"--data-tag",
|
|
103
|
+
"-d",
|
|
104
|
+
"data_tag",
|
|
105
|
+
type=bool,
|
|
106
|
+
default=False,
|
|
107
|
+
is_flag=True,
|
|
108
|
+
show_default=True,
|
|
109
|
+
help="Add data tag before base64 string",
|
|
110
|
+
)
|
|
111
|
+
@click.argument("img_path", type=str)
|
|
112
|
+
def base64convert_img(img_path: str, data_tag: bool) -> None:
|
|
113
|
+
"""Convert img to base64"""
|
|
114
|
+
print(Base64EncodeDecode.encode_image(img_path, data_tag=data_tag))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@click.group(name="b64")
|
|
118
|
+
def base64_group():
|
|
119
|
+
"""Base64 encode decode"""
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
base64_group.add_command(base64encode)
|
|
124
|
+
base64_group.add_command(base64decode)
|
|
125
|
+
base64_group.add_command(base64convert_img)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@click.group(name="tool")
|
|
129
|
+
def tool_group() -> None:
|
|
130
|
+
"""Tools"""
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
tool_group.add_command(file_checksum)
|
|
135
|
+
tool_group.add_command(base64_group)
|
|
136
|
+
tool_group.add_command(text2chem)
|
absfuyu/core.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Core
|
|
|
3
3
|
-------------
|
|
4
4
|
Contain type hints and other stuffs
|
|
5
5
|
|
|
6
|
-
Version: 2.3.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 2.3.2
|
|
7
|
+
Date updated: 11/02/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module Package
|
|
@@ -18,7 +18,7 @@ __all__ = [
|
|
|
18
18
|
"DATA_PATH",
|
|
19
19
|
]
|
|
20
20
|
|
|
21
|
-
__package_feature__ = ["beautiful", "extra", "
|
|
21
|
+
__package_feature__ = ["beautiful", "extra", "full", "dev"]
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
# Library
|
absfuyu/extensions/__init__.py
CHANGED
absfuyu/pkg_data/__init__.py
CHANGED
|
@@ -26,9 +26,9 @@ from absfuyu.logger import logger
|
|
|
26
26
|
###########################################################################
|
|
27
27
|
_EXTERNAL_DATA = {
|
|
28
28
|
"chemistry.json": "https://raw.githubusercontent.com/Bowserinator/Periodic-Table-JSON/master/PeriodicTableJSON.json",
|
|
29
|
-
"countries.json": "https://github.com/dr5hn/countries-states-cities-database/blob/master/countries.json",
|
|
30
29
|
"tarot.json": "https://raw.githubusercontent.com/dariusk/corpora/master/data/divination/tarot_interpretations.json",
|
|
31
30
|
"word_list.json": "https://raw.githubusercontent.com/dwyl/english-words/master/words_dictionary.json",
|
|
31
|
+
# "countries.json": "https://raw.githubusercontent.com/dr5hn/countries-states-cities-database/refs/heads/master/json/countries.json",
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
|
|
@@ -37,6 +37,7 @@ _EXTERNAL_DATA = {
|
|
|
37
37
|
class DataList:
|
|
38
38
|
CHEMISTRY = files("absfuyu.pkg_data").joinpath("chemistry.pkl")
|
|
39
39
|
TAROT = files("absfuyu.pkg_data").joinpath("tarot.pkl")
|
|
40
|
+
PASSWORDLIB = files("absfuyu.pkg_data").joinpath("passwordlib_lzma.pkl")
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
class PkgData:
|
|
Binary file
|
absfuyu/tools/checksum.py
CHANGED
|
@@ -3,8 +3,8 @@ Absufyu: Checksum
|
|
|
3
3
|
-----------------
|
|
4
4
|
Check MD5, SHA256, ...
|
|
5
5
|
|
|
6
|
-
Version: 1.1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.1.1
|
|
7
|
+
Date updated: 05/02/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
@@ -58,6 +58,17 @@ class Checksum:
|
|
|
58
58
|
hash_mode: str | Literal["md5", "sha1", "sha256", "sha512"] = "sha256",
|
|
59
59
|
save_result_to_file: bool = False,
|
|
60
60
|
) -> None:
|
|
61
|
+
"""Checksum engine
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
path : str | Path
|
|
66
|
+
Path to file/directory to perform checksum
|
|
67
|
+
hash_mode : str | Literal["md5", "sha1", "sha256", "sha512"], optional
|
|
68
|
+
Hash mode, by default "sha256"
|
|
69
|
+
save_result_to_file : bool, optional
|
|
70
|
+
Save checksum result(s) to file, by default False
|
|
71
|
+
"""
|
|
61
72
|
self.path = Path(path)
|
|
62
73
|
self.hash_mode = hash_mode
|
|
63
74
|
self.save_result_to_file = save_result_to_file
|
|
@@ -109,7 +120,7 @@ class Checksum:
|
|
|
109
120
|
str
|
|
110
121
|
Checksum hash
|
|
111
122
|
"""
|
|
112
|
-
if self.path.absolute().is_dir():
|
|
123
|
+
if self.path.absolute().is_dir(): # Dir
|
|
113
124
|
new_path = self.path.joinpath(self.checksum_result_file_name)
|
|
114
125
|
# List of files
|
|
115
126
|
if recursive:
|
|
@@ -125,7 +136,7 @@ class Checksum:
|
|
|
125
136
|
name = x.relative_to(self.path)
|
|
126
137
|
res.append(f"{self._checksum_operation(x)} | {name}")
|
|
127
138
|
output = "\n".join(res)
|
|
128
|
-
else:
|
|
139
|
+
else: # File
|
|
129
140
|
new_path = self.path.with_name(self.checksum_result_file_name)
|
|
130
141
|
output = self._checksum_operation(self.path)
|
|
131
142
|
|
absfuyu/tools/converter.py
CHANGED
|
@@ -3,8 +3,8 @@ Absufyu: Converter
|
|
|
3
3
|
------------------
|
|
4
4
|
Convert stuff
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.4.0
|
|
7
|
+
Date updated: 10/02/2025 (dd/mm/yyyy)
|
|
8
8
|
|
|
9
9
|
Feature:
|
|
10
10
|
--------
|
|
@@ -26,6 +26,7 @@ import re
|
|
|
26
26
|
import string
|
|
27
27
|
from itertools import chain, combinations
|
|
28
28
|
from pathlib import Path
|
|
29
|
+
from typing import Self
|
|
29
30
|
|
|
30
31
|
from absfuyu.core import CLITextColor
|
|
31
32
|
from absfuyu.logger import logger
|
|
@@ -58,7 +59,7 @@ class Base64EncodeDecode:
|
|
|
58
59
|
img_path : Path | str
|
|
59
60
|
Path to image
|
|
60
61
|
data_tag : bool, optional
|
|
61
|
-
Add data tag before base64 string, by default False
|
|
62
|
+
Add data tag before base64 string, by default ``False``
|
|
62
63
|
|
|
63
64
|
Returns
|
|
64
65
|
-------
|
|
@@ -76,8 +77,6 @@ class Base64EncodeDecode:
|
|
|
76
77
|
class ChemistryElement:
|
|
77
78
|
"""Chemistry Element"""
|
|
78
79
|
|
|
79
|
-
_VERSION = (1, 1, 0)
|
|
80
|
-
|
|
81
80
|
def __init__(self, name: str, number: int, symbol: str, atomic_mass: float) -> None:
|
|
82
81
|
"""
|
|
83
82
|
name: element name
|
|
@@ -111,7 +110,7 @@ class ChemistryElement:
|
|
|
111
110
|
}
|
|
112
111
|
|
|
113
112
|
@classmethod
|
|
114
|
-
def from_dict(cls, data: dict[str, str | int | float]) ->
|
|
113
|
+
def from_dict(cls, data: dict[str, str | int | float]) -> Self:
|
|
115
114
|
"""
|
|
116
115
|
Convert from ``dict`` data
|
|
117
116
|
|
|
@@ -159,7 +158,7 @@ class Text2Chemistry:
|
|
|
159
158
|
# logger.debug(available)
|
|
160
159
|
return base.difference(available)
|
|
161
160
|
|
|
162
|
-
def convert(self, text: str) -> list[list[ChemistryElement]]:
|
|
161
|
+
def convert(self, text: str) -> list[list[ChemistryElement]] | list:
|
|
163
162
|
"""
|
|
164
163
|
Convert text to chemistry symbol
|
|
165
164
|
|
|
@@ -231,6 +230,36 @@ class Text2Chemistry:
|
|
|
231
230
|
|
|
232
231
|
return output
|
|
233
232
|
|
|
233
|
+
@staticmethod
|
|
234
|
+
def beautify_result(
|
|
235
|
+
result: list[list[ChemistryElement]] | list,
|
|
236
|
+
) -> str: # Added in version 4.2.0
|
|
237
|
+
"""Beautify the result from ``Text2Chemistry.convert()``
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
result : list[list[ChemistryElement]] | list
|
|
242
|
+
Convert ``Text2Chemistry.convert()`` result
|
|
243
|
+
|
|
244
|
+
Returns
|
|
245
|
+
-------
|
|
246
|
+
str
|
|
247
|
+
Beautified output
|
|
248
|
+
"""
|
|
249
|
+
if len(result) == 0:
|
|
250
|
+
res = "No possible combination"
|
|
251
|
+
else:
|
|
252
|
+
msg = []
|
|
253
|
+
for i, solution in enumerate(result, start=1):
|
|
254
|
+
msg.append(f"Option {i:02}: {', '.join([x.symbol for x in solution])}")
|
|
255
|
+
for x in solution:
|
|
256
|
+
msg.append(
|
|
257
|
+
f"{x.symbol} ({x.number}. {x.name} - {round(x.atomic_mass, 2)})"
|
|
258
|
+
)
|
|
259
|
+
msg.append("---")
|
|
260
|
+
res = "\n".join(msg)
|
|
261
|
+
return res
|
|
262
|
+
|
|
234
263
|
|
|
235
264
|
class Str2Pixel:
|
|
236
265
|
"""Convert str into pixel"""
|
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
# type: ignore
|
|
2
|
-
# flake8: noqa
|
|
3
|
-
|
|
4
1
|
"""
|
|
5
2
|
Absfuyu: Passwordlib
|
|
6
3
|
--------------------
|
|
7
4
|
Password library
|
|
8
5
|
|
|
9
|
-
Version: 1.0.
|
|
10
|
-
Date updated:
|
|
6
|
+
Version: 1.0.0
|
|
7
|
+
Date updated: 11/02/2025 (dd/mm/yyyy)
|
|
11
8
|
"""
|
|
12
9
|
|
|
10
|
+
# Module level
|
|
11
|
+
###########################################################################
|
|
12
|
+
__all__ = ["PasswordGenerator"]
|
|
13
|
+
|
|
14
|
+
|
|
13
15
|
# Library
|
|
14
16
|
###########################################################################
|
|
15
|
-
# from collections import namedtuple
|
|
16
17
|
import hashlib
|
|
18
|
+
import lzma
|
|
17
19
|
import os
|
|
18
20
|
import random
|
|
19
21
|
import re
|
|
20
|
-
from typing import
|
|
21
|
-
|
|
22
|
-
from absfuyu_res import DATA
|
|
22
|
+
from typing import NamedTuple, Optional
|
|
23
23
|
|
|
24
24
|
from absfuyu.general.data_extension import DictExt, Text
|
|
25
25
|
from absfuyu.general.generator import Charset, Generator
|
|
26
26
|
from absfuyu.logger import logger
|
|
27
|
+
from absfuyu.pkg_data import DataList
|
|
27
28
|
from absfuyu.util import set_min
|
|
28
29
|
from absfuyu.util.pkl import Pickler
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
# Function
|
|
32
33
|
###########################################################################
|
|
33
|
-
def
|
|
34
|
+
def _password_check(password: str) -> bool:
|
|
34
35
|
"""
|
|
35
36
|
Verify the strength of ``password``.
|
|
36
37
|
A password is considered strong if:
|
|
@@ -84,13 +85,13 @@ def password_check(password: str) -> bool:
|
|
|
84
85
|
|
|
85
86
|
# Class
|
|
86
87
|
###########################################################################
|
|
87
|
-
class
|
|
88
|
-
|
|
88
|
+
class PasswordHash(NamedTuple):
|
|
89
|
+
salt: bytes
|
|
90
|
+
key: bytes
|
|
89
91
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
self._words: List[str] = Pickler.load(DATA.PASSWORDLIB)
|
|
92
|
+
|
|
93
|
+
class PasswordGenerator:
|
|
94
|
+
"""Password Generator"""
|
|
94
95
|
|
|
95
96
|
def __str__(self) -> str:
|
|
96
97
|
return f"{self.__class__.__name__}()"
|
|
@@ -98,38 +99,31 @@ class Password:
|
|
|
98
99
|
def __repr__(self) -> str:
|
|
99
100
|
return self.__str__()
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
@staticmethod
|
|
103
|
+
def password_hash(password: str) -> PasswordHash:
|
|
102
104
|
"""
|
|
103
105
|
Generate hash for password
|
|
104
106
|
"""
|
|
105
107
|
salt = os.urandom(32)
|
|
106
108
|
key = hashlib.pbkdf2_hmac(
|
|
107
109
|
hash_name="sha256",
|
|
108
|
-
password=
|
|
110
|
+
password=password.encode("utf-8"),
|
|
109
111
|
salt=salt,
|
|
110
112
|
iterations=100000,
|
|
111
113
|
)
|
|
112
|
-
out = {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
114
|
+
# out = {
|
|
115
|
+
# "salt": salt,
|
|
116
|
+
# "key": key,
|
|
117
|
+
# }
|
|
118
|
+
out = PasswordHash(salt, key)
|
|
116
119
|
return out
|
|
117
120
|
|
|
118
121
|
@staticmethod
|
|
119
|
-
def password_check(password: str):
|
|
122
|
+
def password_check(password: str) -> dict:
|
|
120
123
|
data = Text(password).analyze()
|
|
124
|
+
data = DictExt(data).apply(lambda x: True if x > 0 else False) # type: ignore
|
|
121
125
|
data.__setitem__("length", len(password))
|
|
122
|
-
|
|
123
|
-
return data
|
|
124
|
-
|
|
125
|
-
@property
|
|
126
|
-
def words(self) -> List[str]:
|
|
127
|
-
"""
|
|
128
|
-
Word list to generate passphrase
|
|
129
|
-
|
|
130
|
-
:rtype: list[str]
|
|
131
|
-
"""
|
|
132
|
-
return self._words
|
|
126
|
+
return dict(data)
|
|
133
127
|
|
|
134
128
|
# Password generator
|
|
135
129
|
@staticmethod
|
|
@@ -187,24 +181,26 @@ class Password:
|
|
|
187
181
|
while True:
|
|
188
182
|
pwd = Generator.generate_string(
|
|
189
183
|
charset=charset,
|
|
190
|
-
size=set_min(length, min_value=8),
|
|
184
|
+
size=set_min(length, min_value=8), # type: ignore
|
|
191
185
|
times=1,
|
|
192
186
|
string_type_if_1=True,
|
|
193
187
|
)
|
|
194
188
|
|
|
195
189
|
analyze = Text(pwd).analyze() # Count each type of char
|
|
196
190
|
|
|
197
|
-
s = sum([1 for x in analyze.values() if x > 0])
|
|
191
|
+
s = sum([1 for x in analyze.values() if x > 0]) # type: ignore
|
|
198
192
|
if s > check: # Break loop if each type of char has atleast 1
|
|
199
193
|
break
|
|
200
|
-
return pwd
|
|
194
|
+
return pwd # type: ignore
|
|
201
195
|
|
|
196
|
+
@staticmethod
|
|
202
197
|
def generate_passphrase(
|
|
203
|
-
self,
|
|
204
198
|
num_of_blocks: int = 5,
|
|
205
199
|
block_divider: Optional[str] = None,
|
|
206
200
|
first_letter_cap: bool = True,
|
|
207
201
|
include_number: bool = True,
|
|
202
|
+
*,
|
|
203
|
+
custom_word_list: list[str] | None = None,
|
|
208
204
|
) -> str:
|
|
209
205
|
"""
|
|
210
206
|
Generate a random passphrase
|
|
@@ -223,6 +219,9 @@ class Password:
|
|
|
223
219
|
include_number : bool
|
|
224
220
|
Add number to the end of each word (Default: ``True``)
|
|
225
221
|
|
|
222
|
+
custom_word_list : list[str] | None
|
|
223
|
+
Custom word list for passphrase generation, by default uses a list of 360K+ words
|
|
224
|
+
|
|
226
225
|
Returns
|
|
227
226
|
-------
|
|
228
227
|
str
|
|
@@ -234,25 +233,28 @@ class Password:
|
|
|
234
233
|
>>> print(Password().generate_passphrase())
|
|
235
234
|
Myomectomies7-Sully4-Torpedomen7-Netful2-Begaud8
|
|
236
235
|
"""
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
value += str(random.choice(range(10)))
|
|
243
|
-
return value
|
|
236
|
+
words: list[str] = (
|
|
237
|
+
(lzma.decompress(Pickler.load(DataList.PASSWORDLIB)).decode().split(",")) # type: ignore
|
|
238
|
+
if not custom_word_list
|
|
239
|
+
else custom_word_list
|
|
240
|
+
)
|
|
244
241
|
|
|
245
242
|
if not block_divider:
|
|
246
243
|
block_divider = "-"
|
|
247
244
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
245
|
+
dat = [random.choice(words) for _ in range(num_of_blocks)]
|
|
246
|
+
|
|
247
|
+
if first_letter_cap:
|
|
248
|
+
dat = list(map(lambda x: x.title(), dat))
|
|
249
|
+
|
|
250
|
+
if include_number:
|
|
251
|
+
idx = random.choice(range(5))
|
|
252
|
+
dat[idx] += str(random.choice(range(10)))
|
|
253
|
+
|
|
254
|
+
return block_divider.join(dat)
|
|
251
255
|
|
|
252
256
|
|
|
253
257
|
# Run
|
|
254
258
|
###########################################################################
|
|
255
259
|
if __name__ == "__main__":
|
|
256
260
|
logger.setLevel(10)
|
|
257
|
-
# print(os.urandom(32))
|
|
258
|
-
print(Password.password_check(Password.generate_password()))
|