记录邻里邦自动签到接口


获取验证码

根据验证码获取 token

  • 接口:https://m-center-prod-linli.timesgroup.cn/times/times-bff/bff/api-c/v1/oauth/token
  • 请求方法:POST
  • 请求参数
    // 修改 username 为自己的手机号码
    // 修改 password 为收到的验证码
    {
      "grant_type":"sms_code",
      "scope":"all",
      "client_id":"mini_charge",
      "client_secret":"times",
      "username":"13412341234",
      "password":"123456",
      "areaCode":"86"
    }
  • 返回结果
    {
      "code":200,
      "data":{
        "authUserDTO":{
          "expiresIn":86399,
          "refreshToken":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9",
          "token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9",
          "tokenHead":"Bearer "
        },
        "userDTO":{
          "eWechatOpenId":"",
          "eWechatUnionId":"",
          "extUserId":1,
          "id":1,
          "isCancel":0,
          "isEnable":0,
          "isShowRealName":0,
          "isSupplement":0,
          "phone":"13412341234",
          "phoneAreaCode":"86",
          "projectId":1,
          "projectName":"",
          "sex":0,
          "storeType":1,
          "storeUrl":"https://mall-prod-app-linli.timesgroup.cn/app/index",
          "updateTime":1,
          "userImage":"https://thirdwx.qlogo.cn/",
          "userName":"",
          "wxMinipOpenId":"",
          "wxOpenId":"",
          "wxUnionId":""
        },
        "userProjectHistoryDTOS":[
          {
            "projectId":1,
            "projectName":"",
            "storeType":1,
            "storeUrl":"https://mall-prod-app-linli.timesgroup.cn/app/index"
          }
        ]
      },
      "message":"success"
    }

获取会员 id - memberId

  • 接口:https://m-center-prod-linli.timesgroup.cn/times/member-bff/member/api-c/v1/member/detailByPhone?phone=13412341234&phoneArea=86
  • 请求方法:GET
  • 请求参数
    // 把 url 中的 phone 值替换为自己的手机号码
  • 请求头加入上一步返回来的 token 拼接在 Bearer 后,如下:
    {
      headers: {
        Authorization: "Bearer eyJhbGciOiJSU"
      }
    }
  • 返回结果,只需要拿到结果中的 memberId
    {
      "code": 200,
      "data": {
        "cardNo": "",
        "cardType": "111",
        "couponNum": 0,
        "createTime": 1702007451000,
        "createUser": 2,
        "email": "",
        "grow": 0,
        "integral": 1,
        "integralTotal": 1,
        "isDeleted": 0,
        "isEnable": 0,
        "isShowRealName": 0,
        "memberCardRelats": [
          {
            "createTime": 1702007451000,
            "createUser": 2,
            "expirationTime": 1,
            "grow": 300,
            "id": 1,
            "isDeleted": 0,
            "levelId": 1,
            "levelName": "普通会员",
            "memberCardId": 3,
            "memberCardImage": "",
            "memberCardName": "时代邻里会员卡",
            "memberCode": "",
            "memberId": 1,
            "rangeBegin": 0,
            "rangeEnd": 5000,
            "updateTime": 1702187857000,
            "updateUser": 2,
            "upgradedTime": 1702007451000
          }
        ],
        "memberCode": "1",
        "memberId": 1,
        "memberIdStr": "1",
        "memberImage": "https://thirdwx.qlogo.cn/mmopen/vi_32",
        "memberName": "",
        "memberSources": [
          {
            "createTime": 1702007451000,
            "createUser": 2,
            "id": 1,
            "isDeleted": 0,
            "memberId": 1,
            "sourceCode": "linlipro",
            "updateTime": 1702007451000,
            "updateUser": 2
          }
        ],
        "memberType": 0,
        "memo": "",
        "phone": "",
        "phoneAreaCode": "86",
        "realName": "微信用户",
        "sex": 0,
        "updateTime": 1735185253000,
        "updateUser": 2
      },
      "message": "success"
    }

签到

  • 接口:https://m-center-prod-linli.timesgroup.cn/times/member-bff/user-behaviour//api-c/v1/user-behaviour/collect
  • 请求方法:POST
  • 请求头
    // 第二步返回来的 token 拼接在 `Bearer `后
    {
      headers: {
        Authorization: 'Bearer eyJhbGciOiJSU',
        Content-Type: 'application/json'
      }
    }
  • 请求参数
    // memberId 为第三步返回的 memberId
    // createTime、sign为当前时间 格式:YYYY-MM-DD HH:mm:ss
    {
      "behaviourId": 10,
      "clientCode": "sys_linlibang",
      "createTime": "2024-11-27 08:19:45",
      "mapPamater": {
        "sign": "2024-11-27 08:19:45"
      },
      "memberId": "12332131232133"
    }
  • 返回结果
    {
      code: 200,
      data: true,
      message: 'success'
    }

自动刷新 token

  • token 过期时间,当前测试为 8 小时过期,需要在过期前刷新 token,下次签到就不用重新获取验证码登录(理论上可以无限续期)

  • 接口:https://m-center-prod-linli.timesgroup.cn/times/auth/oauth/token

  • 请求方法:POST

  • 请求参数(FormData 格式)

    // grant_type=refresh_token&client_secret=times&client_id=app_c&scope=all&refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2iMava32uw
    // 第一次刷新:refresh_token 需要设置为第二步获取返回来的 refreshToken
    // 第一次以后的刷新:refresh_token 获取本接口返回来的 refreshToken 即可
    let formData = new FormData()
    formData.append("grant_type", "refresh_token")
    formData.append("client_secret", "times")
    formData.append("client_id", "app_c")
    formData.append("scope", "all")
    formData.append("refresh_token", "")
  • 返回结果

    {
      "code":200,
      "data":{
        "expiresIn":86399,
        "refreshToken":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2",
        "token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25",
        "tokenHead":"Bearer "
      },
      "message":"操作成功"
    }

青龙脚本使用(手动导入)

代码

  • 打开青龙面板 - 脚本管理 - 新建文件夹 times_script
  • times_script 下新建如下几个文件
// config.json

{
  "token": "",
  "refreshToken": ""
}
// times_get_code.js

const axios = require("axios")
// 获取配置文件,手机号和验证码 {"phone":"13412341234","code":"123456"}
// 如果 token 和 refreshToken 都为空时就获取 或者 isNweOne 为 true 时获取
function getTimesConfig() {
  try {
    const timesConfig = process.env.TIMES_CONFIG
    if (timesConfig) {
      console.log("获取到的API_KEY环境变量值为:", timesConfig)
      console.log("API_KEY环境变量值转为JSON格式:", JSON.parse(timesConfig))
      return JSON.parse(timesConfig)
    } else {
      console.log("未获取到API_KEY环境变量")
      return ""
    }
  } catch (error) {
    console.log("获取timesConfig时出错:", error.message)
  }
}

// 获取验证码
async function getVerifyCode() {
  try {
    let timesConfig = getTimesConfig()
    let params = {
      phoneNumber: timesConfig.phone,
      areaCode: "86",
    }
    console.log("请求参数:", params)
    let { data } = await axios.post(
      "https://m-center-prod-linli.timesgroup.cn/times/pub-center/verifycode/api/v1/send",
      params,
      {}
    )
    console.log("获取验证码结果:", data)
  } catch (error) {
    console.log("获取验证码时出错:", error.message)
  }
}

// 获取验证码
getVerifyCode()
// times_get_token.js

const fs = require("fs")
const path = require("path")
const axios = require("axios")
// 获取配置文件,手机号和验证码 {"phone":"13412341234","code":"123456"}
function getTimesConfig() {
  try {
    const timesConfig = process.env.TIMES_CONFIG
    if (timesConfig) {
      console.log("获取到的API_KEY环境变量值为:", timesConfig)
      console.log("API_KEY环境变量值转为JSON格式:", JSON.parse(timesConfig))
      return JSON.parse(timesConfig)
    } else {
      console.log("未获取到API_KEY环境变量")
      return ""
    }
  } catch (error) {
    console.log("获取timesConfig时出错:", error.message)
  }
}

// 定义config.json文件的路径,假设它与脚本在同一目录下
const configPath = path.join(__dirname, "config.json")

// 读取文件
function readConfigFile() {
  let existingData = JSON.parse(fs.readFileSync(configPath, "utf8"))
  console.log("读取config.json数据:", existingData)
  return existingData
}

// 写入数据
function writeConfigFile(data) {
  try {
    let { token, refreshToken } = data
    console.log("token", token, refreshToken)
    let configData = readConfigFile()
    if (token !== "") {
      configData.token = token
    }
    if (refreshToken !== "") {
      configData.refreshToken = refreshToken
    }
    fs.writeFileSync(configPath, JSON.stringify(configData, null, 2), "utf8")
    console.log("修改后的config.json数据:", readConfigFile())
  } catch (error) {
    console.log("修改config.json时出错:", error.message)
  }
}

// 通过验证码获取token
async function getNewToken() {
  let timesConfig = getTimesConfig()
  let params = {
    grant_type: "sms_code",
    scope: "all",
    client_id: "mini_charge",
    client_secret: "times",
    username: timesConfig.phone,
    password: timesConfig.code,
    areaCode: "86",
  }

  let { data } = await axios.post(
    "https://m-center-prod-linli.timesgroup.cn/times/times-bff/bff/api-c/v1/oauth/token",
    params,
    {}
  )
  console.log("获取token结果:", data)
  if (data.code === 200) {
    writeConfigFile({
      token: data.data.authUserDTO.token,
      refreshToken: data.data.authUserDTO.refreshToken,
    })
  } else {
    console.log("获取token时出错:", data.message)
  }
}
// 获取token
getNewToken()
// times_refresh_token.js

const fs = require("fs")
const path = require("path")
const axios = require("axios")
const FormData = require("form-data")
// 定义config.json文件的路径,与脚本在同一目录下
const configPath = path.join(__dirname, "config.json")

// 读取文件
function readConfigFile() {
  let existingData = JSON.parse(fs.readFileSync(configPath, "utf8"))
  console.log("读取config.json数据:", existingData)
  return existingData
}

// 写入数据
function writeConfigFile(data) {
  try {
    let { token, refreshToken } = data
    console.log("token", token, refreshToken)
    let configData = readConfigFile()
    if (token !== "") {
      configData.token = token
    }
    if (refreshToken !== "") {
      configData.refreshToken = refreshToken
    }
    fs.writeFileSync(configPath, JSON.stringify(configData, null, 2), "utf8")
    console.log("修改后的config.json数据:", readConfigFile())
  } catch (error) {
    console.log("读取或修改config.json时出错:", error.message)
  }
}

// 刷新token
async function startRefreshToken() {
  let configData = readConfigFile()
  console.log("获取configData:", configData)
  if (configData.token) {
    let formData = new FormData()
    formData.append("grant_type", "refresh_token")
    formData.append("client_secret", "times")
    formData.append("client_id", "app_c")
    formData.append("scope", "all")
    formData.append("refresh_token", configData.refreshToken)
    let config = {
      headers: {
        "Content-type": "multipart/form-data",
      },
    }
    console.log("请求参数:", formData)
    let { data } = await axios.post(
      "https://m-center-prod-linli.timesgroup.cn/times/auth/oauth/token",
      formData,
      config
    )
    console.log("刷新token结果:", data)
    if (data.code === 200) {
      writeConfigFile({
        token: data.data.token,
        refreshToken: data.data.refreshToken,
      })
    } else {
      console.log("刷新tokenn时出错:", data.message)
    }
  }
}
// 刷新token
startRefreshToken()
// times_signin.js

const fs = require("fs")
const path = require("path")
const axios = require("axios")
// 定义config.json文件的路径,假设它与脚本在同一目录下
const configPath = path.join(__dirname, "config.json")

// 读取文件
function readConfigFile() {
  let existingData = JSON.parse(fs.readFileSync(configPath, "utf8"))
  console.log("读取config.json数据:", existingData)
  return existingData
}

function getFormattedDateTime() {
  const now = new Date()
  const year = now.getFullYear()
  const month = (now.getMonth() + 1).toString().padStart(2, "0")
  const day = now.getDate().toString().padStart(2, "0")
  const hour = now.getHours().toString().padStart(2, "0")
  const minute = now.getMinutes().toString().padStart(2, "0")
  const second = now.getSeconds().toString().padStart(2, "0")
  return `${year}-${month}-${day} ${hour}:${minute}:${second}`
}

// 签到
async function startSignin() {
  let configData = readConfigFile()
  let currentTime = getFormattedDateTime()
  console.log(currentTime)
  if (configData.token) {
    let params = {
      behaviourId: 10,
      clientCode: "sys_linlibang",
      createTime: currentTime,
      mapPamater: {
        sign: currentTime,
      },
      memberId: "3884010548647428143", //
    }
    let config = {
      headers: {
        Authorization: "Bearer " + configData.token,
        "Content-type": "application/json",
      },
    }
    try {
      console.log("请求参数:", params)
      let { data } = await axios.post(
        "https://m-center-prod-linli.timesgroup.cn/times/member-bff/user-behaviour//api-c/v1/user-behaviour/collect",
        params,
        config
      )
      console.log("签到结果:", data)
      if (data.code === 200) {
        console.log("签到成功!")
      } else {
        console.log("签到n时出错:", data.message)
      }
    } catch (err) {
      console.log("err", err)
    }
  }
}
// 签到
startSignin()

配置

  • 青龙面板 - 环境变量 - 创建变量 - 填入如下信息,phone 为自己已经注册邻里邦的手机号码(必填),code 为稍后运行 times_get_code.js 脚本后发送到手机上的验证码(首次配置时可为空,待获取验证码后再填写)
    • 名称:TIMES_CONFIG
    • 值:{"phone":"13412341234","code":"103282"}
  • 注意:当前配置只针对 +86 的号码,非 +86 请自行在代码中修改区号

添加定时任务

  • 任务一

    • 名称:邻里邦获取验证码
    • 命令/脚本:task times_script/times_get_code.js
    • 定时规则: 0 0 1 12 *
  • 任务二

    • 名称:邻里邦获取token
    • 命令/脚本:task times_script/times_get_token.js
    • 定时规则: 0 0 1 12 *
  • 任务三

    • 名称:邻里邦刷新token(每三小时执行一次)
    • 命令/脚本:task times_script/times_refresh_token.js
    • 定时规则: 0 */3 * * *
  • 任务四

    • 名称:邻里邦签到
    • 命令/脚本:task times_script/times_signin.js
    • 定时规则: 5 9 * * *

使用步骤

  • 运行前确认 配置 中的 TIMES_CONFIG 配置已填写 phone
  • 运行 任务一 获取验证码,把手机上获取到的验证码填写到 配置 中的 TIMES_CONFIGcode
  • 运行 任务二 获取 token,token 会自动填写到 config.json 中
  • 运行 任务三 刷新 token,任务会每隔三小时自动刷新一次
  • 运行 任务四 进行签到,任务会在每天早上 9 点 5 分自动签到
  • 到此不出意外的话签到任务会每天自动运行,不再需要人工干涉
  • 如果出先签到不成功,请按照 使用步骤 在运行一次,如果还不行那就是接口改变了,需要重新调试代码

  目录