gecko-web-runtime-client 1.4.3__tar.gz → 1.4.4__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 (92) hide show
  1. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/PACKAGE_README.md +2 -2
  2. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/PKG-INFO +3 -3
  3. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/__init__.py +1 -1
  4. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/__init__.py +12 -12
  5. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/__main__.py +23 -23
  6. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_article.py +110 -110
  7. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_diagnostics.py +207 -207
  8. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_diagnostics_round.py +177 -177
  9. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_frame_context.py +61 -61
  10. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_logging.py +18 -18
  11. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_manual_data.py +135 -135
  12. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_slider_image.py +90 -90
  13. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_variant_resources.py +52 -52
  14. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/client.py +87 -87
  15. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/exceptions.py +5 -5
  16. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/__init__.py +1 -1
  17. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/computed-style.js +89 -89
  18. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/drag-control.js +9 -9
  19. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/drag-motion.js +40 -40
  20. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/drag-target.js +25 -25
  21. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/host-timeline.js +43 -43
  22. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/input-events.js +103 -103
  23. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/manual-gesture.js +39 -39
  24. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/native-gl-client.js +130 -130
  25. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/native-webgl.js +66 -66
  26. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/native_gl.py +323 -323
  27. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/native_gl_service.py +168 -168
  28. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/plugin_profile.json +4 -4
  29. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/sdk-handshake.js +21 -21
  30. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/var-speed-drag-control.js +10 -10
  31. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/window-message-bridge.js +32 -32
  32. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/code.js +841 -841
  33. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/package-lock.json +401 -401
  34. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/package.json +10 -10
  35. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/executor.py +215 -215
  36. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/native_gl_service.py +164 -164
  37. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/resources/frame-context-manifest.json +12 -12
  38. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/resources/migration-manifest.json +259 -259
  39. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/resources/var-speed-resource-manifest.json +28 -28
  40. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/runtime.py +116 -116
  41. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/models.py +54 -54
  42. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/transport.py +48 -48
  43. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime_client.egg-info/PKG-INFO +3 -3
  44. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/pyproject.toml +1 -1
  45. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/README.md +0 -0
  46. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/__init__.py +0 -0
  47. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/__main__.py +0 -0
  48. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/_leases.py +0 -0
  49. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/api/__init__.py +0 -0
  50. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/api/context.py +0 -0
  51. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/api/page.py +0 -0
  52. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/api/pool.py +0 -0
  53. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/api/runtime.py +0 -0
  54. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/data_plane.py +0 -0
  55. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/diagnostics/__init__.py +0 -0
  56. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/diagnostics/gc_cc.py +0 -0
  57. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/diagnostics/logging.py +0 -0
  58. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/diagnostics/memory.py +0 -0
  59. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/diagnostics/metrics.py +0 -0
  60. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/diagnostics/resources.py +0 -0
  61. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/http.py +0 -0
  62. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/installer.py +0 -0
  63. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/_flow.py +0 -0
  64. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/capability_profile.json +0 -0
  65. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/ft.module.js +0 -0
  66. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/layout_profile.json +0 -0
  67. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/media_profile.json +0 -0
  68. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/rtproxy.js +0 -0
  69. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/tracker.module.js +0 -0
  70. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/integrations/wechat/js_runtime/environment/assets/webgl_profile.json +0 -0
  71. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/iocp_pipe.py +0 -0
  72. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/media_capture.py +0 -0
  73. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/media_downloader.py +0 -0
  74. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/models/__init__.py +0 -0
  75. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/models/navigation.py +0 -0
  76. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/network/__init__.py +0 -0
  77. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/network/_urls.py +0 -0
  78. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/network/client.py +0 -0
  79. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/network/exceptions.py +0 -0
  80. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/network/models.py +0 -0
  81. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/network/transport.py +0 -0
  82. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/pipe.py +0 -0
  83. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/session.py +0 -0
  84. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/workflows/__init__.py +0 -0
  85. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/workflows/turnstile.py +0 -0
  86. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/workflows/wechat.py +0 -0
  87. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime/workflows/wechat_challenge.py +0 -0
  88. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime_client.egg-info/SOURCES.txt +0 -0
  89. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime_client.egg-info/dependency_links.txt +0 -0
  90. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime_client.egg-info/requires.txt +0 -0
  91. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/gecko_web_runtime_client.egg-info/top_level.txt +0 -0
  92. {gecko_web_runtime_client-1.4.3 → gecko_web_runtime_client-1.4.4}/setup.cfg +0 -0
@@ -19,11 +19,11 @@ HTTP imports are retained through `wpr-http`.
19
19
 
20
20
  The same distribution includes `gecko_web_runtime.integrations.wechat`.
21
21
  It uses `wpr-http` and a local Node.js environment, with no browser Page.
22
- Install version 1.4.3 or newer with its optional dependencies, then prepare Node.js
22
+ Install version 1.4.4 or newer with its optional dependencies, then prepare Node.js
23
23
  20+ dependencies in the user's cache:
24
24
 
25
25
  ```powershell
26
- pip install --upgrade "gecko-web-runtime-client[wechat]>=1.4.3"
26
+ pip install --upgrade "gecko-web-runtime-client[wechat]>=1.4.4"
27
27
  python -m gecko_web_runtime.integrations.wechat install-js
28
28
  ```
29
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gecko-web-runtime-client
3
- Version: 1.4.3
3
+ Version: 1.4.4
4
4
  Summary: Thin Python client for the Firefox-derived WPR Gecko Worker protocol
5
5
  Author: WPR project
6
6
  License: MPL-2.0-compatible client; see the runtime bundle notices
@@ -40,11 +40,11 @@ HTTP imports are retained through `wpr-http`.
40
40
 
41
41
  The same distribution includes `gecko_web_runtime.integrations.wechat`.
42
42
  It uses `wpr-http` and a local Node.js environment, with no browser Page.
43
- Install version 1.4.3 or newer with its optional dependencies, then prepare Node.js
43
+ Install version 1.4.4 or newer with its optional dependencies, then prepare Node.js
44
44
  20+ dependencies in the user's cache:
45
45
 
46
46
  ```powershell
47
- pip install --upgrade "gecko-web-runtime-client[wechat]>=1.4.3"
47
+ pip install --upgrade "gecko-web-runtime-client[wechat]>=1.4.4"
48
48
  python -m gecko_web_runtime.integrations.wechat install-js
49
49
  ```
50
50
 
@@ -1 +1 @@
1
- """Optional site integrations shipped with the browser client."""
1
+ """Optional site integrations shipped with the browser client."""
@@ -1,12 +1,12 @@
1
- """HTTP and local-JavaScript WeChat article client."""
2
-
3
- from .client import AsyncWechatClient
4
- from .exceptions import WechatDependencyError
5
- from .models import WechatArticle, WechatConfig
6
-
7
- __all__ = [
8
- "AsyncWechatClient",
9
- "WechatArticle",
10
- "WechatConfig",
11
- "WechatDependencyError",
12
- ]
1
+ """HTTP and local-JavaScript WeChat article client."""
2
+
3
+ from .client import AsyncWechatClient
4
+ from .exceptions import WechatDependencyError
5
+ from .models import WechatArticle, WechatConfig
6
+
7
+ __all__ = [
8
+ "AsyncWechatClient",
9
+ "WechatArticle",
10
+ "WechatConfig",
11
+ "WechatDependencyError",
12
+ ]
@@ -1,23 +1,23 @@
1
- """Install optional Node dependencies into the user's WPR cache."""
2
-
3
- import argparse
4
-
5
- from .js_runtime.runtime import LocalJsRuntime
6
- from .models import WechatConfig
7
-
8
-
9
- def main():
10
- parser = argparse.ArgumentParser()
11
- parser.add_argument("command", choices=["install-js"])
12
- parser.add_argument("--cache-dir")
13
- parser.add_argument("--node", default="node")
14
- args = parser.parse_args()
15
- runtime = LocalJsRuntime(
16
- WechatConfig(js_cache_dir=args.cache_dir, node_executable=args.node)
17
- )
18
- print(runtime.install())
19
- return 0
20
-
21
-
22
- if __name__ == "__main__":
23
- raise SystemExit(main())
1
+ """Install optional Node dependencies into the user's WPR cache."""
2
+
3
+ import argparse
4
+
5
+ from .js_runtime.runtime import LocalJsRuntime
6
+ from .models import WechatConfig
7
+
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser()
11
+ parser.add_argument("command", choices=["install-js"])
12
+ parser.add_argument("--cache-dir")
13
+ parser.add_argument("--node", default="node")
14
+ args = parser.parse_args()
15
+ runtime = LocalJsRuntime(
16
+ WechatConfig(js_cache_dir=args.cache_dir, node_executable=args.node)
17
+ )
18
+ print(runtime.install())
19
+ return 0
20
+
21
+
22
+ if __name__ == "__main__":
23
+ raise SystemExit(main())
@@ -1,110 +1,110 @@
1
- """Article parsing without a Node dependency or a hard-coded article ID."""
2
-
3
- from html.parser import HTMLParser
4
- import hashlib
5
- import re
6
- from urllib.parse import parse_qs, urlsplit
7
-
8
-
9
- class ArticleParser(HTMLParser):
10
- def __init__(self):
11
- super().__init__(convert_charrefs=True)
12
- self.stack = []
13
- self.title = []
14
- self.content = []
15
- self.media = []
16
- self.body_found = False
17
-
18
- def handle_starttag(self, tag, attrs):
19
- values = dict(attrs)
20
- title = values.get("id") == "activity-name" or any(x[1] for x in self.stack)
21
- body = values.get("id") == "js_content" or any(x[2] for x in self.stack)
22
- self.body_found |= values.get("id") == "js_content"
23
- skip = tag in {"script", "style"} or any(x[3] for x in self.stack)
24
- if body and tag in {
25
- "img",
26
- "iframe",
27
- "video",
28
- "mp-video",
29
- "mp-common-videosnap",
30
- }:
31
- self.media.append(
32
- {
33
- "tag": tag,
34
- "src": values.get("data-src") or values.get("src", ""),
35
- "vid": values.get("vid") or values.get("data-vid", ""),
36
- }
37
- )
38
- if body and tag in {"p", "section", "br", "h1", "h2"}:
39
- self.content.append("\n")
40
- if tag not in {
41
- "area",
42
- "base",
43
- "br",
44
- "col",
45
- "embed",
46
- "hr",
47
- "img",
48
- "input",
49
- "link",
50
- "meta",
51
- "param",
52
- "source",
53
- "track",
54
- "wbr",
55
- }:
56
- self.stack.append((tag, title, body, skip))
57
-
58
- def handle_startendtag(self, tag, attrs):
59
- self.handle_starttag(tag, attrs)
60
- self.handle_endtag(tag)
61
-
62
- def handle_endtag(self, tag):
63
- for index in range(len(self.stack) - 1, -1, -1):
64
- if self.stack[index][0] == tag:
65
- if self.stack[index][2] and tag in {"p", "section", "h1", "h2"}:
66
- self.content.append("\n")
67
- del self.stack[index:]
68
- break
69
-
70
- def handle_data(self, data):
71
- if not self.stack or self.stack[-1][3]:
72
- return
73
- if self.stack[-1][1]:
74
- self.title.append(data)
75
- if self.stack[-1][2]:
76
- self.content.append(data)
77
-
78
-
79
- def parse_article(html, url):
80
- parser = ArticleParser()
81
- parser.feed(html)
82
- title = " ".join("".join(parser.title).split())
83
- text = re.sub(
84
- r"\n\s*\n+", "\n\n", re.sub(r"[ \t]+", " ", "".join(parser.content))
85
- ).strip()
86
- query = parse_qs(urlsplit(url).query)
87
- identity = {
88
- f"target_{key}_matches": query[key][0] in html if query.get(key) else None
89
- for key in ("mid", "sn")
90
- }
91
- verified = (
92
- bool(title)
93
- and parser.body_found
94
- and (len(text) > 100 or bool(parser.media))
95
- and not re.search(r"register_code\s*:", html)
96
- and all(value is not False for value in identity.values())
97
- )
98
- proof = {
99
- "title": title,
100
- "content_chars": len(text),
101
- "media_count": len(parser.media),
102
- "media": parser.media,
103
- "html_bytes": len(html.encode("utf-8")),
104
- "html_sha256": hashlib.sha256(html.encode("utf-8")).hexdigest(),
105
- "article_verified": verified,
106
- **identity,
107
- }
108
- if not verified:
109
- raise ValueError("Article title/body/target identity verification failed")
110
- return proof, text
1
+ """Article parsing without a Node dependency or a hard-coded article ID."""
2
+
3
+ from html.parser import HTMLParser
4
+ import hashlib
5
+ import re
6
+ from urllib.parse import parse_qs, urlsplit
7
+
8
+
9
+ class ArticleParser(HTMLParser):
10
+ def __init__(self):
11
+ super().__init__(convert_charrefs=True)
12
+ self.stack = []
13
+ self.title = []
14
+ self.content = []
15
+ self.media = []
16
+ self.body_found = False
17
+
18
+ def handle_starttag(self, tag, attrs):
19
+ values = dict(attrs)
20
+ title = values.get("id") == "activity-name" or any(x[1] for x in self.stack)
21
+ body = values.get("id") == "js_content" or any(x[2] for x in self.stack)
22
+ self.body_found |= values.get("id") == "js_content"
23
+ skip = tag in {"script", "style"} or any(x[3] for x in self.stack)
24
+ if body and tag in {
25
+ "img",
26
+ "iframe",
27
+ "video",
28
+ "mp-video",
29
+ "mp-common-videosnap",
30
+ }:
31
+ self.media.append(
32
+ {
33
+ "tag": tag,
34
+ "src": values.get("data-src") or values.get("src", ""),
35
+ "vid": values.get("vid") or values.get("data-vid", ""),
36
+ }
37
+ )
38
+ if body and tag in {"p", "section", "br", "h1", "h2"}:
39
+ self.content.append("\n")
40
+ if tag not in {
41
+ "area",
42
+ "base",
43
+ "br",
44
+ "col",
45
+ "embed",
46
+ "hr",
47
+ "img",
48
+ "input",
49
+ "link",
50
+ "meta",
51
+ "param",
52
+ "source",
53
+ "track",
54
+ "wbr",
55
+ }:
56
+ self.stack.append((tag, title, body, skip))
57
+
58
+ def handle_startendtag(self, tag, attrs):
59
+ self.handle_starttag(tag, attrs)
60
+ self.handle_endtag(tag)
61
+
62
+ def handle_endtag(self, tag):
63
+ for index in range(len(self.stack) - 1, -1, -1):
64
+ if self.stack[index][0] == tag:
65
+ if self.stack[index][2] and tag in {"p", "section", "h1", "h2"}:
66
+ self.content.append("\n")
67
+ del self.stack[index:]
68
+ break
69
+
70
+ def handle_data(self, data):
71
+ if not self.stack or self.stack[-1][3]:
72
+ return
73
+ if self.stack[-1][1]:
74
+ self.title.append(data)
75
+ if self.stack[-1][2]:
76
+ self.content.append(data)
77
+
78
+
79
+ def parse_article(html, url):
80
+ parser = ArticleParser()
81
+ parser.feed(html)
82
+ title = " ".join("".join(parser.title).split())
83
+ text = re.sub(
84
+ r"\n\s*\n+", "\n\n", re.sub(r"[ \t]+", " ", "".join(parser.content))
85
+ ).strip()
86
+ query = parse_qs(urlsplit(url).query)
87
+ identity = {
88
+ f"target_{key}_matches": query[key][0] in html if query.get(key) else None
89
+ for key in ("mid", "sn")
90
+ }
91
+ verified = (
92
+ bool(title)
93
+ and parser.body_found
94
+ and (len(text) > 100 or bool(parser.media))
95
+ and not re.search(r"register_code\s*:", html)
96
+ and all(value is not False for value in identity.values())
97
+ )
98
+ proof = {
99
+ "title": title,
100
+ "content_chars": len(text),
101
+ "media_count": len(parser.media),
102
+ "media": parser.media,
103
+ "html_bytes": len(html.encode("utf-8")),
104
+ "html_sha256": hashlib.sha256(html.encode("utf-8")).hexdigest(),
105
+ "article_verified": verified,
106
+ **identity,
107
+ }
108
+ if not verified:
109
+ raise ValueError("Article title/body/target identity verification failed")
110
+ return proof, text