
Wechat Article Extractor
- 1 installs
- 6 repo stars
- Updated February 19, 2026
- freestylefly/third-image-skill
This is a copy of wechat-article-extractor by freestylefly - installs and ranking accrue to the original listing.
Helps with ai & agent building tasks.
About
wechat-article-extractor is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- wechat-article-extractor
- AI & Agent Building
- AI-coding skill
Wechat Article Extractor by the numbers
- 1 all-time installs (skills.sh)
- Data as of Jul 24, 2026 (Skillselion catalog sync)
npx skills add https://github.com/freestylefly/third-image-skill --skill wechat-article-extractorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 6 |
| Last updated | February 19, 2026 |
| Repository | freestylefly/third-image-skill ↗ |
What it does
Helps with ai & agent building tasks.
Files
Canghe Image Generation
Generate images using the Canghe AI API with gemini-3-pro-image-preview model.
⚠️ 需要 API Key
>
使用本 skill 前,请先访问 https://api.canghe.ai/ 注册并获取 API Key。
获取后通过环境变量CANGHE_API_KEY或--api-key参数传入。
API Information
- Endpoint:
https://api.canghe.ai/v1/chat/completions - Model:
gemini-3-pro-image-preview - Method: POST
Request Format
{
"model": "gemini-3-pro-image-preview",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Image generation prompt"
},
{
"type": "image_url",
"image_url": {
"url": "reference image URL (optional)"
}
}
]
}
],
"max_tokens": 4096
}Usage
Text-to-Image
Generate image from text prompt only:
python3 scripts/generate_image.py "A beautiful sunset over mountains"Image-to-Image (with reference)
Generate image using a reference image:
python3 scripts/generate_image.py "Transform this into oil painting style" --reference /path/to/image.jpgOr with URL:
python3 scripts/generate_image.py "Add cyberpunk elements" --reference-url "https://example.com/image.jpg"Programmatic Usage
from scripts.generate_image import generate_image
# Text-to-image
result = generate_image("A cute cat wearing a hat")
print(result["image_url"])
# With reference image
result = generate_image("Make this watercolor style", reference_image_path="/path/to/image.jpg")
print(result["image_url"])Authentication
获取 API Key: 访问 https://api.canghe.ai/ 注册账号并获取 API Key。
设置 API Key 的方式:
export CANGHE_API_KEY="your-api-key-here"Or pass it as a command-line argument:
python3 scripts/generate_image.py "prompt" --api-key "your-api-key"Output
The API returns an image URL that can be downloaded or displayed. The script will: 1. Save the generated image to the specified output path 2. Print the image URL 3. Return metadata about the generation
Bud1ptslg1Scoscriptslg1ScompS�scriptsmoDDblob�8X����AscriptsmodDblob�8X����Ascriptsph1Scomp` @� @� @� @EDSDB `� @� @� @PK.�S\`�p�
canghe-image-gen/SKILL.md�V_kG�O1�_l�;5M)T�� �k��&�)%i}��.�ۻ��Y��M�@j�!u��Ph�8���'�.mN���:���t���/�����o�7ֲ,�����!�Z���*���.�"酬��QN$�/ ��Um�s0�0�Ux̳.[{�"N�<ڀ t�?aí8�B.H�.-�S�j������3w�����S�dhlX5��@D���P��`:���.'�����������#\tl�u��/j��xk�)xR��g��O�6,��1�I��l��0Ύ�D�|�u�+ڶa\�B�ァ'����f�����m
��}����y�ɯ ��C���Ç���������P�2�b�D����M�"��|�0y�gw� i�f~�ټ�nSY�h�xs�{���y���E���x����7f?�)�A��̧ho�BŲпU��
$�{�o~{s�4���rg��i�sl5�A/P^l��Q�1Y(��r6̵KE�Fd� �ȧ�TT��u mx~{�T�BW�.��Z�hn"�TH�P�1�J�rG ����Co�T��ԊTE2���5�]���TyP��u�� ��L�ՇM��lF�LUV�,�QܙMW�D���nL�ݵ�F9�~��@\���V*4G*�n~��-ğ0�l6�ѿe#[-��#�e�)S�{��76��%R�7���/=ZZ��7�=���,i.W��Qig�!m���u��6��&�ӰBI,���3A��v� ��$��`����G��66q
^Z�d��]�"'L���� �R !�|��T��lb��e
�)FD֊2,�n�D���<m:H����4W(�bV���X��0I��*��U�6���P(xX�$P��������y����p0�j�@^�����,28�/ajDq wbd�!��!l/��J������ayB��䌡u�1�I���5Pʝ��i�R59�j�]S�i�ގ)+��g��k��B������@��gI���i`t�_w^�g"��}��}��N��D�55��0e6Øg������~6FD`JK��H�J����«�J���i�BA��~�|,�����x)Ne��`C�MbZ��p��C�RW=\OD>iR��!E����%�����?��{����:ު�|i��7�,�m��Ga\�qV)tPI\" ��>@��/PK�S\�0�2�
H*canghe-image-gen/scripts/generate_image.py�Yms۸��_��?����$�ɴ�3�ع��8�i���p`��h[��ww� �8�^�_D�.������F���,碼c��l��� ��5/�����Y�ׂ�,J���U�>�J�&>5u])���]�33�L��u�����5�K�aJ��e*m��@H�q���Ң}��Z�zپ}���=W�}�[�TU���M.o���^��ֲ\��?�x�۵F�p&&���fo�]�܊�n�6:��
��O����W+Y�(�_��X(��Lj�&��bvVj��Z��5��K{.�Q�0�D�'A<KN�O>~8;�dKn���b>経S2c�����<�p3O���J�������wxf-
Y�ُ�Z9C�����ldb��BU&ZKL�XKD�5�`ڨ ��
��`�S:fM�V2�TΊ,ㆳ��b�8�'�jQzd�,T7�q�V��U?��%[�J�,]��;F(�G��g�O�ڂXf����*J���'�
��{x|'�nV+��սP�U|!�i8���&����p�B:>�R�u8������[�q-W�"������W��_�)�U|����, J/W�&�L�u��vk�n��W/�oD�Q&q&�Sؘ����m�0���e��1��Ւ�>zv��I�@�E�T����TU�y��Ho�\1�Ê�;��l�_]����&G÷��XL�t_����u�����q)L^���F���ox���dq�υQ�2D�!��y n�[�]t��
����U)�N�.gZBOm-���-��� ����_^MRb&S�)�~]�o#�Ѩξ@���/���em��ܴ'�<o���|@���@5ԩ17�>>���i�I��1�Kw\�F��q�Gu�U
mϢʩ�'��=�E%���
NyՋ�\�=aO@kH�wZ��C�%�H�QCr�:�A��0� ��wRU%E]8�
8uY�Ni�����7��Kԭ�V���I���\�,�MN����l6�3 ��.����<#�Y���J�eɮCoLARJ8u�����q����mw2Y�����rХ�і�b���`�n���' \G�� �\X��B)���Do���W�MՔ���*�Ɋ�W�a�Gtw��"|�&�ot�mb^C�͢�>�p��uB��}��F� x�6����{�txe�Zq
K�1���F�
/������?ۅ2l�_�:G?B�*�kKlv��eJQ?GX����p����Uؼ
\ �]T���#�*�m2 帤m���sq]i�n�^w�~��r�N�Sf�jV�Y>���Ò�.�F'��~X�Ϟ�3�ES"���K�W�.�On�f�����u�xaDcT��ě��dK!��<O��<":nj�ˌi~GH�\Wޏ�[X��_�VM�E���fdqH�E�;}0���'ѭZ�LG0����@���>��uR{�䠑G��A����J�C�=O�
W��ճ�ށ�| �*�X�F_�mw�]�ا�n�p���Dj*��6'�&�ўq'�Ka�)nad_��R5���\R�ҫs���@&��oV�f�<.�h�kߤ�gQ+��y�M���s
��d��F�.�yj�
�M%S�y�WϮ��^�35bs�f!B�
A���%��������pNe�� �����zI�%T���R��@���a��裺%2,V�+�mx�r}�=��r�"�`��ʙ������ݞ��9�s��U�-��ތ�����C�gzp��*�հL���>�i�z_4����.����=Wp��W�٪S��J�0�p�e�R�%�o1:�h?
R$ �@ �V])y(F��co�5L,�El�%����=O��(:f������
���Й4�g?�J��3�ir���}�>��x�:�*Is��e��ߟ�dߊ�~�n�O�Z��z��������������ܨM��-`v#xc�ən L�������i��Q�<��9��CPA�ߣVB����S��ݾ�
�}���n�[p�-@<���l;�VEPy�9�T�������\ɔ�{��Zڀ��!?W��}��Vp��c��Y�%W�]1ϲ�;��B۽���z��wn?D�g�)S�LuT�����*�X�>�������\�5c̈��K�>�V�ī�x}�!�К�(`��٫Yk�웬®W%��n����_�/�FkA�1s�Ɣ�KI���zI��x4Y�%�M�g-~�xj3�����Wp���~FBߑ�0v@�� 3}hj:2};q�qe3���M������>���U�z�)�k'H�D�
�����ߧ��R����[�C��T�'�Yz���]h7���Q7��3��7��ָ��l��a���i#����f�¹�E+,|��~BhYSԺ��S������t�� ש�KhNQ -���LB�ķ=��n��U�>��[?���m��z���ÿ��l�pu&�qE����<n 56�ݹ��RF=�7j���L}�=�X��EH, OO|<(��b�C�t4R<�?V�~��u���k��_1a���+7
xVE�ro�C4�ѿ<�WE����U����������p8�����؛��1���[T��xot����Ϩ���TL��Bm2��ɹ�_C�I?B�#���?��)� �:L�s) j1
���BIR�B$ [.Y�$�{$Ih9�F$�/PK.�S\`�p�
��canghe-image-gen/SKILL.mdPK�S\�0�2�
H*���canghe-image-gen/scripts/generate_image.pyPK��Canghe Image Gen
Claude Code Skill for generating images using the Canghe AI API with the gemini-3-pro-image-preview model.
Features
- Text-to-Image: Generate images from text prompts
- Image-to-Image: Use reference images (local files or URLs) for style transfer and modifications
- Easy Integration: Works as a Claude Code skill or standalone Python script
- Flexible Authentication: Support for environment variables or command-line arguments
Prerequisites
- Python 3.7+
requestslibrary- Canghe AI API Key
Installation
1. Get API Key
Visit https://api.canghe.ai/ to register and obtain your API key.
2. Install as Claude Skill
# Copy the skill file to your Claude skills directory
cp canghe-image-gen.skill ~/.claude/skills/3. Install Dependencies
pip install requestsUsage
Command Line
Text-to-Image
export CANGHE_API_KEY="your-api-key"
python3 scripts/generate_image.py "A beautiful sunset over mountains"Image-to-Image (Local File)
python3 scripts/generate_image.py "Transform this into oil painting style" \
--reference /path/to/input.jpg \
--output ./output/painting.pngImage-to-Image (URL)
python3 scripts/generate_image.py "Add cyberpunk elements" \
--reference-url "https://example.com/image.jpg" \
--output ./output/cyberpunk.pngPython API
from scripts.generate_image import generate_image
# Text-to-image
result = generate_image(
prompt="A cute cat wearing a hat",
api_key="your-api-key"
)
print(result["image_url"])
# With reference image (file path)
result = generate_image(
prompt="Make this watercolor style",
api_key="your-api-key",
reference_image="/path/to/image.jpg"
)
# With reference image (URL)
result = generate_image(
prompt="Transform to anime style",
api_key="your-api-key",
reference_image="https://example.com/photo.jpg"
)Command Line Options
| Option | Short | Description |
|---|---|---|
prompt | - | The image generation prompt (required) |
--reference | -r | Path to reference image file |
--reference-url | -u | URL of reference image |
--output | -o | Output file path |
--api-key | -k | API key (or set CANGHE_API_KEY env var) |
--max-tokens | - | Maximum tokens (default: 4096) |
--raw | - | Print raw API response |
API Reference
- Endpoint:
https://api.canghe.ai/v1/chat/completions - Model:
gemini-3-pro-image-preview - Method: POST
Request Format
{
"model": "gemini-3-pro-image-preview",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Image generation prompt"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,..."
}
}
]
}
],
"max_tokens": 4096
}Project Structure
canghe-image-gen/
├── SKILL.md # Skill definition for Claude Code
├── README.md # This file
├── scripts/
│ └── generate_image.py # Main generation script
└── canghe-image-gen.skill # Packaged skill fileEnvironment Variables
| Variable | Description |
|---|---|
CANGHE_API_KEY | Your Canghe AI API key |
License
MIT License
Links
#!/usr/bin/env python3
"""
Canghe AI Image Generation Script
Supports:
- Text-to-image generation
- Image-to-image generation (with reference image)
"""
import argparse
import base64
import json
import os
import sys
from pathlib import Path
from typing import Optional
from urllib.parse import urlparse
try:
import requests
except ImportError:
print("Error: requests library is required. Install with: pip install requests")
sys.exit(1)
API_ENDPOINT = "https://api.canghe.ai/v1/chat/completions"
MODEL = "gemini-3-pro-image-preview"
def encode_image_to_base64(image_path: str) -> str:
"""Encode image file to base64 data URL."""
with open(image_path, "rb") as f:
image_data = f.read()
# Determine MIME type based on file extension
ext = Path(image_path).suffix.lower()
mime_types = {
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".gif": "image/gif",
".webp": "image/webp",
".bmp": "image/bmp",
}
mime_type = mime_types.get(ext, "image/jpeg")
base64_data = base64.b64encode(image_data).decode("utf-8")
return f"data:{mime_type};base64,{base64_data}"
def is_url(string: str) -> bool:
"""Check if string is a valid URL."""
try:
result = urlparse(string)
return all([result.scheme, result.netloc])
except:
return False
def generate_image(
prompt: str,
api_key: Optional[str] = None,
reference_image: Optional[str] = None,
max_tokens: int = 4096,
) -> dict:
"""
Generate image using Canghe AI API.
Args:
prompt: The image generation prompt
api_key: API key (defaults to CANGHE_API_KEY env var)
reference_image: Path to reference image or URL (optional)
max_tokens: Maximum tokens for generation
Returns:
Dictionary containing the API response
"""
api_key = api_key or os.environ.get("CANGHE_API_KEY")
if not api_key:
raise ValueError(
"API key is required. Set CANGHE_API_KEY environment variable or pass --api-key."
)
# Build content array
content = [{"type": "text", "text": prompt}]
# Add reference image if provided
if reference_image:
if is_url(reference_image):
image_url = reference_image
else:
if not os.path.exists(reference_image):
raise FileNotFoundError(f"Reference image not found: {reference_image}")
image_url = encode_image_to_base64(reference_image)
content.append({"type": "image_url", "image_url": {"url": image_url}})
# Build request payload
payload = {
"model": MODEL,
"messages": [{"role": "user", "content": content}],
"max_tokens": max_tokens,
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
}
# Make API request
response = requests.post(API_ENDPOINT, headers=headers, json=payload, timeout=120)
if response.status_code != 200:
raise RuntimeError(f"API request failed: {response.status_code} - {response.text}")
return response.json()
def download_image(url: str, output_path: str) -> str:
"""Download image from URL and save to file."""
# Handle base64 data URLs
if url.startswith("data:"):
# Extract base64 data
header, base64_data = url.split(",", 1)
image_data = base64.b64decode(base64_data)
else:
# Download from URL
response = requests.get(url, timeout=60)
response.raise_for_status()
image_data = response.content
# Ensure output directory exists
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
with open(output_path, "wb") as f:
f.write(image_data)
return output_path
def extract_image_url(response: dict) -> Optional[str]:
"""Extract image URL from API response."""
try:
message = response["choices"][0]["message"]
content = message.get("content", "")
# Try to parse as JSON if it's a string
if isinstance(content, str):
try:
content = json.loads(content)
except json.JSONDecodeError:
pass
# Handle different response formats
if isinstance(content, dict):
# Look for image URL in common fields
for key in ["image_url", "url", "image", "img_url"]:
if key in content:
return content[key]
# If content is a string and looks like a URL, return it
if isinstance(content, str) and (content.startswith("http") or content.startswith("data:")):
return content
return None
except (KeyError, IndexError):
return None
def main():
parser = argparse.ArgumentParser(
description="Generate images using Canghe AI API",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Text-to-image
python3 generate_image.py "A beautiful sunset"
# With reference image (file)
python3 generate_image.py "Make this oil painting" --reference input.jpg
# With reference image (URL)
python3 generate_image.py "Add cyberpunk style" --reference-url https://example.com/img.jpg
# Save to specific path
python3 generate_image.py "A cute cat" --output ./output/my_cat.png
""",
)
parser.add_argument("prompt", help="Image generation prompt")
parser.add_argument(
"--reference", "-r", help="Path to reference image file (for image-to-image)"
)
parser.add_argument(
"--reference-url", "-u", help="URL of reference image (for image-to-image)"
)
parser.add_argument(
"--output", "-o", help="Output file path (default: auto-generated)"
)
parser.add_argument("--api-key", "-k", help="API key (or set CANGHE_API_KEY env var)")
parser.add_argument(
"--max-tokens", type=int, default=4096, help="Maximum tokens (default: 4096)"
)
parser.add_argument(
"--raw", action="store_true", help="Print raw API response"
)
args = parser.parse_args()
# Determine reference image
ref_image = args.reference or args.reference_url
try:
print(f"Generating image with prompt: {args.prompt}")
if ref_image:
print(f"Using reference image: {ref_image}")
response = generate_image(
prompt=args.prompt,
api_key=args.api_key,
reference_image=ref_image,
max_tokens=args.max_tokens,
)
if args.raw:
print("\n--- Raw API Response ---")
print(json.dumps(response, indent=2, ensure_ascii=False))
print("---")
# Extract and display image URL
image_url = extract_image_url(response)
if image_url:
print(f"\nGenerated image URL: {image_url}")
# Download and save if output path specified
if args.output:
saved_path = download_image(image_url, args.output)
print(f"Image saved to: {saved_path}")
elif args.output is None and not args.raw:
# Auto-generate output path
output_path = f"generated_image_{os.urandom(4).hex()}.png"
saved_path = download_image(image_url, output_path)
print(f"Image saved to: {saved_path}")
else:
print("\nNo image URL found in response. Full response:")
print(json.dumps(response, indent=2, ensure_ascii=False))
except FileNotFoundError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
except ValueError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
except RuntimeError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Unexpected error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
{
"name": "wechat-article-extractor",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"cheerio": "^1.2.0",
"dayjs": "^1.11.19",
"lodash.unescape": "^4.0.1",
"qs": "^6.15.0",
"request-promise": "^4.2.6"
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"safer-buffer": "~2.1.0"
}
},
"node_modules/assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT",
"peer": true
},
"node_modules/aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": "*"
}
},
"node_modules/aws4": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
"license": "MIT",
"peer": true
},
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
"tweetnacl": "^0.14.3"
}
},
"node_modules/bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"license": "MIT"
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC"
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"license": "Apache-2.0",
"peer": true
},
"node_modules/cheerio": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz",
"integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==",
"license": "MIT",
"dependencies": {
"cheerio-select": "^2.1.0",
"dom-serializer": "^2.0.0",
"domhandler": "^5.0.3",
"domutils": "^3.2.2",
"encoding-sniffer": "^0.2.1",
"htmlparser2": "^10.1.0",
"parse5": "^7.3.0",
"parse5-htmlparser2-tree-adapter": "^7.1.0",
"parse5-parser-stream": "^7.1.2",
"undici": "^7.19.0",
"whatwg-mimetype": "^4.0.0"
},
"engines": {
"node": ">=20.18.1"
},
"funding": {
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
}
},
"node_modules/cheerio-select": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-select": "^5.1.0",
"css-what": "^6.1.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"peer": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"license": "MIT",
"peer": true
},
"node_modules/css-select": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-what": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
"license": "MIT",
"peer": true,
"dependencies": {
"assert-plus": "^1.0.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/dayjs": {
"version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
"license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause"
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
"license": "MIT",
"peer": true,
"dependencies": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"node_modules/encoding-sniffer": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
"integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==",
"license": "MIT",
"dependencies": {
"iconv-lite": "^0.6.3",
"whatwg-encoding": "^3.1.1"
},
"funding": {
"url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT",
"peer": true
},
"node_modules/extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
"engines": [
"node >=0.6.0"
],
"license": "MIT",
"peer": true
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT",
"peer": true
},
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"license": "MIT",
"peer": true
},
"node_modules/forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": "*"
}
},
"node_modules/form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 0.12"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
"license": "MIT",
"peer": true,
"dependencies": {
"assert-plus": "^1.0.0"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=4"
}
},
"node_modules/har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"deprecated": "this library is no longer supported",
"license": "MIT",
"peer": true,
"dependencies": {
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/htmlparser2": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
"integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.2.2",
"entities": "^7.0.1"
}
},
"node_modules/htmlparser2/node_modules/entities": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
},
"engines": {
"node": ">=0.8",
"npm": ">=1.3.7"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"license": "MIT",
"peer": true
},
"node_modules/isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
"license": "MIT",
"peer": true
},
"node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"license": "MIT",
"peer": true
},
"node_modules/json-schema": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"license": "(AFL-2.1 OR BSD-3-Clause)",
"peer": true
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"license": "MIT",
"peer": true
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"license": "ISC",
"peer": true
},
"node_modules/jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"license": "MIT",
"peer": true,
"dependencies": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.4.0",
"verror": "1.10.0"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/lodash": {
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT"
},
"node_modules/lodash.unescape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
"integrity": "sha512-DhhGRshNS1aX6s5YdBE3njCCouPgnG29ebyHvImlZzXZf2SHgt+J08DHgytTPnpywNbO1Y8mNUFyQuIDBq2JZg==",
"license": "MIT"
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"peer": true,
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": "*"
}
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
"integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
"license": "MIT",
"dependencies": {
"domhandler": "^5.0.3",
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-parser-stream": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
"integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
"license": "MIT",
"dependencies": {
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5/node_modules/entities": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"license": "MIT",
"peer": true
},
"node_modules/psl": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"funding": {
"url": "https://github.com/sponsors/lupomontero"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/request-promise": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz",
"integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==",
"deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142",
"license": "ISC",
"dependencies": {
"bluebird": "^3.5.0",
"request-promise-core": "1.1.4",
"stealthy-require": "^1.1.1",
"tough-cookie": "^2.3.3"
},
"engines": {
"node": ">=0.10.0"
},
"peerDependencies": {
"request": "^2.34"
}
},
"node_modules/request-promise-core": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.19"
},
"engines": {
"node": ">=0.10.0"
},
"peerDependencies": {
"request": "^2.34"
}
},
"node_modules/request/node_modules/qs": {
"version": "6.5.5",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz",
"integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==",
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.6"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"peer": true
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sshpk": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
"integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
"bin": {
"sshpk-conv": "bin/sshpk-conv",
"sshpk-sign": "bin/sshpk-sign",
"sshpk-verify": "bin/sshpk-verify"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==",
"license": "ISC",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"license": "BSD-3-Clause",
"dependencies": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"license": "Unlicense",
"peer": true
},
"node_modules/undici": {
"version": "7.22.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz",
"integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"punycode": "^2.1.0"
}
},
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"license": "MIT",
"peer": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"engines": [
"node >=0.6.0"
],
"license": "MIT",
"peer": true,
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
"license": "MIT",
"dependencies": {
"iconv-lite": "0.6.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"license": "MIT",
"engines": {
"node": ">=18"
}
}
}
}
{
"dependencies": {
"cheerio": "^1.2.0",
"dayjs": "^1.11.19",
"lodash.unescape": "^4.0.1",
"qs": "^6.15.0",
"request-promise": "^4.2.6"
}
}
WeChat Article Extractor
 
一个 Claude Code Skill,用于提取微信公众号文章的元数据和内容。支持多种文章类型,包括图文、视频、图片集、语音和转载文章。当用户需要提供微信公众号文章链接(mp.weixin.qq.com)时,Claude 会自动触发此 Skill 来提取文章信息。
功能特性
- 解析微信公众号文章 URL (
mp.weixin.qq.com) - 提取文章元数据:标题、作者、摘要、发布时间
- 获取公众号信息:名称、头像、微信号、功能介绍
- 提取文章内容(HTML 格式)
- 获取封面图片 URL
- 支持多种文章类型:图文、视频、图片集、语音、纯文字、转载
- 处理各种异常情况:内容删除、链接过期、访问限制、账号迁移等
- 支持搜狗微信搜索结果的解析 (
weixin.sogou.com) - 可选提取文章标签和内嵌链接
安装
这是一个 Claude Code Skill,可以通过以下方式安装:
通过 Claude Code 安装(推荐)
在 Claude Code 中运行:
/skill install wechat-article-extractor或指定目录安装:
/skill install /path/to/wechat-article-extractor-skill手动克隆安装
git clone https://github.com/yourusername/wechat-article-extractor-skill.git
cd wechat-article-extractor-skill
npm install然后在 Claude Code 中将该目录作为 Skill 加载。
使用方法
基本用法 - 从 URL 提取
const { extract } = require('./scripts/extract.js');
async function main() {
const url = 'https://mp.weixin.qq.com/s?__biz=...&mid=...&idx=...&sn=...';
const result = await extract(url);
if (result.done) {
console.log('文章标题:', result.data.msg_title);
console.log('公众号:', result.data.account_name);
console.log('发布时间:', result.data.msg_publish_time_str);
} else {
console.error('提取失败:', result.msg);
}
}
main();从 HTML 内容提取
如果你已经获取了页面 HTML,可以直接传入:
const { extract } = require('./scripts/extract.js');
async function main() {
const html = await fetch(url).then(r => r.text());
const result = await extract(html, { url: sourceUrl });
console.log(result);
}
main();高级选项
const result = await extract(url, {
shouldReturnContent: true, // 返回 HTML 内容(默认:true)
shouldReturnRawMeta: false, // 返回原始元数据(默认:false)
shouldFollowTransferLink: true, // 自动跟随迁移后的公众号链接(默认:true)
shouldExtractMpLinks: false, // 提取内嵌的微信公众号链接(默认:false)
shouldExtractTags: false, // 提取文章标签(默认:false)
shouldExtractRepostMeta: false // 提取转载来源信息(默认:false)
});响应格式
成功响应
{
done: true,
code: 0,
data: {
// 公众号信息
account_name: "公众号名称",
account_alias: "微信号",
account_avatar: "头像URL",
account_description: "功能介绍",
account_id: "原始ID",
account_biz: "biz参数",
account_biz_number: 1234567890,
account_qr_code: "二维码URL",
// 文章信息
msg_title: "文章标题",
msg_desc: "文章摘要",
msg_content: "HTML内容",
msg_cover: "封面图URL",
msg_author: "作者",
msg_type: "post", // post|video|image|voice|text|repost
msg_has_copyright: true,
msg_publish_time: Date,
msg_publish_time_str: "2024/01/15 10:30:00",
// 链接参数
msg_link: "文章链接",
msg_source_url: "阅读原文链接",
msg_sn: "sn参数",
msg_mid: 1234567890,
msg_idx: 1
}
}错误响应
{
done: false,
code: 1001,
msg: "无法获取文章信息"
}错误代码表
| 代码 | 错误信息 | 说明 |
|---|---|---|
| 1000 | 文章获取失败 | 一般性失败 |
| 1001 | 无法获取文章信息 | 缺少标题或发布时间 |
| 1002 | 请求失败 | HTTP 请求失败 |
| 1003 | 响应为空 | 空响应 |
| 1004 | 访问过于频繁 | 被限流 |
| 1005 | 脚本解析失败 | 页面脚本解析错误 |
| 1006 | 公众号已迁移 | 账号已迁移,包含新链接 |
| 2001 | 请提供文章内容或链接 | 缺少输入参数 |
| 2002 | 链接已过期 | 链接已失效 |
| 2003 | 内容涉嫌侵权 | 内容因侵权被移除 |
| 2004 | 无法获取迁移后的链接 | 迁移链接获取失败 |
| 2005 | 内容已被发布者删除 | 作者已删除内容 |
| 2006 | 内容因违规无法查看 | 内容被平台屏蔽 |
| 2007 | 内容发送失败 | 发送失败 |
| 2008 | 系统出错 | 系统错误 |
| 2009 | 不支持的链接 | URL 格式不支持 |
| 2010 | 内容获取失败 | 内容获取失败 |
| 2011 | 涉嫌过度营销 | 营销/垃圾内容 |
| 2012 | 账号已被屏蔽 | 账号被封禁 |
| 2013 | 账号已自主注销 | 账号已注销 |
| 2014 | 内容被投诉 | 内容被举报 |
| 2015 | 账号处于迁移流程中 | 账号正在迁移 |
| 2016 | 冒名侵权 | 冒充侵权 |
支持的文章类型
| 类型 | 说明 | msg_type |
|---|---|---|
| 图文 | 普通图文文章 | post |
| 视频 | 视频内容 | video |
| 图片集 | 多张图片展示 | image |
| 语音 | 音频内容 | voice |
| 纯文字 | 无标题文字内容 | text |
| 转载 | 转载他人文章 | repost |
项目结构
wechat-article-extractor-skill/
├── scripts/
│ ├── extract.js # 核心提取逻辑
│ └── errors.js # 错误代码定义
├── SKILL.md # Skill 定义文件(Claude Skill 格式,包含触发条件和描述)
├── package.json # 项目配置
└── README.md # 本文件依赖项
cheerio- 服务端 HTML 解析dayjs- 日期格式化request-promise- HTTP 请求qs- 查询字符串解析lodash.unescape- HTML 实体解码
注意事项
1. 频率限制: 频繁请求可能会导致 IP 被暂时封禁,建议添加适当的延迟 2. 页面结构: 微信页面结构可能会变化,如遇问题请检查是否为最新版本 3. Cookie: 某些文章可能需要登录才能访问完整内容 4. 反爬措施: 请遵守微信的使用条款,合理使用本工具
示例应用
批量提取文章
const { extract } = require('./scripts/extract.js');
async function batchExtract(urls) {
const results = [];
for (const url of urls) {
try {
const result = await extract(url);
if (result.done) {
results.push({
title: result.data.msg_title,
author: result.data.account_name,
publishTime: result.data.msg_publish_time_str
});
}
// 添加延迟避免被限流
await new Promise(r => setTimeout(r, 1000));
} catch (err) {
console.error(`提取失败: ${url}`, err.message);
}
}
return results;
}
const urls = [
'https://mp.weixin.qq.com/s?__biz=...',
'https://mp.weixin.qq.com/s?__biz=...'
];
batchExtract(urls).then(console.log);保存为 Markdown
const { extract } = require('./scripts/extract.js');
const fs = require('fs');
async function saveAsMarkdown(url, filename) {
const result = await extract(url);
if (!result.done) {
console.error('提取失败:', result.msg);
return;
}
const { data } = result;
const markdown = `
# ${data.msg_title}
> 作者: ${data.msg_author || data.account_name}
> 公众号: ${data.account_name}
> 发布时间: ${data.msg_publish_time_str}
${data.msg_content}
---
原文链接: [${data.msg_link}](${data.msg_link})
`;
fs.writeFileSync(filename, markdown);
console.log(`已保存: ${filename}`);
}
saveAsMarkdown('https://mp.weixin.qq.com/s?__biz=...', 'article.md');许可证
MIT
贡献
欢迎提交 Issue 和 Pull Request!
更新日志
v1.0.0
- 初始版本发布
- 支持基本的文章信息提取
- 支持多种文章类型
- 完善的错误处理机制
module.exports = {
1000: '文章获取失败',
1001: '无法获取文章信息',
1002: '请求失败',
1003: '响应为空',
1004: '访问过于频繁',
1005: '脚本解析失败',
1006: '公众号已迁移',
2001: '请提供文章内容或链接',
2002: '链接已过期',
2003: '内容涉嫌侵权,无法查看',
2004: '无法获取迁移后的链接',
2005: '内容已被发布者删除',
2006: '内容因违规无法查看',
2007: '内容发送失败无法查看',
2008: '系统出错',
2009: '不支持的链接',
2010: '内容获取失败',
2011: '由用户投诉并经平台审核,涉嫌过度营销、骚扰用户',
2012: '此帐号已被屏蔽,内容无法查看',
2013: '此帐号已自主注销,内容无法查看',
2014: '此内容被投诉且经审核确认存在不实信息',
2015: '此帐号处于帐号迁移流程中',
2016: '由用户投诉并经平台审核,涉嫌冒名侵权'
};
const qs = require('qs');
const dayjs = require('dayjs');
const request = require('request-promise');
const cheerio = require('cheerio');
const unescape = require('lodash.unescape');
const errors = require('./errors');
const defaultConfig = {
shouldReturnRawMeta: false,
shouldReturnContent: true,
shouldFollowTransferLink: true,
shouldExtractMpLinks: false,
shouldExtractTags: false,
shouldExtractRepostMeta: false
};
function getError(code) {
return { done: false, code, msg: errors[code] };
}
function normalizeUrl(url = '') {
const parts = url.replace(/&/g, '&').split('?');
const querys = qs.stringify(qs.parse(parts[1]));
return querys ? `${parts[0]}?${querys}` : parts[0];
}
function getParameterByName(name, url) {
name = name.replace(/[\[\]]/g, '\\$&');
const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
const results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
function parseUrlParams(url) {
if (!url) return {};
const rs = require('querystring').parse(url.replace(/&/g, '&').split('?')[1]);
return {
mid: rs.mid * 1,
idx: rs.idx * 1,
sn: rs.sn,
biz: rs.__biz
};
}
async function extract(input, options = {}) {
const config = Object.assign({}, defaultConfig, options);
const {
shouldReturnRawMeta,
shouldReturnContent,
shouldFollowTransferLink,
shouldExtractMpLinks,
shouldExtractTags,
shouldExtractRepostMeta
} = config;
if (!input) return getError(2001);
let paramType = 'HTML';
let url = options.url ? normalizeUrl(options.url) : null;
let rawUrl = null;
let html = input;
let type = 'post';
let hasCopyright = false;
// Handle URL input
if (/^http/.test(input)) {
const normalized = normalizeUrl(input);
if (!/https?:\/\/mp\.weixin\.qq\.com/.test(normalized) &&
!/https?:\/\/weixin\.sogou\.com/.test(normalized)) {
return getError(2009);
}
paramType = 'URL';
rawUrl = normalized;
if (!url) url = normalized;
const host = /weixin\.sogou\.com/.test(normalized) ? 'weixin.sogou.com' : 'mp.weixin.qq.com';
try {
html = await request({
uri: normalized,
method: 'GET',
headers: {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Host': host
}
});
} catch (e) {
return getError(1002);
}
} else {
html = input.replace(/\\n/g, '');
}
if (!html) return getError(1003);
// Check for error pages
if (html.includes('访问过于频繁') && !html.includes('js_content')) {
return paramType === 'URL' ? getError(1004) : getError(2010);
}
if (html.includes('链接已过期') && !html.includes('js_content')) return getError(2002);
if (html.includes('被投诉且经审核涉嫌侵权,无法查看')) return getError(2003);
if (html.includes('该公众号已迁移')) {
const match = html.match(/var\stransferTargetLink\s=\s'(.*?)';/);
if (match && match[1]) {
if (shouldFollowTransferLink) {
return await extract(match[1]);
}
return { ...getError(1006), url: match[1] };
}
return getError(2004);
}
if (html.includes('该内容已被发布者删除')) return getError(2005);
if (html.includes('此内容因违规无法查看')) return getError(2006);
if (html.includes('此内容发送失败无法查看')) return getError(2007);
if (html.includes('由用户投诉并经平台审核,涉嫌过度营销')) return getError(2011);
if (html.includes('此帐号已被屏蔽') && !html.includes('id="js_content"')) return getError(2012);
if (html.includes('此帐号已自主注销') && !html.includes('id="js_content"')) return getError(2013);
if (!html.includes('id="js_content"') && html.includes('此帐号处于帐号迁移流程中')) return getError(2015);
if (html.includes('page_rumor') && !html.includes('id="js_content"')) return getError(2014);
if (html.includes('投诉类型') && html.includes('冒名侵权')) return getError(2016);
if (!html.includes('id="js_content"') && !html.includes('id=\\"js_content\\"')) {
if (html.includes('cover_url')) {
type = 'image';
} else {
return getError(1000);
}
}
// Prepare HTML
html = html.replace('>微信号', ' id="append-account-alias">微信号')
.replace('>功能介绍', ' id="append-account-desc">功能介绍')
.replace(/\n\s+<script/g, '\n\n<script');
const $ = cheerio.load(html, { decodeEntities: false });
// Detect type and copyright
if ($('#copyright_logo')?.text().includes('原创')) hasCopyright = true;
if (/video/.test($('body').attr('class'))) type = 'video';
if ($('#js_content > #img_list').length) type = 'image';
if ($('#js_share_content').length) type = 'repost';
if ($('.page_share_audio').length || $('#voice_parent').length) type = 'voice';
if (/share_media_text/.test(html)) type = 'text';
// Check for expired link or system error
if ($('.weui-msg .weui-msg__title').text().trim() === '链接已过期') return getError(2002);
if ($('.global_error_msg.warn').text().trim().includes('系统出错')) return getError(2008);
// Extract basic info
const basic = {
accountName: $('.profile_nickname').text() || null,
accountBiz: null,
accountBizNumber: null,
accountId: null,
accountAvatar: null
};
const accountAliasPrev = $('#append-account-alias');
let accountAlias = accountAliasPrev.siblings('span').text() || null;
const accountDescPrev = $('#append-account-desc');
let accountDesc = accountDescPrev.siblings('span').text() || null;
if (!accountDesc) {
const $accountDesc = $('.profile_meta_value');
if ($accountDesc[1]) {
try {
const text = $accountDesc[1].children[0].data;
if (text?.length > 10) accountDesc = text;
} catch (e) {}
}
}
const post = {
msg_has_copyright: hasCopyright,
msg_content: shouldReturnContent ? $('#js_content').html() : null
};
// Extract author
try {
const author = $("meta[name='author']").attr('content');
if (author) post.msg_author = author;
} catch (e) {
const $author = $('#js_author_name');
if ($author.length) {
const info = $author.text().trim();
if (info) post.msg_author = info;
}
}
// Extract from scripts
const scripts = html.match(/<script[\s\S]*?>([\s\S]*?)<\/script>/gi) || [];
const extra = { biz: null, sn: null, mid: null, idx: null, msg_title: null, user_name: null, nick_name: null, hd_head_img: null };
let picturePageInfoList = null;
for (const script of scripts) {
// Picture type
if (script.includes('picture_page_info_list') && script.includes('https://mmbiz.qpic.cn')) {
try {
const lines = script.split('\n');
const code = lines.slice(1, lines.length - 2).join('\n').trim()
.replace(/^\(function\(\) {/, '').replace(/}\)\(\);$/, '');
const fn = new Function(`var x = {}; ${code.replace(/window\./g, 'x.').replace('//g', '/\\n/g')}\nreturn x;`);
const result = fn();
if (result.picture_page_info_list) picturePageInfoList = result.picture_page_info_list;
} catch (e) {}
}
// Voice type
if (type === 'voice' && script.includes('voiceid')) {
const lines = script.split(/\n|\r/).filter(one => one.includes('voiceid'))
.sort((a, b) => a.length > b.length ? -1 : 1);
if (lines.length) {
const val = lines[0].replace(/['"|:,voiceid\s]/g, '');
if (val) post.msg_source_url = `https://res.wx.qq.com/voice/getvoice?mediaid=${val.trim()}`;
}
}
// Extract extra fields
for (const field of Object.keys(extra)) {
const reg = new RegExp(`var\\s+${field}\\s*=`);
if (reg.test(script) && !extra[field]) {
try {
const line = script.split('\n').filter(one => reg.test(one))[0];
const fn = new Function(`${line}\nreturn ${field};`);
extra[field] = fn();
} catch (e) {}
}
if (!extra[field]) {
const reg2 = new RegExp(`window\\.${field}\\s*=`);
if (reg2.test(script)) {
try {
const line = script.split('\n').filter(one => reg2.test(one))[0];
const fn = new Function(`window = {}; ${line}\nreturn window.${field};`);
extra[field] = fn();
} catch (e) {}
}
}
}
// Data from d object (video/image/voice)
if ((type === 'image' || type === 'voice') && script.includes('d.title =')) {
try {
const lines = script.split('\n').filter(line => !!line.trim());
const codeLines = lines.filter((line, index) =>
/d\./.test(line) || (lines[index - 1] && lines[index - 1].includes('d.') && !line.includes('}'))
);
let code = `var d = {}; function getXmlValue(path) { return false; }\n` +
codeLines.join('\n').replace('var d = _g.cgiData;', 'var d = {}') +
'\nreturn d;';
code = `var _g = {}; ${code}`;
const fn = new Function(code);
const data = fn();
basic.accountName = data.nick_name;
basic.accountAvatar = data.hd_head_img;
basic.accountId = data.user_name;
if (!basic.accountBiz && data.biz) {
basic.accountBiz = data.biz;
basic.accountBizNumber = Buffer.from(data.biz, 'base64').toString() * 1;
}
post.msg_title = data.title;
post.msg_desc = null;
post.msg_cover = null;
post.msg_link = data.msg_link || null;
post.msg_sn = data.sn || null;
post.msg_idx = data.idx ? data.idx * 1 : null;
post.msg_mid = data.mid ? data.mid * 1 : null;
if (type === 'video') {
const vidMatch = html.match(/vid\s*:\s*'(.*?)'/);
if (vidMatch) data.vid = vidMatch[1];
post.msg_cover = $("meta[property='og:image']").attr('content');
}
if (type === 'video' || type === 'voice') {
post.msg_content = $("meta[name='description']").attr('content');
}
if (data.create_time) {
post.msg_publish_time = new Date(data.create_time * 1000);
post.msg_publish_time_str = dayjs(post.msg_publish_time).format('YYYY/MM/DD HH:mm:ss');
}
if (shouldReturnRawMeta) post.raw_data = data;
} catch (e) {
return getError(1005);
}
}
// Post/repost type
if ((type === 'post' || type === 'repost') && script.includes('var msg_link =')) {
try {
const lines = script.split('\n');
let code = lines.slice(1, lines.length - 1)
.filter(line => !line.includes('var title'))
.map(line => {
if (/var\s+msg_desc/.test(line)) {
line = line.replace(/`/g, "'").replace(/"/g, '`');
}
return line;
}).join('\n');
code = `var window = { location: { protocol: 'https' } };
var document = {
addEventListener: function() {},
getElementById: function() {
return { classList: { remove: function() {}, add: function() {} } };
}
};
var location = { protocol: "https" };\n${code}`;
const vars = code.match(/var\s(.*?)\s=/g)?.map(key => key.split(' ')[1]).filter(k => k !== 'window') || [];
let rs = ';\nvar rs = {';
vars.forEach(key => {
rs += `"${key}": typeof ${key} !== 'undefined' ? ${key} : null,`;
});
rs += '}\nreturn rs;';
const stringProto = `String.prototype.html = function(encode) {
var replace = ["'", "'", """, '"', " ", " ", ">", ">", "<", "<", "¥", "¥", "&", "&"];
var replaceReverse = ["&", "&", "¥", "¥", "<", "<", ">", ">", " ", " ", '"', """, "'", "'"];
var target = encode ? replaceReverse : replace;
for (var i = 0, str = this; i < target.length; i += 2) {
str = str.replace(new RegExp(target[i], 'g'), target[i + 1]);
}
return str;
};`;
const fn = new Function(stringProto + code + rs);
const data = fn();
// Fallback for biz
if (!basic.accountBiz) {
const reg = new RegExp(`var\\s+biz\\s*=`);
const matched = html.split('\n').find(line => reg.test(line) && line.length > 10);
if (matched) {
try {
const bizFn = new Function(`${matched}; return biz;`);
const rs = bizFn();
if (rs) {
basic.accountBiz = rs;
basic.accountBizNumber = Buffer.from(rs, 'base64').toString() * 1;
}
} catch (e) {}
}
}
['msg_title', 'msg_desc', 'msg_link', 'msg_source_url'].forEach(key => {
post[key] = data[key] || null;
});
post.msg_cover = data.msg_cdn_url;
post.msg_article_type = data._ori_article_type || null;
post.msg_publish_time = new Date(data.ct * 1000);
post.msg_publish_time_str = dayjs(post.msg_publish_time).format('YYYY/MM/DD HH:mm:ss');
if (shouldReturnRawMeta) post.raw_data = data;
basic.accountId = data.user_name;
basic.accountAvatar = data.ori_head_img_url;
if (!basic.accountName && data.nickname) basic.accountName = data.nickname;
} catch (e) {
return getError(1005);
}
}
}
// Set extracted extra fields
if (extra.biz) {
basic.accountBiz = extra.biz;
basic.accountBizNumber = Buffer.from(extra.biz, 'base64').toString() * 1;
}
post.msg_sn = extra.sn || post.msg_sn || null;
post.msg_idx = extra.idx ? extra.idx * 1 : post.msg_idx || null;
post.msg_mid = extra.mid ? extra.mid * 1 : post.msg_mid || null;
// Fallback for missing fields
if (!post.msg_publish_time) {
const date = $('#post-date').text() || $('#publish_time').text();
if (date) post.msg_publish_time = new Date(date);
}
if (!post.msg_publish_time && html.includes('.ct')) {
const line = html.split('\n').find(one => one.includes('.ct'));
const matched = /'(\d+)'/g.exec(line);
if (matched && matched[1]?.length >= 10) {
post.msg_publish_time = new Date(matched[1] * 1000);
}
}
if (!post.msg_title) {
const title = $('.rich_media_title').text();
if (title) post.msg_title = title.trim();
}
// Set account info from extra
if (!basic.accountId && extra.user_name) basic.accountId = extra.user_name;
if (!basic.accountName && extra.nick_name) basic.accountName = extra.nick_name;
if (!basic.accountAvatar && extra.hd_head_img) basic.accountAvatar = extra.hd_head_img;
if (!basic.accountName && $('.wx_follow_nickname')) {
const name = $('.wx_follow_nickname').text();
if (name) basic.accountName = name.trim();
}
// Build result
const data = {
account_name: basic.accountName,
account_alias: accountAlias,
account_avatar: basic.accountAvatar?.length > 10 ? basic.accountAvatar : null,
account_description: accountDesc,
account_id: basic.accountId,
account_biz: basic.accountBiz,
account_biz_number: basic.accountBizNumber,
account_qr_code: `https://open.weixin.qq.com/qr/code?username=${basic.accountId || accountAlias}`,
...post,
msg_type: type
};
// Clean empty strings to null
for (const key in data) {
if (data[key] === '') data[key] = null;
}
// Handle text type (no title)
if (!data.msg_title && type === 'post') {
data.msg_type = 'text';
const title = $("meta[property='og:title']").attr('content');
const desc = $("meta[property='og:description']").attr('content');
if (title) {
data.msg_title = title;
const rawContent = $('#js_panel_like_title').html();
data.msg_content = rawContent ? rawContent.trim().replace(/\n/g, '<br/>') : title;
}
if (!title && desc) data.msg_title = desc;
}
// Fallback for time
if (!data.msg_publish_time) {
const matched = html.match(/d\.ct\s*=\s*"(\d+)"/);
if (matched && matched[1]) {
data.msg_publish_time = new Date(matched[1] * 1000);
data.msg_publish_time_str = dayjs(data.msg_publish_time).format('YYYY/MM/DD HH:mm:ss');
}
}
// Fallback for link params
if (!data.msg_mid || !data.msg_link) {
let linkUrl = options?.url || rawUrl || $("meta[property='og:url']").attr('content');
if (linkUrl && /^http/.test(linkUrl) && /mid/.test(linkUrl) && /__biz/.test(linkUrl)) {
linkUrl = linkUrl.replace(/&/g, '&');
if (!data.msg_link) data.msg_link = linkUrl;
if (!data.msg_mid) data.msg_mid = getParameterByName('mid', linkUrl);
if (!data.msg_idx) data.msg_idx = getParameterByName('idx', linkUrl);
if (!data.msg_sn) data.msg_sn = getParameterByName('sn', linkUrl);
}
}
// Unescape title
if (data.msg_title) data.msg_title = unescape(data.msg_title);
// Handle video content
if (data.msg_type === 'video') {
if (!data.msg_content) {
data.msg_content = data.msg_title;
} else {
data.msg_content = data.msg_content
.replace(/\\x26/g, '&')
.replace(/\\x0a/g, '<br/>');
}
}
// Final fallback for title
if (!data.msg_title) {
const title = $("meta[property='og:title']").attr('content');
if (title) data.msg_title = title;
}
// Fallback for description
if (!data.msg_desc) {
data.msg_desc = $("meta[property='og:description']").attr('content') ||
$("meta[name='description']").attr('content');
}
if (!data.msg_desc && data.msg_content) {
const text = data.msg_content.replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
if (text.length > 0) {
data.msg_desc = text.substring(0, 140) + (text.length > 140 ? '...' : '');
}
}
// Handle script in content
if (data.msg_content?.includes('<script') && data.msg_content.includes('script>') && data.msg_content.includes('nonce=')) {
const desc = $("meta[property='og:description']").attr('content');
if (desc) data.msg_content = desc;
}
// Validate result
if (!data.msg_title || !data.msg_publish_time) return getError(1001);
// Text type fallback
if (type === 'text' && !data.msg_content && data.msg_title) {
data.msg_content = data.msg_title;
}
// Image type with picture_page_info_list
if (picturePageInfoList) {
data.msg_type = 'image';
data.msg_content = `${data.msg_title}<br>`;
for (const one of picturePageInfoList) {
data.msg_content += `<img src="${one.cdn_url}" style="max-width:100%"/><br><br>`;
}
}
// Extract mp links
if (shouldExtractMpLinks) {
const mpLinks = [];
$('a').each((i, ele) => {
const href = $(ele).attr('href');
if (href?.includes('mp.weixin.qq.com')) {
mpLinks.push({ title: $(ele).text(), href });
}
});
data.mp_links_count = mpLinks.length;
data.mp_links = mpLinks;
}
// Extract tags
if (shouldExtractTags) {
const tags = [];
$('.article-tag__item-wrp').each((i, ele) => {
const $this = $(ele);
try {
const tagUrl = $this.attr('data-url');
const name = $this.find('.article-tag__item').text();
let count = $this.find('.article-tag__item-num').text();
if (name) {
if (!count && tags.length === 0) {
const $count = $('.article-tag-card__right');
if ($count.length) count = $count.text().replace('个', '');
}
tags.push({
id: getParameterByName('album_id', tagUrl) || getParameterByName('tag_id', tagUrl) || null,
url: tagUrl,
name: name.replace(/^#/, ''),
count: count?.replace(/\D/g, '') * 1 || 0
});
}
} catch (e) {}
});
data.tags = tags;
}
// Extract repost meta
if (shouldExtractRepostMeta && html.includes('copyright_info') && html.includes('original_primary_nickname')) {
const name = $('.original_primary_nickname').text();
if (name) data.repost_meta = { account_name: name };
}
// Clean up link
if (data.msg_link?.includes('&')) {
data.msg_link = data.msg_link.replace(/&/g, '&');
}
return { code: 0, done: true, data };
}
module.exports = { extract };