├── README.md ├── main.py ├── poetry.lock └── pyproject.toml /README.md: -------------------------------------------------------------------------------- 1 | # Chinese Common User Passwords Profiler 2 | > 基于社会工程学的弱口令密码字典生成工具 3 | 4 | # 使用方法 : 5 | 6 | 1. 第一步 : 定义已知信息 7 | ``` 8 | class Person: 9 | NAME = u"李二狗" 10 | PHONE = ["13512345678",] 11 | CARD = "220281198309243953" 12 | BIRTHDAY = ("1983", "09", "24") 13 | HOMETOWN = (u"四川", u"成都", u"高新区") 14 | PLACE = [(u"河北", u"秦皇岛", u"北戴河"),] 15 | QQ = ["987654321",] 16 | COMPANY = [(u"腾讯", "tencent"),] 17 | SCHOOL = [(u"清华大学", u"清华", "tsinghua")] 18 | ACCOUNT = ["twodog",] 19 | PASSWORD = ["old_password",] 20 | ``` 21 | 2. 第二步 : 运行脚本 22 | > 暂只支持 Python2(依赖库 hanzi2pinyin 暂时没有 Python3 版) 23 | 24 | ``` 25 | python2 chinese-weak-password-generator.py 26 | ``` 27 | 3. 输出生成的密码字典并保存到当前目录 password.list 文件中 28 | ``` 29 | [+] liergou => de1644a2551503c15176b5b8a5b27c79 30 | [+] Liergou => e4df76f225cae5c0681997967844e094 31 | [+] 13512345678 => 688c96a5bb633ab6ffb491ee6070ac27 32 | [+] 19830924 => 618a337ffb5525156005105b22763ee0 33 | [+] sichuan => 34f280dfd7f86fa505a52836fa8be720 34 | [+] chengdu => 354b500e3a784a489a27ee50d19ba328 35 | [+] gaoxinqu => f4afe70731e17fe24b94badd9d81989a 36 | [+] Sichuan => c01eba257b2d44afb1896051da9185b4 37 | [+] Chengdu => f4aa575f70b3f78887deb96ce611b187 38 | [+] Gaoxinqu => caec347ba4c61605c14699b0d1703d95 39 | [+] qinhuangdao => 34fbdaed690c48b32f07ec44936ade19 40 | [+] beidaihe => 96c9ca76dc81c12f6b1666c6e30004f3 41 | [+] Qinhuangdao => ba302e73c151f9610ca627bf1c843615 42 | [+] Beidaihe => 2bfeb21646a1c54e19491cb8cd7fcaaf 43 | [+] 987654321 => 6ebe76c9fb411be97b3b0d48b791a7c9 44 | [+] tengxun => 9549c286dff3eec9b3bd97ff6c199bde 45 | [+] Tengxun => 06aca03b11b933a52feebad6a70dba9b 46 | [+] tencent => 3da576879001c77b442b9f8ef95c09d6 47 | [+] Tencent => 9414b97199d909061ce91aeb8faa421f 48 | [+] tencent => 3da576879001c77b442b9f8ef95c09d6 49 | [+] qinghuadaxue => 07e3d184643bbb8fc19aabfdc17b5099 50 | [+] Qinghuadaxue => 0d0078ead5f23c6a5a3999d2678bd1b0 51 | [+] qinghua => 21b6fe282fd67514b7dd062ebea60cd6 52 | [+] Qinghua => 124ded788d9d76fee1f505660ab31b97 53 | [+] tsinghua => 552733250c23324e4169dfe43a4b1233 54 | [+] Tsinghua => 6c5c1441e3283e7543342e59277ea219 55 | [+] tsinghua => 552733250c23324e4169dfe43a4b1233 56 | [+] liergou123 => ddd377570e00c707b58fdcabc1284e07 57 | [+] ergou123 => 4565c4b3ff262ec3f36fa3e9236dc2f9 58 | [+] Liergou123 => 37b4b7f76bf5ec4e430e29f97361fc8b 59 | [+] Ergou123 => 24a46328a4b517eb0d19ebe137e19b37 60 | [+] 13512345678123 => 4bede9e906599088b2999de2723daeb3 61 | [+] 5678123 => 76dcf9b102efa543eecc3b9f847e0934 62 | [+] 243953123 => b343b11001bdabf6513ab55ca7cef889 63 | [+] 220281123 => 0ff5d66e1cf72d9280e7c935cd516204 64 | [+] 1983123 => 934cbe84b040cc8f227997c46e50825c 65 | [+] 0924123 => f8993670caaeac3ffec0667b20b6fae2 66 | [+] 19830924123 => 38bda51e49d7dbc9d05f971e708bdc12 67 | [+] sichuan123 => f6a04d5db1b1f61c05dab88c44babdbf 68 | [+] chengdu123 => b0ef8986d2a279db71cc243b45890a41 69 | [+] gaoxinqu123 => bad977ad7ca2ce50b4afe6c286a50f86 70 | [+] Sichuan123 => 46ef84063649179f96719b9e82ab7c38 71 | [+] Chengdu123 => 4eb7c5687a356a473794822a3b607972 72 | [+] Gaoxinqu123 => 6c0700d2452298ba095cca5c625dacf7 73 | [+] hebei123 => 0fd6af13695ce645e421d6b8ecc44afa 74 | [+] qinhuangdao123 => daef90327893a558e061e39deba4c438 75 | [+] beidaihe123 => 36b7699549da7645d34cd680ca119624 76 | [+] Hebei123 => e06e73ddff2b2608f021402b14b24533 77 | [+] Qinhuangdao123 => 5e310dac76c41f794b4dc51ddef8747e 78 | [+] Beidaihe123 => 0d9777ea45400e4984c1a5ddcb01c7f7 79 | [+] 987654321123 => 6932768a2fa35b66643d2b85f8c74f3b 80 | [+] tengxun123 => aaec5c7d699c131f92de802ed0e97b34 81 | [+] Tengxun123 => 1a4341fd6278eb27938275a9ba471942 82 | [+] tencent123 => 7e89ae637d34630951bb16a54eb509ad 83 | [+] Tencent123 => 5c1157efc98a64a758c31f839280dd32 84 | [+] tencent123 => 7e89ae637d34630951bb16a54eb509ad 85 | [+] qinghuadaxue123 => fe9e00002288176cbefc6bccf1ce5070 86 | [+] Qinghuadaxue123 => 5ab2a9cefe356b56cf92b1220b2ebb6a 87 | [+] qhdx123 => c50db9d0b488c010c6fb42b5ff011e57 88 | [+] qinghua123 => 85bfb8827f36a0cdea6bfbc7f1a7e640 89 | [+] Qinghua123 => 6e11028a878522d705a881a58f7e043a 90 | [+] tsinghua123 => 0c07f797cc47f333ea66914e1e942fdc 91 | [+] Tsinghua123 => dd4802e8f366682e64f584a124bca827 92 | [+] tsinghua123 => 0c07f797cc47f333ea66914e1e942fdc 93 | [+] twodog123 => f36bb3069a5dff1e379f6661cfc20510 94 | [+] Twodog123 => cd2c691843e79134a577f4e23529f096 95 | [+] twodog123 => f36bb3069a5dff1e379f6661cfc20510 96 | [+] liergou@ => 15188c6f4ecf8a75afdcc282126a3b63 97 | [+] Liergou@ => ae03dc5278c348d96a630e91b49a23de 98 | [+] 13512345678@ => c73545900759f24b7db46300f667b90e 99 | [+] 243953@ => 210cdf8b221a7d4085dedde2c0eec550 100 | [+] 220281@ => 42c90fae6b332d83727ce281fb4b8b14 101 | [+] 19830924@ => c302c41ecf38bfedf42cccb8c77d9d38 102 | [+] sichuan@ => 195eeb05c2de157df2720a1e0e814f6a 103 | [+] chengdu@ => a6dfb50a2d4fbae87fe599212d99c4ea 104 | [+] gaoxinqu@ => 8b9be9a26915265501e9bc9b283da0bc 105 | [+] Sichuan@ => b7c74335b988bcd0206cc13fb1a01514 106 | [+] Chengdu@ => 02909a156f7a95dfd02f10ce743c3061 107 | [+] Gaoxinqu@ => 5c96d1c46b14c1d1f02854c2f7179471 108 | [+] qinhuangdao@ => 3be6b307e807fb69f2a690169a72d100 109 | [+] beidaihe@ => c678f99cad2d8d7013ef69ad9d42a0d3 110 | [+] Qinhuangdao@ => d6be705e873f2c21cdca9dab7b3d6f64 111 | [+] Beidaihe@ => bc96c22a8276f3025e965b1b1b53d3a0 112 | [+] 987654321@ => 775166f0b8517d632502e5d7415376f8 113 | [+] tengxun@ => 2a1e9a45302a8df38adaec946737ff4c 114 | [+] Tengxun@ => fcc7d393f00907c9bc4e7a63cd8e1d9f 115 | [+] tencent@ => 52163609f75e54ee6fe9c8f76d3e04c0 116 | [+] Tencent@ => 7cb34e3ad7da645ade9621e6fd5cac04 117 | [+] tencent@ => 52163609f75e54ee6fe9c8f76d3e04c0 118 | [+] qinghuadaxue@ => 96d347435f231c802630b058924fdcf4 119 | [+] Qinghuadaxue@ => aa1fafb590a926bdf683a9a12ef49ebb 120 | [+] qinghua@ => 6d1e78889084335a05433b5736cbc6e4 121 | [+] Qinghua@ => 6f668440454daf3e135f9f1faba7d41c 122 | [+] tsinghua@ => c24f6d1e9635faa0a8aa81bfe7f80d57 123 | [+] Tsinghua@ => 9edc7ec2eb0ef43642b3b5af634afe5a 124 | [+] tsinghua@ => c24f6d1e9635faa0a8aa81bfe7f80d57 125 | [+] twodog@ => 13233f59565ca53942d54888be3ecb2c 126 | [+] Twodog@ => 8db63d1f815d2abbe51bf6adbf3a32d9 127 | [+] twodog@ => 13233f59565ca53942d54888be3ecb2c 128 | [+] liergouabc => 754caea1fd5c89c034ea55c0376a7fb9 129 | ... 130 | ``` 131 | 132 | # 参考资料 133 | 134 | ``` 135 | http://www.moonsec.com/post-181.html 136 | ``` 137 | 138 | * https://arxiv.org/abs/2306.01545 -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import hashlib 3 | import logging 4 | from itertools import product, combinations 5 | from jinja2 import Template 6 | from pypinyin import lazy_pinyin, Style 7 | from rich.logging import RichHandler 8 | from rich.console import Console 9 | 10 | # Initialize rich console and logging 11 | console = Console() 12 | logging.basicConfig( 13 | level=logging.INFO, 14 | format="%(message)s", 15 | handlers=[RichHandler(console=console)] 16 | ) 17 | logger = logging.getLogger("PasswordGenerator") 18 | 19 | 20 | # Utility functions 21 | def to_pinyin(word): 22 | """Convert Chinese characters to full pinyin.""" 23 | return ''.join(lazy_pinyin(word)) 24 | 25 | 26 | def to_pinyin_first_letter(word): 27 | """Convert Chinese characters to the first letter of pinyin.""" 28 | return ''.join(lazy_pinyin(word, style=Style.FIRST_LETTER)) 29 | 30 | 31 | def to_title_case(word): 32 | """Convert a word to title case.""" 33 | return word[0].upper() + word[1:] 34 | 35 | 36 | def get_md5(password): 37 | """Generate MD5 hash of a password.""" 38 | return hashlib.md5(password.encode()).hexdigest() 39 | 40 | 41 | def extract_components(data, use_pinyin=True): 42 | """ 43 | Extract components from input data. 44 | Supports tuples, lists, and strings. 45 | """ 46 | result = [] 47 | if isinstance(data, tuple): 48 | for item in data: 49 | pinyin = to_pinyin(item) if use_pinyin else item 50 | result.extend([pinyin, to_pinyin_first_letter(item), to_title_case(pinyin)]) 51 | elif isinstance(data, list): 52 | for item in data: 53 | result.extend(extract_components(item, use_pinyin)) 54 | else: 55 | pinyin = to_pinyin(data) if use_pinyin else data 56 | result.extend([pinyin, to_pinyin_first_letter(data), to_title_case(pinyin)]) 57 | return result 58 | 59 | class Person: 60 | """ 61 | Represents a person with more generic, flexible attributes. 62 | """ 63 | def __init__(self): 64 | self.attributes = {} 65 | 66 | def set_surname(self, surname): 67 | self.attributes['surname'] = surname 68 | 69 | def set_first_name(self, first_name): 70 | self.attributes['first_name'] = first_name 71 | 72 | def set_phone_numbers(self, phone_numbers): 73 | self.attributes['phone_numbers'] = phone_numbers 74 | 75 | def set_identity(self, identity): 76 | self.attributes['identity'] = identity 77 | 78 | def set_birthdate(self, birthdate): 79 | self.attributes['birthdate'] = birthdate 80 | 81 | def set_hometowns(self, hometowns): 82 | self.attributes['hometowns'] = hometowns 83 | 84 | def set_places(self, places): 85 | self.attributes['places'] = places 86 | 87 | def set_social_media(self, social_media): 88 | self.attributes['social_media'] = social_media 89 | 90 | def set_workplaces(self, workplaces): 91 | self.attributes['workplaces'] = workplaces 92 | 93 | def set_educational_institutions(self, institutions): 94 | self.attributes['educational_institutions'] = institutions 95 | 96 | def set_accounts(self, accounts): 97 | self.attributes['accounts'] = accounts 98 | 99 | def set_passwords(self, passwords): 100 | self.attributes['passwords'] = passwords 101 | 102 | def get_components(self): 103 | """ 104 | Extract components from all attributes for password generation. 105 | Use pinyin conversion where needed. 106 | """ 107 | return { 108 | 'name': extract_components((self.attributes.get('surname', ''), 109 | self.attributes.get('first_name', ''))), 110 | 'phone_numbers': extract_components(self.attributes.get('phone_numbers', []), use_pinyin=False), 111 | 'identity': extract_components(self.attributes.get('identity', ''), use_pinyin=False), 112 | 'birthdate': extract_components(self.attributes.get('birthdate', ''), use_pinyin=False), 113 | 'hometowns': extract_components(self.attributes.get('hometowns', [])), 114 | 'places': extract_components(self.attributes.get('places', [])), 115 | 'social_media': extract_components(self.attributes.get('social_media', []), use_pinyin=False), 116 | 'workplaces': extract_components(self.attributes.get('workplaces', [])), 117 | 'educational_institutions': extract_components(self.attributes.get('educational_institutions', [])), 118 | 'accounts': extract_components(self.attributes.get('accounts', []), use_pinyin=False), 119 | } 120 | 121 | # Password generation functions 122 | def generate_combinations(components, delimiters): 123 | """ 124 | Generate all possible combinations of components with delimiters. 125 | """ 126 | for length in range(1, len(components) + 1): 127 | for component_group in combinations(components.values(), length): 128 | for delimiter_group in product(delimiters, repeat=length - 1): 129 | for component_combination in product(*component_group): 130 | password = component_combination[0] 131 | for delim, comp in zip(delimiter_group, component_combination[1:]): 132 | password += delim + comp 133 | yield password 134 | 135 | 136 | def generate_passwords(templates, combinations, prefixes, suffixes): 137 | """ 138 | Generate passwords based on templates, combinations, prefixes, and suffixes. 139 | """ 140 | for tmpl_str in templates: 141 | template = Template(tmpl_str) 142 | for combination in combinations: 143 | for prefix in prefixes: 144 | for suffix in suffixes: 145 | yield template.render(combination=combination, prefix=prefix, suffix=suffix) 146 | 147 | 148 | # Command-line interface 149 | def parse_args(): 150 | """ 151 | Parse command-line arguments. 152 | """ 153 | parser = argparse.ArgumentParser(description="Password Generator using Personal Information") 154 | parser.add_argument("--prefixes", nargs="*", default=["qwert", "123"], help="List of prefixes") 155 | parser.add_argument("--suffixes", nargs="*", default=["", "123", "@", "abc", ".", "123.", "!!!"], help="List of suffixes") 156 | parser.add_argument("--delimiters", nargs="*", default=["", "-", ".", "|", "_", "+", "#", "@"], help="List of delimiters") 157 | parser.add_argument("--templates", nargs="*", default=['{{ prefix }}{{ combination }}{{ suffix }}'], help="List of templates") 158 | return parser.parse_args() 159 | 160 | 161 | def main(): 162 | """ 163 | Main function to generate passwords based on user input. 164 | """ 165 | args = parse_args() 166 | 167 | # Create a sample person 168 | person = Person() 169 | person.set_surname("李") 170 | person.set_first_name("二狗") 171 | person.set_phone_numbers(["13512345678"]) 172 | person.set_identity("220281198309243953") 173 | person.set_birthdate(("1983", "09", "24")) 174 | person.set_hometowns((u"四川", u"成都", u"高新区")) 175 | person.set_places([(u"河北", u"秦皇岛", u"北戴河")]) 176 | person.set_social_media(["987654321"]) 177 | person.set_workplaces([(u"腾讯", "tencent")]) 178 | person.set_educational_institutions([(u"清华大学", u"清华", "tsinghua")]) 179 | person.set_accounts(["twodogs"]) 180 | person.set_passwords(["old_password"]) 181 | 182 | # Extract components 183 | components = person.get_components() 184 | logger.info("Extracted components: %s", components) 185 | 186 | # Generate combinations 187 | combinations = generate_combinations(components, delimiters=args.delimiters) 188 | 189 | # Generate passwords 190 | passwords = set() 191 | for password in generate_passwords(args.templates, combinations, args.prefixes, args.suffixes): 192 | if password not in passwords: 193 | passwords.add(password) 194 | print(password) 195 | 196 | 197 | if __name__ == "__main__": 198 | main() 199 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "hanzi2pinyin" 5 | version = "0.1" 6 | description = "汉字转拼音" 7 | optional = false 8 | python-versions = "*" 9 | groups = ["main"] 10 | files = [ 11 | {file = "hanzi2pinyin-0.1.tar.gz", hash = "sha256:361c0cfd451b49c510152547d8853dfa8615739e92f7aa90027f918775704418"}, 12 | ] 13 | 14 | [[package]] 15 | name = "jinja2" 16 | version = "3.1.6" 17 | description = "A very fast and expressive template engine." 18 | optional = false 19 | python-versions = ">=3.7" 20 | groups = ["main"] 21 | files = [ 22 | {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, 23 | {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, 24 | ] 25 | 26 | [package.dependencies] 27 | MarkupSafe = ">=2.0" 28 | 29 | [package.extras] 30 | i18n = ["Babel (>=2.7)"] 31 | 32 | [[package]] 33 | name = "markdown-it-py" 34 | version = "3.0.0" 35 | description = "Python port of markdown-it. Markdown parsing, done right!" 36 | optional = false 37 | python-versions = ">=3.8" 38 | groups = ["main"] 39 | files = [ 40 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 41 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 42 | ] 43 | 44 | [package.dependencies] 45 | mdurl = ">=0.1,<1.0" 46 | 47 | [package.extras] 48 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 49 | code-style = ["pre-commit (>=3.0,<4.0)"] 50 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 51 | linkify = ["linkify-it-py (>=1,<3)"] 52 | plugins = ["mdit-py-plugins"] 53 | profiling = ["gprof2dot"] 54 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 55 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 56 | 57 | [[package]] 58 | name = "markupsafe" 59 | version = "3.0.2" 60 | description = "Safely add untrusted strings to HTML/XML markup." 61 | optional = false 62 | python-versions = ">=3.9" 63 | groups = ["main"] 64 | files = [ 65 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, 66 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, 67 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, 68 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, 69 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, 70 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, 71 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, 72 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, 73 | {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, 74 | {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, 75 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, 76 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, 77 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, 78 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, 79 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, 80 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, 81 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, 82 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, 83 | {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, 84 | {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, 85 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, 86 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, 87 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, 88 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, 89 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, 90 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, 91 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, 92 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, 93 | {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, 94 | {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, 95 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, 96 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, 97 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, 98 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, 99 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, 100 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, 101 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, 102 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, 103 | {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, 104 | {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, 105 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, 106 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, 107 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, 108 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, 109 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, 110 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, 111 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, 112 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, 113 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, 114 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, 115 | {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, 116 | {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, 117 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, 118 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, 119 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, 120 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, 121 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, 122 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, 123 | {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, 124 | {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, 125 | {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, 126 | ] 127 | 128 | [[package]] 129 | name = "mdurl" 130 | version = "0.1.2" 131 | description = "Markdown URL utilities" 132 | optional = false 133 | python-versions = ">=3.7" 134 | groups = ["main"] 135 | files = [ 136 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 137 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 138 | ] 139 | 140 | [[package]] 141 | name = "pygments" 142 | version = "2.19.1" 143 | description = "Pygments is a syntax highlighting package written in Python." 144 | optional = false 145 | python-versions = ">=3.8" 146 | groups = ["main"] 147 | files = [ 148 | {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, 149 | {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, 150 | ] 151 | 152 | [package.extras] 153 | windows-terminal = ["colorama (>=0.4.6)"] 154 | 155 | [[package]] 156 | name = "pypinyin" 157 | version = "0.50.0" 158 | description = "汉字拼音转换模块/工具." 159 | optional = false 160 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" 161 | groups = ["main"] 162 | files = [ 163 | {file = "pypinyin-0.50.0-py2.py3-none-any.whl", hash = "sha256:a6ff9a58b8c3ba4599e5ebccf7fab7850257114be70c05a7baab435d1208358f"}, 164 | {file = "pypinyin-0.50.0.tar.gz", hash = "sha256:72e77c4b9b78bad102aca5fefccebdb23439b02717c626039be14a78643980fb"}, 165 | ] 166 | 167 | [[package]] 168 | name = "rich" 169 | version = "14.0.0" 170 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 171 | optional = false 172 | python-versions = ">=3.8.0" 173 | groups = ["main"] 174 | files = [ 175 | {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, 176 | {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, 177 | ] 178 | 179 | [package.dependencies] 180 | markdown-it-py = ">=2.2.0" 181 | pygments = ">=2.13.0,<3.0.0" 182 | typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} 183 | 184 | [package.extras] 185 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 186 | 187 | [[package]] 188 | name = "typing-extensions" 189 | version = "4.13.1" 190 | description = "Backported and Experimental Type Hints for Python 3.8+" 191 | optional = false 192 | python-versions = ">=3.8" 193 | groups = ["main"] 194 | markers = "python_version == \"3.10\"" 195 | files = [ 196 | {file = "typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69"}, 197 | {file = "typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff"}, 198 | ] 199 | 200 | [metadata] 201 | lock-version = "2.1" 202 | python-versions = "^3.10" 203 | content-hash = "14f7b931f25badc26ec18f30c3b5c86c8f2f14dfb4172d7f4d9ebff946b52348" 204 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "ccupp" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Yihang Wang "] 6 | readme = "README.md" 7 | package-mode = false 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.10" 11 | hanzi2pinyin = "^0.1" 12 | pypinyin = "^0.50.0" 13 | jinja2 = "^3.1.3" 14 | rich = "^14.0.0" 15 | 16 | 17 | [build-system] 18 | requires = ["poetry-core"] 19 | build-backend = "poetry.core.masonry.api" 20 | --------------------------------------------------------------------------------