Tencent-WxRead-Cookies

Posted on 2026-03-03 16:59  笔名钟意  阅读(3)  评论(0)    收藏  举报

---
title: 微信读书Cookies续活
layout: post
cover: 'https://upyun.thatcdn.cn/myself/typora/202309132130825.jpg'
tags: ['Tencent']
categories: ['堆栈']
poster:
headline: ''
caption: ''
color: 'white'
description: '实现微信读书的Cookies在生命结束后自动刷新'
date: 2023-09-13 19:00:00

机制分析

很多优秀的文章分析了延期机制, 这里列举两个
{% link https://zhaohongxuan.github.io/2022/05/16/how-to-relong-cookies-in-weread/ Hank's Blog 微信读书延期机制分析 %}

稳定性

目前跑了几天, Cookies都能自动刷新保活

主要代码

续活我没使用代理服务器, 直接请求了

// 刷新 Cookie 的函数,模拟发送请求获取新 Cookie
const refCookie = async (uid: string) => {
  try {
    const response = await axios.head('https://weread.qq.com', { headers: globalHeaders() });
    if (response.status === 200 || response.headers['set-cookie']) {
        globalCookies = CookieUtil.WebArrayToString(response.headers['set-cookie'], globalCookies);
        return (await upUserCookie(uid)) ? true : false
    }else {
        return false
    }
  } catch (e) {
    return false
  }
}

全部代码

运行在自己搭建的 Laf 云函数, 不能无脑抄。 代码虽烂 但已写注释。
需要临时使用我的接口可以联系我

{% folding 代码结构图 %}
{% image https://upyun.thatcdn.cn/myself/typora/202309132103742.png 这样也许清晰一点 ratio:1689/1282 %}

import axios from 'axios';
import cloud from "@/cloud-sdk";

/**
 * API请求入口方法
 */
exports.main = async function (ctx: FunctionContext) {
  try {
    const { cookies, uid, refresh } = ctx.method === 'GET' ? ctx.query || ctx.params : ctx.body;
    if (verifyData(uid)) {  // 用户获取cookie请求
      if ((await userServer.verifyUser(uid))) {
        !(await CookiesApi.verifyRefresh(uid)) || (await CookiesApi.refCookie(refresh))
        return msgServer.success("获取Cookie成功", { cookies: globalCookies })
      } else {
        return msgServer.failed("搞咩! " + uid + " 不存在!");
      }
    } else if (verifyData(cookies)) {   // 新增用户请求
      const uid = CookieUtil.StringToJson(cookies)['wr_vid']
      globalCookies = cookies
      const userInfo = (await CookiesApi.getUserInfo(uid))
      if (!userInfo['name']) {
        return msgServer.failed("搞咩! cookies 不能用!");
      }
      const userData: WxReadUser = {
        'userVid': uid,
        'userInfo': userInfo,
        'cookies': globalCookies,
        'cookies_uptime': (new Date()).valueOf(),
        'cookies_life': true
      }
      const add = (await userServer.addUser(userData))
      if (add.answer) {
        return msgServer.success(
          `存入cookies成功, 未来取用cookies请通过以下方式${'\n'}[ https://sijnzx.laf.thatcoder.cn/tencent-weread-refcookie?uid=您的userVid ]`,
          { userVid: uid, userInfo }
        )
      } else {
        return msgServer.error()
      }
    } else if (verifyData(refresh)) {   // 刷新请求
      let req: any
      if (!(await CookiesApi.verifyRefresh(refresh))) {
        return msgServer.success("Cookie不需要刷新")
      } else {
        return (await CookiesApi.refCookie(refresh)) ? msgServer.success("刷新Cookie成功") : msgServer.error()
      }
    } else {
      return msgServer.failed("搞咩! 传的什么狗屁参数!");
    }
  } catch (error) {
    return msgServer.error()
  }
};

/**
 * 获取数据库访问器
 */
const db = cloud.database().collection('tc_tencent_wxread');

/**
 * cookies格式工具
 */
const CookieUtil = {
  StringToJson: (cookiesString: string) => {
    const cookieGroup = cookiesString.split('; ')
    const cookieJson = {}
    for (let i = 0; i < cookieGroup.length; i++) {
      const cookieGroupJson = cookieGroup[i].split('=')
      cookieJson[cookieGroupJson[0]] = cookieGroupJson.length === 1 ? '' : cookieGroupJson[1]
    }
    return cookieJson
  },
  JsonToString: (cookiesJson: object) => {
    const keyValuePairs = [];
    for (const key in cookiesJson) {
      if (cookiesJson.hasOwnProperty(key)) {
        const value = cookiesJson[key];
        keyValuePairs.push(`${key}=${value}`);
      }
    }
    return keyValuePairs.join('; ');
  },
  WebArrayToString: (cookiesArray: Array<string>, cookiesString: string) => {
    let cookieJson = CookieUtil.StringToJson(cookiesString)
    for (const cookie of cookiesArray) {
      const refresh: Array<string> = cookie.split('; ')[0].split('=')
      cookieJson[refresh[0]] = refresh[1]
    }
    return CookieUtil.JsonToString(cookieJson)
  },
  StringToArray: (cookiesString: string) => {
    return cookiesString.split('; ')
  }
}

// 全局变量。不合理, 但是能减少云函数单文件代码量
var globalCookies = ""
var globalHeaders = () => {
  return {
    Cookie: CookieUtil.StringToArray(globalCookies), // 传入的 Cookie 数组
    Referer: 'https://weread.qq.com/',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': '*',
  }
}

/**
 * 微信读书API方法
 */
const CookiesApi = {
  /**
   * 获取用户信息
   * @uid: 微信Cookie['wr_vid']
   */
  getUserInfo: async (uid: string) => {
    let userInfo = { 'userVid': "" }
    await axios.get('https://weread.qq.com/web/user?userVid=' + uid, { headers: globalHeaders() }).then(e => {
      userInfo = e.data
    })
    return userInfo
  },
  /**
   * 验证Cookie是否存活
   * @uid: 微信Cookie['wr_vid']
   */
  verifyAlive: async (uid: string) => {
    const cookie = (await userServer.getUserCookie(uid))
    globalCookies = cookie
    let userInfo = await CookiesApi.getUserInfo(uid)
    return (String)(userInfo['userVid']).includes(uid)
  },
  // 判断是否需要刷新 Cookie
  verifyRefresh: async (uid: string) => {
    const time = (await userServer.getUserCookieTime(uid))
    if ( !(await CookiesApi.verifyAlive(uid)) || (new Date()).valueOf() - time >= 3600000) {
      return true
    }
    return false
  },
  // 刷新 Cookie 的函数,模拟发送请求获取新 Cookie
  refCookie: async (uid: string) => {
    try {
      const response = await axios.head('https://weread.qq.com', { headers: globalHeaders() });
      // if (response.status === 200) {
      //   return true
      // }
      const newCookies = response.headers['set-cookie']
      if (!newCookies) {
        return false
      }
      globalCookies = CookieUtil.WebArrayToString(newCookies, globalCookies);
      return (await userServer.upUserCookie(uid)) ? true : false
    } catch (e) {
      return false
    }
  }
}

/**
 * 数据库服务层。 呵, JS哪来的服务层
 */
const userServer = {
  /**
   * 验证数据库是否存在用户
   * @uid: 微信Cookie['wr_vid']
   * @return: {boolean}
   */
  verifyUser: async (uid: string) => {
    return (await db.where({ 'userVid': uid }).count()).total > 0
  },
  /**
   * 获取用户Cookie
   * @uid: 微信Cookie['wr_vid']
   */
  getUserCookie: async (uid: string) => {
    const get = (await db.where({ 'userVid': uid }).limit(1).get())
    return get.data[0]['cookies']
  },
  /**
   * 获取用户CookieTime
   * @uid: 微信Cookie['wr_vid']
   */
  getUserCookieTime: async (uid: string) => {
    const get = (await db.where({ 'userVid': uid }).limit(1).get())
    return get.data[0]['cookies_uptime']
  },
  /**
   * 更新用户Cookie
   */
  upUserCookie: async (uid: string) => {
    return (await db.where({'userVid': uid}).limit(1).update({ cookies: globalCookies, cookies_uptime: (new Date()).valueOf(), cookies_life: true })).ok
  },
  /**
   * 新增数据库用户信息
   */
  addUser: async (userData: WxReadUser) => {
    let add: any
    if ((await userServer.verifyUser(userData.userVid))) {
      add = (await db.where({'userVid': userData.userVid}).limit(1).update(userData))
    } else {
      add = (await db.add(userData))
    }
    return { answer: add.ok, id: add.upsertId }
  }
}

/**
 * 回执服务层
 */
const msgServer = {
  success: (msg: string, data: any = {}) => {
    return JSON.stringify({ statusCode: 200, event: "操作成功", message: msg, data })
  },
  failed: (msg: string) => {
    return JSON.stringify({ statusCode: 400, event: "操作失败", message: msg })
  },
  error: () => {
    return JSON.stringify({ statusCode: 500, event: "程序错误", message: "请联系钟意, 必应搜索钟意博客。" })
  }
}

/**
 * 微信用户对象接口
 */
interface WxReadUser {
  'userVid': string,
  'userInfo'?: any,
  'cookies': string,
  'cookies_uptime'?: number,
  'cookies_life'?: boolean
}

const verifyData = (data: any) => {
  if (data === null || data === [] || data === {} || data === undefined || data === '') {
    return false
  } else if (data.length > 0) {
    return true
  } else {
    return false
  }
}

实际应用

  • 获取自己的微信读书信息
  • 下载微信读书的书籍
  • 导出书单信息
  • 带出读书笔记
  • 自动阅读( 这个功能有什么用? )