青龙面板阿里云盘自动签到

509 阅读5分钟

阿里云盘自动签到

/*
cron "0 9 * * *" autoSignin.js, tag=阿里云盘签到
*/ // 设置定时任务,每天上午9点执行 autoSignin.js 脚本,并为其添加“阿里云盘签到”的标签

const axios = require('axios') // 引入 axios 库,用于发送 HTTP 请求
const { initInstance, getEnv, updateCkEnv } = require('./qlApi.js') // 从 qlApi.js 文件中引入三个方法
const notify = require('./sendNotify') // 引入 sendNotify 文件,用于发送通知

const updateAccesssTokenURL = 'https://auth.aliyundrive.com/v2/account/token' // 定义更新 access_token 的 API 地址
const signinURL =
  'https://member.aliyundrive.com/v1/activity/sign_in_list?_rx-s=mobile' // 定义签到的 API 地址
const rewardURL =
  'https://member.aliyundrive.com/v1/activity/sign_in_reward?_rx-s=mobile' // 定义领取奖励的 API 地址

// 使用 refresh_token 更新 access_token
function updateAccesssToken(queryBody, remarks) {
  const errorMessage = [remarks, '更新 access_token 失败'] // 定义错误消息数组
  return axios(updateAccesssTokenURL, { // 使用 axios 发送 POST 请求
    method: 'POST',
    data: queryBody,
    headers: { 'Content-Type': 'application/json' }
  })
    .then(d => d.data) // 获取响应数据
    .then(d => {
      const { code, message, nick_name, refresh_token, access_token } = d
      if (code) {
        if (
          code === 'RefreshTokenExpired' ||
          code === 'InvalidParameter.RefreshToken'
        )
          errorMessage.push('refresh_token 已过期或无效')
        else errorMessage.push(message)
        return Promise.reject(errorMessage.join(', '))
      }
      return { nick_name, refresh_token, access_token }
    })
    .catch(e => {
      errorMessage.push(e.message)
      return Promise.reject(errorMessage.join(', '))
    })
}

//签到列表
function sign_in(access_token, remarks) {
  const sendMessage = [remarks]
  return axios(signinURL, {
    method: 'POST',
    data: {
      isReward: false
    },
    headers: {
      Authorization: access_token,
      'Content-Type': 'application/json'
    }
  })
    .then(d => d.data)
    .then(async json => {
      if (!json.success) {
        sendMessage.push('签到失败', json.message)
        return Promise.reject(sendMessage.join(', '))
      }

      sendMessage.push('签到成功')

      const { signInLogs, signInCount } = json.result
      const currentSignInfo = signInLogs[signInCount - 1] // 当天签到信息

      sendMessage.push(`本月累计签到 ${signInCount} 天`)

      // 未领取奖励列表
      const rewards = signInLogs.filter(
        v => v.status === 'normal' && !v.isReward
      )

      if (rewards.length) {
        for await (reward of rewards) {
          const signInDay = reward.day
          try {
            //获取奖励
            const rewardInfo = await getReward(access_token, signInDay)
            sendMessage.push(
              `第${signInDay}天奖励领取成功: 获得${rewardInfo.name || ''}${
                rewardInfo.description || ''
              }`
            )
          } catch (e) {
            sendMessage.push(`第${signInDay}天奖励领取失败:`, e)
          }
        }
      } else if (currentSignInfo.isReward) {
        sendMessage.push(
          `今日签到获得${currentSignInfo.reward.name || ''}${
            currentSignInfo.reward.description || ''
          }`
        )
      }

      return sendMessage.join(', ')
    })
    .catch(e => {
      sendMessage.push('签到失败')
      sendMessage.push(e.message)
      return Promise.reject(sendMessage.join(', '))
    })
}

// 领取奖励
function getReward(access_token, signInDay) {
  return axios(rewardURL, {
    method: 'POST',
    data: { signInDay },
    headers: {
      authorization: access_token,
      'Content-Type': 'application/json'
    }
  })
    .then(d => d.data)
    .then(json => {
      if (!json.success) {
        return Promise.reject(json.message)
      }

      return json.result
    })
}

// 获取环境变量,返回initInstance方法生成的instance,及refreshtoken数组
async function getRefreshToken() {
  let instance = null
  try {
    //initInstance,qlApi导入的方法
    instance = await initInstance()
  } catch (e) {}

  //从环境变量中获取 refreshToken 的值,不存在返回空数组
  let refreshToken = process.env.refreshToken || []
  try {
    //调用qlApi导入的getEnv方法,获取refreshToken的值,覆盖掉上面的配置
    if (instance) refreshToken = await getEnv(instance, 'refreshToken')
  } catch (e) {}

  let refreshTokenArray = []

  if (Array.isArray(refreshToken)) refreshTokenArray = refreshToken
  else if (refreshToken.indexOf('&') > -1)
    refreshTokenArray = refreshToken.split('&')
  else if (refreshToken.indexOf('\n') > -1)
    refreshTokenArray = refreshToken.split('\n')
  else refreshTokenArray = [refreshToken]

  if (!refreshTokenArray.length) {
    console.log('未获取到refreshToken, 程序终止')
    process.exit(1)
  }

  return {
    instance,
    refreshTokenArray
  }
}

//立即执行的异步函数表达式 (IIFE)语法
!(async () => {
  //获取环境变量
  const { instance, refreshTokenArray } = await getRefreshToken()

  const message = []
  let index = 1
  for await (refreshToken of refreshTokenArray) {
    let remarks = refreshToken.remarks || `账号${index}`
    const queryBody = {
      grant_type: 'refresh_token',
      refresh_token: refreshToken.value || refreshToken
    }

    try {
      const { nick_name, refresh_token, access_token } =
        await updateAccesssToken(queryBody, remarks)

      if (nick_name && nick_name !== remarks)
        remarks = `${nick_name}(${remarks})`

      // 更新环境变量
      if (instance) {
        let params = {
          name: refreshToken.name,
          value: refresh_token,
          remarks: refreshToken.remarks || nick_name // 优先存储原有备注信息
        }
        // 新版青龙api
        if (refreshToken.id) {
          params.id = refreshToken.id
        }
        // 旧版青龙api
        if (refreshToken._id) {
          params._id = refreshToken._id
        }
        //调用导入的方法
        await updateCkEnv(instance, params)
      }

      //签到
      const sendMessage = await sign_in(access_token, remarks)
      console.log(sendMessage)
      console.log('\n')
      message.push(sendMessage)
    } catch (e) {
      console.log(e)
      console.log('\n')
      message.push(e)
    }
    index++
  }
  await notify.sendNotify(`阿里云盘签到`, message.join('\n'))
})()

立即执行的异步函数表达式

立即执行的异步函数表达式 (IIFE)。下面是对这种语法的详细分析:

!(
  ()=>{}
)()
  1. IIFE (立即执行的函数表达式) :
    • IIFE 是 "Immediately Invoked Function Expression" 的缩写,意为立即执行的函数表达式。
    • 它是一个在定义后立即执行的 JavaScript 函数。
    • 这种结构可以创建一个新的作用域,防止变量污染全局作用域。
  1. async:
    • async 关键字用于声明一个函数是异步的,这意味着函数的执行不会阻塞后面的代码。
    • 异步函数通常与 await 关键字一起使用,以等待一个 Promise 的结果。
  1. () => {} :
    • 这是一个箭头函数表达式,它提供了一种更简洁的方式来写函数。
    • 箭头函数有一些特性,如没有自己的 this、arguments、super 或 new.target。
  1. () :
    • 在箭头函数后面的括号 () 使得这个函数立即执行。
    • 这是 IIFE 的核心部分,因为它使得函数在定义后立即执行。

在这段代码中,IIFE 用于执行一系列的异步操作,如获取环境变量、更新 access_token、签到等。使用异步 IIFE 允许我们在函数内部使用 await 关键字,而不需要将整个外部函数声明为异步的。这种结构在处理一系列的异步操作时非常有用,尤其是当我们想要立即执行这些操作,而不是等待某个事件或回调。

在JavaScript中,在立即执行函数表达式 (IIFE) 前加上一个 ! 是一种常见的技巧,用于确保代码的正确解析和执行。这里是为什么:

  1. 避免语法错误:当两个文件或两段代码被压缩或合并到一起时,如果前一个文件或代码段没有正确地以分号结束,直接使用一个括号 ( 开始的IIFE可能会导致语法错误。例如:
function foo() {
  // some code
} // 假设这里没有分号
(function() {
  // IIFE
})();

上述代码会被解析为 function foo() { /.../ }(function() { /.../ })();,这是一个语法错误。

但是,如果你在IIFE前加上一个 !,它会确保代码正确地解析,即使前一个代码段没有正确地以分号结束。

function foo() {
  // some code
} // 假设这里没有分号
!function() {
  // IIFE
}();
  1. 减少文件大小:在某些情况下,使用 ! 作为IIFE的前缀比使用传统的 ; 更节省空间,这在文件压缩时可能是有益的。
  2. 明确意图:使用 ! 可以向阅读代码的人明确表示这是一个IIFE,这有助于提高代码的可读性。

总之,! 在这里主要是为了确保代码的安全执行,并避免可能的语法错误。这是JavaScript社区中的一个常见模式,尤其是在库和框架的源代码中。

let refreshToken = process.env.refreshToken || []

这段代码 let refreshToken = process.env.refreshToken || [] 使用了几个JavaScript的基本概念和特性。下面是对这些概念和特性的详细介绍:

  1. let 关键字:
    • let 是用于声明变量的关键字。
    • 与 var 不同,let 声明的变量具有块级作用域,这意味着它们只在最近的一对 {} 代码块内可见。
    • let 声明的变量不会被提升,这意味着你不能在声明之前使用它。
  1. process 对象:
    • process 是一个全局对象,提供了有关当前Node.js进程的信息。
    • 它可以用于访问环境变量、命令行参数、当前进程的状态等。
  1. process.env:
    • process.env 是一个对象,包含了当前shell的所有环境变量。
    • 这通常用于从运行环境中读取配置设置,例如数据库连接字符串、API密钥等。
  1. process.env.refreshToken:
    • 这是尝试从环境变量中读取名为 refreshToken 的值。
    • 如果这个环境变量存在,它的值将被赋给 refreshToken 变量。
  1. || (逻辑或操作符) :
    • || 是JavaScript中的逻辑或操作符。
    • 它返回其左侧操作数的值,如果该值为真值(truthy),否则返回其右侧操作数的值。
    • 在这里,它用于提供一个默认值。如果 process.env.refreshToken 为假值(例如 undefined、null、0、"" 等),则 refreshToken 变量将被赋值为一个空数组 []。

综上所述,这段代码的目的是尝试从环境变量中获取 refreshToken 的值。如果该值不存在或为假值,refreshToken 变量将被赋值为一个空数组。