无敌奶龙大王是如何做到微信公众号私聊到服务器媒体库的?

背景
这篇文章记录一个“小工具脚本”的实现思路:从微信公众号后台(mp.weixin.qq.com)抓取用户会话里的表情消息(type=47),把表情文件下载到服务器,再(可选)导入到 WordPress 媒体库,方便后续在站点里整理与展示。

整体流程(从输入到产出)
准备请求凭证(Cookie + Token)。
获取所有会话用户列表(脚本里对应 get_all_users())。
逐个用户拉取最近消息(脚本里对应 get_user_messages(fakeid))。
筛选出表情消息(常见为 type=47)。
按 msgid 调用下载接口,把表情文件保存到目录(脚本里对应 download_emoji(msgid))。
(可选)通过 docker exec 在 WordPress 容器里运行 wp media import 导入媒体库(脚本里对应 import_to_wordpress(path))。
最终你会得到:一个本地文件夹里的表情文件 +(可选)WordPress 媒体库里的附件记录。
接口与关键点(脱敏说明)
脚本通常会用到以下页面/接口(URL 结构示意,不含任何真实参数值):

消息列表:https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=50&day=7&token=…
单聊页(用于获取会话/用户信息):https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=…
文件下载:https://mp.weixin.qq.com/cgi-bin/downloadfile?msgid=…
重点在于:Cookie/Token 只是“门票”,真正的业务逻辑是“列用户 → 拉消息 → 过滤 type=47 → 按 msgid 下载文件”。

import os
import json
import time
import subprocess
import requests
from datetime import datetime

# ============ 配置 ============
COOKIE = ''
TOKEN = ''
SAVE_DIR = '/opt/1panel/apps/wordpress/wordpress/data/wechat-media/emoji'
WP_CONTAINER = '1Panel-wordpress-4JAd'
# ==============================

HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Cookie': COOKIE,
    'Referer': 'https://mp.weixin.qq.com/',
}

def get_all_users():
    url = f'https://mp.weixin.qq.com/cgi-bin/message?t=message/list&count=50&day=7&token={TOKEN}&lang=zh_CN&f=json'
    try:
        resp = requests.get(url, headers=HEADERS, timeout=30)
        data = resp.json()
        if data.get('base_resp', {}).get('ret') != 0:
            return []
        users = []
        for item in data.get('item', []):
            msg = item.get('msg', {})
            msg_items_str = msg.get('msg_items', '{}')
            msg_items = json.loads(msg_items_str) if isinstance(msg_items_str, str) else msg_items_str
            for m in msg_items.get('msg_item', []):
                fakeid = m.get('fakeid')
                nick = m.get('nick_name', 'unknown')
                if fakeid and fakeid not in [u['fakeid'] for u in users]:
                    users.append({'fakeid': fakeid, 'nick_name': nick})
        return users
    except:
        return []

def get_user_messages(fakeid):
    url = f'https://mp.weixin.qq.com/cgi-bin/singlesendpage?t=message/send&action=index&tofakeid={fakeid}&token={TOKEN}&lang=zh_CN&f=json'
    try:
        resp = requests.get(url, headers=HEADERS, timeout=30)
        data = resp.json()
        if data.get('base_resp', {}).get('ret') != 0:
            return []
        return data.get('page_info', {}).get('msg_items', {}).get('msg_item', [])
    except:
        return []

def import_to_wordpress(filepath):
    """导入文件到 WordPress 媒体库并分配到 Unreviewed 文件夹"""
    # 容器内路径
    wp_path = filepath.replace('/opt/1panel/apps/wordpress/wordpress/data', '/var/www/html')
    cmd = f'docker exec {WP_CONTAINER} wp media import {wp_path} --porcelain --allow-root 2>&1'
    try:
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30)
        # --porcelain 返回附件 ID
        attachment_id = result.stdout.strip()
        if attachment_id.isdigit():
            print(f'    -> 已导入媒体库 (ID: {attachment_id})')
            # 设置文件夹为 Unreviewed (使用 slug)
            set_folder_cmd = f'docker exec {WP_CONTAINER} wp post term set {attachment_id} media_folder unreviewed-1770143382-1 --allow-root 2>&1'
            subprocess.run(set_folder_cmd, shell=True, capture_output=True, text=True, timeout=10)
            print(f'    -> 已分配到 Unreviewed 文件夹')
            return True
        else:
            print(f'    -> 导入失败: {result.stdout[:100]}')
    except Exception as e:
        print(f'    -> 导入错误: {e}')
    return False

def download_emoji(msg_id):
    url = f'https://mp.weixin.qq.com/cgi-bin/downloadfile?msgid={msg_id}&token={TOKEN}&lang=zh_CN'
    try:
        resp = requests.get(url, headers=HEADERS, timeout=60)
        ct = resp.headers.get('Content-Type', '')
        if 'image' not in ct and 'gif' not in ct:
            return None
        ext = 'gif'
        if 'png' in ct: ext = 'png'
        elif 'jpeg' in ct or 'jpg' in ct: ext = 'jpg'
        
        os.makedirs(SAVE_DIR, exist_ok=True)
        filename = f'{msg_id}.{ext}'
        filepath = os.path.join(SAVE_DIR, filename)
        
        if os.path.exists(filepath):
            print(f'  [{msg_id}] 已存在,跳过')
            return None
        
        with open(filepath, 'wb') as f:
            f.write(resp.content)
        
        size_kb = len(resp.content) / 1024
        print(f'  [{msg_id}] 已下载: {filename} ({size_kb:.1f}KB)')
        
        # 导入到 WordPress
        import_to_wordpress(filepath)
        return filepath
    except Exception as e:
        print(f'  [{msg_id}] 失败: {e}')
        return None

def main():
    print(f'=== 表情包下载 {datetime.now().strftime("%H:%M:%S")} ===')
    users = get_all_users()
    
    for user in users:
        messages = get_user_messages(user['fakeid'])
        emoji_msgs = [m for m in messages if m.get('type') == 47]
        
        for msg in emoji_msgs:
            msg_id = msg.get('id')
            if msg_id:
                download_emoji(msg_id)
                time.sleep(0.5)
    
    print('完成')

if __name__ == '__main__':
    main()

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注