python-plugins 0.1.6__tar.gz → 0.1.7__tar.gz

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.
Files changed (74) hide show
  1. {python_plugins-0.1.6 → python_plugins-0.1.7}/CHANGES.rst +7 -0
  2. {python_plugins-0.1.6 → python_plugins-0.1.7}/PKG-INFO +1 -1
  3. python_plugins-0.1.7/docs/conf.py +51 -0
  4. {python_plugins-0.1.6 → python_plugins-0.1.7}/docs/index.rst +1 -1
  5. python_plugins-0.1.7/docs/requirements.txt +2 -0
  6. {python_plugins-0.1.6 → python_plugins-0.1.7}/docs/usage.rst +2 -1
  7. python_plugins-0.1.7/src/python_plugins/__about__.py +1 -0
  8. python_plugins-0.1.7/src/python_plugins/__init__.py +1 -0
  9. python_plugins-0.1.7/src/python_plugins/convert/pretty.py +11 -0
  10. python_plugins-0.1.7/src/python_plugins/crypto/file_to_file.py +112 -0
  11. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/crypto/str_to_list.py +13 -3
  12. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/utils/remove_pycache.py +1 -1
  13. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/weixin/wechat.py +2 -2
  14. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/weixin/wechat_crypt.py +13 -9
  15. python_plugins-0.1.7/tests/test_crypt_file.py +84 -0
  16. {python_plugins-0.1.6 → python_plugins-0.1.7}/tests/test_utils.py +3 -0
  17. python_plugins-0.1.7/tests/test_weixin.py +132 -0
  18. python_plugins-0.1.6/docs/conf.py +0 -30
  19. python_plugins-0.1.6/docs/requirements.txt +0 -2
  20. python_plugins-0.1.6/src/python_plugins/__about__.py +0 -1
  21. python_plugins-0.1.6/tests/__init__.py +0 -1
  22. python_plugins-0.1.6/tests/test_weixin.py +0 -104
  23. {python_plugins-0.1.6 → python_plugins-0.1.7}/.github/workflows/release.yml +0 -0
  24. {python_plugins-0.1.6 → python_plugins-0.1.7}/.gitignore +0 -0
  25. {python_plugins-0.1.6 → python_plugins-0.1.7}/.readthedocs.yaml +0 -0
  26. {python_plugins-0.1.6 → python_plugins-0.1.7}/LICENSE.rst +0 -0
  27. {python_plugins-0.1.6 → python_plugins-0.1.7}/README.rst +0 -0
  28. {python_plugins-0.1.6 → python_plugins-0.1.7}/docs/Makefile +0 -0
  29. {python_plugins-0.1.6 → python_plugins-0.1.7}/docs/api.rst +0 -0
  30. {python_plugins-0.1.6 → python_plugins-0.1.7}/docs/changes.rst +0 -0
  31. {python_plugins-0.1.6 → python_plugins-0.1.7}/docs/examples.rst +0 -0
  32. {python_plugins-0.1.6 → python_plugins-0.1.7}/docs/make.bat +0 -0
  33. {python_plugins-0.1.6 → python_plugins-0.1.7}/examples/README.rst +0 -0
  34. {python_plugins-0.1.6 → python_plugins-0.1.7}/pyproject.toml +0 -0
  35. {python_plugins-0.1.6 → python_plugins-0.1.7}/requirements/build.in +0 -0
  36. {python_plugins-0.1.6 → python_plugins-0.1.7}/requirements/test.in +0 -0
  37. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/convert/__init__.py +0 -0
  38. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/convert/datetime_str.py +0 -0
  39. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/convert/xml.py +0 -0
  40. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/crypto/__init__.py +0 -0
  41. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/crypto/fernet.py +0 -0
  42. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/dumps/__init__.py +0 -0
  43. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/dumps/postgresql_dump.py +0 -0
  44. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/email/__init__.py +0 -0
  45. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/email/smtp.py +0 -0
  46. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/hashes/__init__.py +0 -0
  47. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/hashes/hash.py +0 -0
  48. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/jwt/__init__.py +0 -0
  49. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/jwt/jwt.py +0 -0
  50. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/models/__init__.py +0 -0
  51. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/models/mixins/__init__.py +0 -0
  52. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/models/mixins/data_mixin.py +0 -0
  53. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/models/mixins/primary_key_mixin.py +0 -0
  54. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/models/mixins/timestamp_mixin.py +0 -0
  55. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/models/mixins/token_minxin.py +0 -0
  56. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/models/mixins/user_minxin.py +0 -0
  57. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/models/update.py +0 -0
  58. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/process/__init__.py +0 -0
  59. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/process/python_venv_process.py +0 -0
  60. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/process/sub_process.py +0 -0
  61. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/random/__init__.py +0 -0
  62. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/random/random_str.py +0 -0
  63. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/weixin/biz_data_crypt.py +0 -0
  64. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/weixin/error_code.py +0 -0
  65. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/weixin/format_response.py +0 -0
  66. {python_plugins-0.1.6 → python_plugins-0.1.7}/src/python_plugins/weixin/weixin_api.py +0 -0
  67. {python_plugins-0.1.6/src/python_plugins → python_plugins-0.1.7/tests}/__init__.py +0 -0
  68. {python_plugins-0.1.6 → python_plugins-0.1.7}/tests/conftest.py +0 -0
  69. {python_plugins-0.1.6 → python_plugins-0.1.7}/tests/test_crypto.py +0 -0
  70. {python_plugins-0.1.6 → python_plugins-0.1.7}/tests/test_email.py +0 -0
  71. {python_plugins-0.1.6 → python_plugins-0.1.7}/tests/test_jwt.py +0 -0
  72. {python_plugins-0.1.6 → python_plugins-0.1.7}/tests/test_process.py +0 -0
  73. {python_plugins-0.1.6 → python_plugins-0.1.7}/tests/test_random.py +0 -0
  74. {python_plugins-0.1.6 → python_plugins-0.1.7}/tests/test_sqlalchemy.py +0 -0
@@ -1,3 +1,10 @@
1
+ v0.1.7
2
+ ------
3
+
4
+ Released 2024-10-16
5
+
6
+ - change html_theme of docs to sphinx_rtd_theme
7
+
1
8
  v0.1.6
2
9
  ------
3
10
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: python-plugins
3
- Version: 0.1.6
3
+ Version: 0.1.7
4
4
  Summary: A collection of Python functions and classes.
5
5
  Project-URL: Documentation, https://python-plugins.readthedocs.io
6
6
  Project-URL: Source, https://github.com/ojso/python-plugins
@@ -0,0 +1,51 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ #
3
+ # For the full list of built-in configuration values, see the documentation:
4
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html
5
+
6
+ # -- Project information -----------------------------------------------------
7
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
+
9
+ project = 'python-plugins'
10
+ copyright = '2024, David Hua'
11
+ author = 'David Hua'
12
+
13
+ # -- General configuration ---------------------------------------------------
14
+ # -- General configuration
15
+
16
+ extensions = [
17
+ "sphinx.ext.duration",
18
+ "sphinx.ext.doctest",
19
+ "sphinx.ext.autodoc",
20
+ "sphinx.ext.autosummary",
21
+ "sphinx.ext.intersphinx",
22
+ ]
23
+
24
+ # intersphinx_mapping = {
25
+ # "rtd": ("https://docs.readthedocs.io/en/stable/", None),
26
+ # "python": ("https://docs.python.org/3/", None),
27
+ # "sphinx": ("https://www.sphinx-doc.org/en/master/", None),
28
+ # }
29
+ # intersphinx_disabled_domains = ["std"]
30
+
31
+ templates_path = ["_templates"]
32
+
33
+ # -- Options for EPUB output
34
+ epub_show_urls = "footnote"
35
+
36
+ # List of patterns, relative to source directory, that match files and
37
+ # directories to ignore when looking for source files.
38
+ # This pattern also affects html_static_path and html_extra_path.
39
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
40
+
41
+ # -- Options for HTML output -------------------------------------------------
42
+
43
+ # The theme to use for HTML and HTML Help pages. See the documentation for
44
+ # a list of builtin themes.
45
+ #
46
+ html_theme = "sphinx_rtd_theme"
47
+
48
+ # Add any paths that contain custom static files (such as style sheets) here,
49
+ # relative to this directory. They are copied after the builtin static files,
50
+ # so a file named "default.css" will overwrite the builtin "default.css".
51
+ html_static_path = ["_static"]
@@ -1,7 +1,7 @@
1
1
  Welcome to python-plugins's documentation!
2
2
  ============================================
3
3
 
4
- **python-plugins** is a Flask extensions with admin, SQLAlchemy, babel, forms, fields, widgets, and so on.
4
+ **python-plugins** is a collection with common python utils.
5
5
 
6
6
  .. toctree::
7
7
  :maxdepth: 2
@@ -0,0 +1,2 @@
1
+ sphinx
2
+ sphinx_rtd_theme
@@ -59,7 +59,8 @@ remove_pycache
59
59
 
60
60
  from python_plugins.utils.remove_pycache import remove_pycache
61
61
 
62
- remove_pycache(".")
62
+ remove_pycache() # default is "."
63
+ remove_pycache("./tests")
63
64
 
64
65
 
65
66
  weixin.wechat
@@ -0,0 +1 @@
1
+ __version__ = "0.1.7"
@@ -0,0 +1 @@
1
+ from .utils.remove_pycache import remove_pycache
@@ -0,0 +1,11 @@
1
+ from re import sub
2
+
3
+
4
+ def prettify_class_name(name):
5
+ """Split words in PascalCase string into separate words.
6
+
7
+ :param name:
8
+ String to split
9
+ """
10
+ return sub(r"(?<=.)([A-Z])", r" \1", name)
11
+
@@ -0,0 +1,112 @@
1
+ from .str_to_list import encrypt_bytes_to_list
2
+ from .str_to_list import encrypt_str_to_list
3
+ from .str_to_list import decrypt_list_to_bytes
4
+ from .str_to_list import decrypt_list_to_str
5
+ from getpass import getpass
6
+
7
+
8
+ def bytes_from_file(fin) -> bytes:
9
+ with open(fin, "rb") as f:
10
+ s = f.read()
11
+ return s
12
+
13
+
14
+ def bytes_to_file(s: bytes, fout):
15
+ with open(fout, "wb") as f:
16
+ f.write(s)
17
+
18
+
19
+ def str_from_txtfile(fin) -> str:
20
+ with open(fin, encoding="utf-8") as f:
21
+ s = f.read()
22
+ return s
23
+
24
+
25
+ def str_to_txtfile(s: str, fout):
26
+ with open(fout, "w", encoding="utf-8") as f:
27
+ f.write(s)
28
+
29
+
30
+ def bytes_to_file(s, fout):
31
+ with open(fout, "wb") as f:
32
+ f.write(s)
33
+
34
+
35
+ def encrypt_txt(s: str, prompt=None, accept_password=False):
36
+ if not prompt:
37
+ prompt = input("input prompt=")
38
+ password = None
39
+ if accept_password:
40
+ password = getpass("input password=")
41
+ if password:
42
+ encrypted_list = encrypt_str_to_list(s, password)
43
+ encrypted_list[0] = "-"
44
+ else:
45
+ encrypted_list = encrypt_str_to_list(s)
46
+ s2 = "\n".join([prompt] + encrypted_list)
47
+ return s2
48
+
49
+
50
+ def decrypt_txt(s: str):
51
+ encrypted_list = s.split("\n")
52
+ prompt = encrypted_list[0]
53
+ if encrypted_list[1] == "-":
54
+ print(prompt)
55
+ password = getpass("input password=")
56
+ s2 = decrypt_list_to_str(encrypted_list[1:], password)
57
+ else:
58
+ s2 = decrypt_list_to_str(encrypted_list[1:])
59
+
60
+ return s2
61
+
62
+
63
+ def encrypt_txtfile(fin, fout, prompt=None, accept_password=False):
64
+ s = str_from_txtfile(fin)
65
+ s2 = encrypt_txt(s, prompt, accept_password)
66
+ str_to_txtfile(s2, fout)
67
+
68
+
69
+ def decrypt_txtfile(fin, fout):
70
+ s = str_from_txtfile(fin)
71
+ s2 = decrypt_txt(s)
72
+ str_to_txtfile(s2, fout)
73
+
74
+
75
+ def encrypt_bytes(s: bytes, prompt=None, accept_password=False):
76
+ if not prompt:
77
+ prompt = input("input prompt=")
78
+ password = None
79
+ if accept_password:
80
+ password = getpass("input password=")
81
+ if password:
82
+ encrypted_list = encrypt_bytes_to_list(s, password)
83
+ encrypted_list[0] = "-"
84
+ else:
85
+ encrypted_list = encrypt_bytes_to_list(s)
86
+ s2 = "\n".join([prompt] + encrypted_list)
87
+ return s2
88
+
89
+
90
+ def decrypt_bytes(s: str):
91
+ encrypted_list = s.split("\n")
92
+ prompt = encrypted_list[0]
93
+ if encrypted_list[1] == "-":
94
+ print(prompt)
95
+ password = getpass("input password=")
96
+ s2 = decrypt_list_to_bytes(encrypted_list[1:], password)
97
+ else:
98
+ s2 = decrypt_list_to_bytes(encrypted_list[1:])
99
+
100
+ return s2
101
+
102
+
103
+ def encrypt_file(fin, fout, prompt=None, accept_password=False):
104
+ s = bytes_from_file(fin)
105
+ s2 = encrypt_bytes(s, prompt, accept_password)
106
+ str_to_txtfile(s2, fout)
107
+
108
+
109
+ def decrypt_file(fin, fout):
110
+ s = str_from_txtfile(fin)
111
+ s2 = decrypt_bytes(s)
112
+ bytes_to_file(s2, fout)
@@ -54,7 +54,7 @@ def str_randsplit_to_list(s, n1=10, n2=30):
54
54
  return r
55
55
 
56
56
 
57
- def encrypt_str_to_list(s: str, password=None, prefix=None):
57
+ def encrypt_bytes_to_list(s: bytes, password=None, prefix=None):
58
58
  if password is None:
59
59
  password = rand_letter(random.randint(6, 16))
60
60
  out_password = password
@@ -66,7 +66,7 @@ def encrypt_str_to_list(s: str, password=None, prefix=None):
66
66
  key, safe_salt, times = get_key(password)
67
67
 
68
68
  cipher_suite = Fernet(key)
69
- encrypted_data = cipher_suite.encrypt(s.encode())
69
+ encrypted_data = cipher_suite.encrypt(s)
70
70
  safe_data = bytes_to_url64str(encrypted_data)
71
71
 
72
72
  list_out = [out_password, safe_salt, str(times)] + str_randsplit_to_list(safe_data)
@@ -74,7 +74,7 @@ def encrypt_str_to_list(s: str, password=None, prefix=None):
74
74
  return list_out
75
75
 
76
76
 
77
- def decrypt_list_to_str(list_in, password=None, prefix=None):
77
+ def decrypt_list_to_bytes(list_in, password=None, prefix=None) -> bytes:
78
78
  _password, safe_salt, _times, *_data = list_in
79
79
  if password is None:
80
80
  password = _password
@@ -88,4 +88,14 @@ def decrypt_list_to_str(list_in, password=None, prefix=None):
88
88
  key, _, _ = get_key(password, safe_salt, times)
89
89
  cipher_suite = Fernet(key)
90
90
  decrypted_bytes = cipher_suite.decrypt(url64str_to_bytes(s))
91
+ return decrypted_bytes
92
+
93
+
94
+ def encrypt_str_to_list(s: str, password=None, prefix=None):
95
+ arr = encrypt_bytes_to_list(s.encode(), password, prefix)
96
+ return arr
97
+
98
+
99
+ def decrypt_list_to_str(list_in, password=None, prefix=None):
100
+ decrypted_bytes = decrypt_list_to_bytes(list_in, password, prefix)
91
101
  return decrypted_bytes.decode()
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import shutil
3
3
 
4
- def remove_pycache(dir_path):
4
+ def remove_pycache(dir_path="."):
5
5
  for root, dirs, files in os.walk(dir_path):
6
6
  if "venv" in root or "git" in root:
7
7
  continue
@@ -1,6 +1,7 @@
1
1
  import hashlib
2
2
  from python_plugins.convert import xml2dict
3
3
  from .wechat_crypt import MessageCrypt
4
+ from .wechat_crypt import get_signature
4
5
  from .format_response import get_wechat_xml_response
5
6
 
6
7
 
@@ -24,8 +25,7 @@ class Wechat:
24
25
  nonce = args["nonce"]
25
26
  echostr = args["echostr"]
26
27
  token = self.app["token"]
27
- tmpstr = "".join(sorted([token, timestamp, nonce])).encode()
28
- if hashlib.sha1(tmpstr).hexdigest() == signature:
28
+ if get_signature([token, timestamp, nonce]) == signature:
29
29
  return echostr
30
30
  else:
31
31
  return
@@ -12,11 +12,15 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
12
12
 
13
13
  ENCRYPT_TEXT_RESPONSE_TEMPLATE = """<xml>
14
14
  <Encrypt><![CDATA[{msg_encrypt}]]></Encrypt>
15
- <MsgSignature><![CDATA[{msg_signaturet}]]></MsgSignature>
15
+ <MsgSignature><![CDATA[{msg_signature}]]></MsgSignature>
16
16
  <TimeStamp>{timestamp}</TimeStamp>
17
17
  <Nonce><![CDATA[{nonce}]]></Nonce>
18
18
  </xml>"""
19
19
 
20
+ def get_signature(arr):
21
+ tmpstr = "".join(sorted(arr)).encode("utf-8")
22
+ return hashlib.sha1(tmpstr).hexdigest()
23
+
20
24
 
21
25
  class MessageCrypt:
22
26
  def __init__(self, appid, token, aeskey):
@@ -24,26 +28,26 @@ class MessageCrypt:
24
28
  self.token = token
25
29
  self.key = base64.b64decode(aeskey + "=")
26
30
 
27
- def get_sha1(self, token, timestamp, nonce, txt):
28
- tmpstr = "".join(sorted([token, timestamp, nonce, txt])).encode("utf8")
29
- return hashlib.sha1(tmpstr).hexdigest()
30
-
31
31
  def generate(self, encrypt, signature, timestamp, nonce):
32
32
  data = {
33
33
  "msg_encrypt": encrypt,
34
- "msg_signaturet": signature,
34
+ "msg_signature": signature,
35
35
  "timestamp": timestamp,
36
36
  "nonce": nonce,
37
37
  }
38
38
  return ENCRYPT_TEXT_RESPONSE_TEMPLATE.format(**data)
39
39
 
40
- def encrypt_msg(self, msg, timestamp, nonce):
40
+ def get_encrypt_sig(self, msg, timestamp, nonce):
41
41
  msg_encrypt = WechatCrypt.encrypt(msg, self.key, self.appid)
42
- signature = self.get_sha1(self.token, timestamp, nonce, msg_encrypt)
42
+ signature = get_signature([self.token, timestamp, nonce, msg_encrypt])
43
+ return (msg_encrypt, signature)
44
+
45
+ def encrypt_msg(self, msg, timestamp, nonce):
46
+ msg_encrypt,signature = self.get_encrypt_sig(msg,timestamp,nonce)
43
47
  return self.generate(msg_encrypt, signature, timestamp, nonce)
44
48
 
45
49
  def decrypt_msg(self, timestamp, nonce, encrypt, msg_signature):
46
- signature = self.get_sha1(self.token, timestamp, nonce, encrypt)
50
+ signature = get_signature([self.token, timestamp, nonce, encrypt])
47
51
  if signature != msg_signature:
48
52
  return "ValidateSignatureError"
49
53
  decrypted = WechatCrypt.decrypt(encrypt, self.key, self.appid)
@@ -0,0 +1,84 @@
1
+ import os
2
+ import os.path as op
3
+ import filecmp
4
+ from python_plugins.random.random_str import rand_sentence
5
+ from python_plugins.crypto.file_to_file import encrypt_txtfile
6
+ from python_plugins.crypto.file_to_file import decrypt_txtfile
7
+ from python_plugins.crypto.file_to_file import encrypt_file
8
+ from python_plugins.crypto.file_to_file import decrypt_file
9
+
10
+
11
+ tmp_path = op.join(os.path.dirname(os.path.abspath(__file__)), "tmp")
12
+
13
+ path_1 = op.join(tmp_path, "test1.txt")
14
+ path_2 = op.join(tmp_path, "test2.txt")
15
+ path_3 = op.join(tmp_path, "test3.txt")
16
+ path_4 = op.join(tmp_path, "test4.txt")
17
+ path_5 = op.join(tmp_path, "test5.txt")
18
+
19
+
20
+ def _create_temp():
21
+ if not op.exists(tmp_path):
22
+ os.mkdir(tmp_path)
23
+ return tmp_path
24
+
25
+
26
+ def safe_delete(path):
27
+ try:
28
+ if op.exists(path):
29
+ os.remove(path)
30
+ except:
31
+ pass
32
+
33
+
34
+ def _remove_testfiles():
35
+ safe_delete(path_1)
36
+ safe_delete(path_2)
37
+ safe_delete(path_3)
38
+ safe_delete(path_4)
39
+ safe_delete(path_5)
40
+
41
+
42
+ def test_crypto_file():
43
+ create_tmp = _create_temp()
44
+ if create_tmp:
45
+ print(create_tmp)
46
+
47
+ prompt = "test"
48
+
49
+ with open(path_1, "w") as f:
50
+ f.write(rand_sentence(30))
51
+ f.write(rand_sentence(30))
52
+
53
+ encrypt_txtfile(path_1, path_2, prompt=prompt)
54
+ decrypt_txtfile(path_2, path_3)
55
+ cmp_result = filecmp.cmp(path_1, path_3)
56
+ assert cmp_result is True
57
+
58
+ encrypt_file(path_1, path_4, prompt=prompt)
59
+ decrypt_file(path_2, path_5)
60
+ cmp_result = filecmp.cmp(path_1, path_5)
61
+ assert cmp_result is True
62
+
63
+ _remove_testfiles()
64
+
65
+ # pytest with `input()` must using `-s`
66
+ # pytest tests\test_crypt_file.py::test_crypto_file_with_password -s
67
+ def _test_crypto_file_with_password():
68
+ prompt = "test"
69
+
70
+ with open(path_1, "w") as f:
71
+ f.write(rand_sentence(30))
72
+ f.write(rand_sentence(30))
73
+
74
+ encrypt_txtfile(path_1, path_2, accept_password=True)
75
+ decrypt_txtfile(path_2, path_3)
76
+ cmp_result = filecmp.cmp(path_1, path_3)
77
+ assert cmp_result is True
78
+
79
+ encrypt_file(path_1, path_4, accept_password=True)
80
+ decrypt_file(path_2, path_5)
81
+ cmp_result = filecmp.cmp(path_1, path_5)
82
+ assert cmp_result is True
83
+
84
+ _remove_testfiles()
@@ -6,3 +6,6 @@ from python_plugins.utils.remove_pycache import remove_pycache
6
6
  def test_remove_pycache():
7
7
  remove_pycache("./tests")
8
8
 
9
+ @pytest.mark.skip
10
+ def test_remove_pycache():
11
+ remove_pycache()
@@ -0,0 +1,132 @@
1
+ import pytest
2
+ import time
3
+ import hashlib
4
+ from python_plugins.weixin.wechat import Wechat
5
+ from python_plugins.weixin.wechat_crypt import get_signature
6
+ from python_plugins.weixin.wechat_crypt import MessageCrypt
7
+ from python_plugins.random import rand_digit, rand_letter
8
+ from python_plugins.convert.xml import xml2dict
9
+
10
+
11
+ test_wechat_app = {
12
+ "name": "test",
13
+ "appid": "wxabcdefghijklmnop",
14
+ "token": "testtest",
15
+ "aeskey": "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
16
+ "appsecret": "abcdefghijklmnopqrstuvwxyz012345",
17
+ }
18
+
19
+ server_id = "gh_1234567890ab"
20
+ openid = "abcdefghijklmnopqrstuvwxyz01"
21
+
22
+
23
+ class MyWechat(Wechat):
24
+ def get_app(self) -> dict:
25
+ return test_wechat_app
26
+
27
+
28
+ class TestWechat:
29
+ def test_verify(self):
30
+ mywechat = MyWechat()
31
+ timestamp = str(int(time.time()))
32
+ nonce = rand_digit(10)
33
+ token = test_wechat_app["token"]
34
+ signature = get_signature([token, timestamp, nonce])
35
+ echostr = rand_digit(18)
36
+ query = {
37
+ "timestamp": timestamp,
38
+ "nonce": nonce,
39
+ "signature": signature,
40
+ "echostr": echostr,
41
+ }
42
+ r = mywechat.verify(query)
43
+ assert r == echostr
44
+
45
+ def test_chat(self):
46
+ """chat(明文)"""
47
+ input_text = rand_letter(10)
48
+ timestamp = str(int(time.time()))
49
+ nonce = rand_digit(10)
50
+
51
+ xml = (
52
+ "<xml>"
53
+ f"<ToUserName><![CDATA[{server_id}]]></ToUserName>"
54
+ f"<FromUserName><![CDATA[{openid}]]></FromUserName>"
55
+ f"<CreateTime>{timestamp}</CreateTime>"
56
+ "<MsgType><![CDATA[text]]></MsgType>"
57
+ f"<Content><![CDATA[{input_text}]]></Content>"
58
+ "<MsgId>23533248665819413</MsgId>"
59
+ "</xml>"
60
+ )
61
+ query = {
62
+ "timestamp": timestamp,
63
+ "nonce": nonce,
64
+ "openid": f"{openid}",
65
+ }
66
+
67
+ mywechat = MyWechat()
68
+ r = mywechat.chat(query, xml)
69
+ data = xml2dict(r)
70
+ # print(data)
71
+ assert data["ToUserName"] == openid
72
+ assert data["FromUserName"] == server_id
73
+ assert data["MsgType"] == "text"
74
+ assert input_text in data["Content"]
75
+
76
+ def test_chat_aes(self):
77
+ """chat(密文)"""
78
+ input_text = rand_letter(10)
79
+ timestamp = str(int(time.time()))
80
+ nonce = rand_digit(10)
81
+ mywechat = MyWechat()
82
+ app = mywechat.app
83
+ mc = MessageCrypt(app["appid"], app["token"], app["aeskey"])
84
+
85
+ xml = (
86
+ "<xml>"
87
+ f"<ToUserName><![CDATA[{server_id}]]></ToUserName>"
88
+ f"<FromUserName><![CDATA[{openid}]]></FromUserName>"
89
+ f"<CreateTime>{timestamp}</CreateTime>"
90
+ "<MsgType><![CDATA[text]]></MsgType>"
91
+ f"<Content><![CDATA[{input_text}]]></Content>"
92
+ "<MsgId>23533248665819413</MsgId>"
93
+ "</xml>"
94
+ )
95
+
96
+ # 获取密文和签名
97
+ encrypt, msg_signature = mc.get_encrypt_sig(xml, timestamp, nonce)
98
+
99
+ xml2 = (
100
+ "<xml>"
101
+ f"<ToUserName><![CDATA[{server_id}]]></ToUserName>"
102
+ f"<FromUserName><![CDATA[{openid}]]></FromUserName>"
103
+ f"<CreateTime>{timestamp}</CreateTime>"
104
+ "<MsgType><![CDATA[text]]></MsgType>"
105
+ f"<Content><![CDATA[{input_text}]]></Content>"
106
+ "<MsgId>6054768590064713728</MsgId>"
107
+ f"<Encrypt><![CDATA[{encrypt}]]></Encrypt>"
108
+ "</xml>"
109
+ )
110
+
111
+ query = {
112
+ "timestamp": timestamp,
113
+ "nonce": nonce,
114
+ "encrypt_type": "aes",
115
+ "msg_signature": msg_signature,
116
+ }
117
+
118
+ r = mywechat.chat(query, xml2)
119
+ # print(r)
120
+ r_dict = xml2dict(r)
121
+ r_xml = mc.decrypt_msg(
122
+ r_dict["TimeStamp"],
123
+ r_dict["Nonce"],
124
+ r_dict["Encrypt"],
125
+ r_dict["MsgSignature"],
126
+ )
127
+ data = xml2dict(r_xml)
128
+ # print(data)
129
+ assert data["ToUserName"] == openid
130
+ assert data["FromUserName"] == server_id
131
+ assert data["MsgType"] == "text"
132
+ assert input_text in data["Content"]
@@ -1,30 +0,0 @@
1
- # Configuration file for the Sphinx documentation builder.
2
- #
3
- # For the full list of built-in configuration values, see the documentation:
4
- # https://www.sphinx-doc.org/en/master/usage/configuration.html
5
-
6
- # -- Project information -----------------------------------------------------
7
- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8
-
9
- project = 'python-plugins'
10
- copyright = '2024, David Hua'
11
- author = 'David Hua'
12
-
13
- # -- General configuration ---------------------------------------------------
14
- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
15
-
16
- extensions = [
17
- 'sphinx.ext.autodoc',
18
- 'sphinx.ext.intersphinx',
19
- 'sphinx.ext.doctest',
20
- 'sphinx.ext.autosummary',
21
- ]
22
-
23
- templates_path = ['_templates']
24
- exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
25
-
26
- # -- Options for HTML output -------------------------------------------------
27
- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
28
-
29
- html_theme = 'classic'
30
- html_title = "python-plugins Documentation"
@@ -1,2 +0,0 @@
1
- # Defining the exact version will make sure things don't break
2
- sphinx
@@ -1 +0,0 @@
1
- __version__ = "0.1.6"
@@ -1 +0,0 @@
1
-
@@ -1,104 +0,0 @@
1
- import pytest
2
- import time
3
- import hashlib
4
- from python_plugins.weixin.wechat import Wechat
5
- from python_plugins.random import rand_digit, rand_letter
6
-
7
-
8
- test_wechat_app = {
9
- "name": "test",
10
- "appid": "wx2c2769f8efd9abc2",
11
- "token": "spamtest",
12
- "aeskey": "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG",
13
- "appsecret": "45658c05647671e481d75b6fa23e6add",
14
- }
15
-
16
-
17
- class MyWechat(Wechat):
18
- def get_app(self) -> dict:
19
- return test_wechat_app
20
-
21
-
22
- class TestWechat:
23
- def test_verify(self):
24
- mywechat = MyWechat()
25
- # timestamp = "1409735669"
26
- timestamp = str(int(time.time()))
27
- # nonce = "1320562132"
28
- nonce = rand_digit(10)
29
- token = test_wechat_app["token"]
30
- signature = hashlib.sha1(
31
- "".join(sorted([token, timestamp, nonce])).encode("utf8")
32
- ).hexdigest()
33
- # echostr = "780419598460648693"
34
- echostr = rand_digit(18)
35
- query = {
36
- "timestamp": timestamp,
37
- "nonce": nonce,
38
- "signature": signature,
39
- "echostr": echostr,
40
- }
41
- r = mywechat.verify(query)
42
- # print(r)
43
- assert r == echostr
44
-
45
- def test_chat(self):
46
- """chat(明文)"""
47
- text = rand_letter(10)
48
- timestamp = str(int(time.time()))
49
- nonce = rand_digit(10)
50
- xml = (
51
- "<xml>"
52
- "<ToUserName><![CDATA[gh_73951532543e]]></ToUserName>"
53
- "<FromUserName><![CDATA[oMIza6tX35aDNfoXr4JGP02QvM08]]></FromUserName>"
54
- "<CreateTime>{timestamp}</CreateTime>"
55
- "<MsgType><![CDATA[text]]></MsgType>"
56
- f"<Content><![CDATA[{text}]]></Content>"
57
- "<MsgId>23533248665819413</MsgId>"
58
- "</xml>"
59
- )
60
- query = {
61
- "timestamp": timestamp,
62
- "nonce": nonce,
63
- "signature": "b5fa0bcde34ab0b6dccd6e23034c26c0cf5ecbaa",
64
- "openid": "oMIza6tX35aDNfoXr4JGP02QvM08",
65
- }
66
-
67
- mywechat = MyWechat()
68
- r = mywechat.chat(query, xml)
69
- # print(r)
70
- assert query["openid"] in r
71
-
72
- def test_chat_aes(self):
73
- """chat(密文),微信提供的demo"""
74
- xml = (
75
- "<xml>"
76
- "<ToUserName><![CDATA[gh_10f6c3c3ac5a]]></ToUserName>"
77
- "<FromUserName><![CDATA[oyORnuP8q7ou2gfYjqLzSIWZf0rs]]></FromUserName>"
78
- "<CreateTime>1409735668</CreateTime>"
79
- "<MsgType><![CDATA[text]]></MsgType>"
80
- "<Content><![CDATA[abcdteT]]></Content>"
81
- "<MsgId>6054768590064713728</MsgId>"
82
- "<Encrypt>"
83
- "<![CDATA[hyzAe4OzmOMbd6TvGdIOO6uBmdJoD0Fk53REIHvxYtJlE2B655HuD0m8KUePW"
84
- "B3+LrPXo87wzQ1QLvbeUgmBM4x6F8PGHQHFVAFmOD2LdJF9FrXpbUAh0B5GIItb52sn896"
85
- "wVsMSHGuPE328HnRGBcrS7C41IzDWyWNlZkyyXwon8T332jisa+h6tEDYsVticbSnyU8dK"
86
- "OIbgU6ux5VTjg3yt+WGzjlpKn6NPhRjpA912xMezR4kw6KWwMrCVKSVCZciVGCgavjIQ6X"
87
- "8tCOp3yZbGpy0VxpAe+77TszTfRd5RJSVO/HTnifJpXgCSUdUue1v6h0EIBYYI1BD1DlD+"
88
- "C0CR8e6OewpusjZ4uBl9FyJvnhvQl+q5rv1ixrcpCumEPo5MJSgM9ehVsNPfUM669WuMyV"
89
- "WQLCzpu9GhglF2PE=]]>"
90
- "</Encrypt>"
91
- "</xml>"
92
- )
93
-
94
- query = {
95
- "timestamp": "1409735669",
96
- "nonce": "1320562132",
97
- "encrypt_type": "aes",
98
- "msg_signature": "5d197aaffba7e9b25a30732f161a50dee96bd5fa",
99
- }
100
-
101
- mywechat = MyWechat()
102
- r = mywechat.chat(query, xml)
103
- # print(r)
104
- assert f'<Nonce><![CDATA[{query["nonce"]}]]></Nonce>' in r