Files
yenai-plugin/model/Pixiv/api.js
2024-04-03 20:43:35 +08:00

247 lines
5.9 KiB
JavaScript

import request, { qs } from '../../lib/request/request.js'
import moment from 'moment'
import { Config } from '../../components/index.js'
import md5 from 'md5'
const CLIENT_ID = 'MOBrBDS8blbauoSck0ZfDbtuzpyT'
const CLIENT_SECRET = 'lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj'
const HASH_SECRET = '28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c'
export default class PixivApi {
constructor (refresh_token) {
this.baseUrl = 'https://app-api.pixiv.net/'
this.headers = {
'User-Agent': 'PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)',
'Accept-Language': Config.pixiv.language,
'App-OS': 'ios',
'App-OS-Version': '14.6',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': '*/*',
'Connection': 'Keep-Alive'
}
this._once = false
this.refresh_token = refresh_token
this.access_token = null
this.auth = null
}
async login () {
if (!this.refresh_token) {
throw Error('[Yenai][Pixiv] 未配置refresh_token刷新令牌')
}
const local_time = moment().format()
const headers = {
...this.headers,
'X-Client-Time': local_time,
'X-Client-Hash': md5(`${local_time}${HASH_SECRET}`)
}
const data = {
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: this.refresh_token
}
const { response, error } = await request.post('https://oauth.secure.pixiv.net/auth/token', {
data,
headers
}).then(res => res.json())
if (error) throw Error(`[Yenai][Pixiv]login Error Response: ${error}`)
this.access_token = response.access_token
this.refresh_token = response.refresh_token
this.auth = response
if (this.access_token) {
const { id, name, account } = this.auth.user
logger.info(`[Yenai][Pixiv]login ${logger.yellow(`${name}(${id}) @${account}`)} ${logger.green('success')}`)
} else {
logger.error(`[Yenai][Pixiv]login ${logger.red('fail')}`)
}
}
async request (target, options = {}, caching = false) {
if (!this.auth) await this.login()
try {
return await this._get(target, options, caching)
} catch (error) {
if (this._once) {
this._once = false
throw error
}
await this.login()
this._once = true
return await this._get(target, options, caching)
}
}
async _get (target, options = {}, cache) {
const headers = {
...this.headers,
Authorization: `Bearer ${this.access_token}`
}
// 读取缓存
const cacheUrl = options.params ? target + '?' + qs(options.params) : target
const cacheKey = `yenai:pixiv:cache:${cacheUrl}`
const cacheData = await redis.get(cacheKey)
if (cacheData) return JSON.parse(cacheData)
// 请求
let data = await request[options.data ? 'post' : 'get'](this.baseUrl + target, {
headers,
...options,
statusCode: 'json'
})
// 写入缓存
if (cache) {
redis.set(cacheKey, JSON.stringify(data), {
EX: timeToSeconds(cache)
})
}
return data
}
async tags () {
return this.request('v1/trending-tags/illust')
}
async rank ({
mode = 'week',
date = moment().subtract(moment().utcOffset(9).hour() >= 12 ? 1 : 2, 'days').format('YYYY-MM-DD'),
page = 1,
size = 30
}) {
return this.request('v1/illust/ranking', {
params: {
mode,
date,
offset: (page - 1) * size
}
}, getNoonTomorrow())
}
async illust ({ id }) {
return this.request('v1/illust/detail', {
params: {
illust_id: id
}
})
}
async member ({ id }) {
return this.request('v1/user/detail', {
params: {
illust_id: id
}
})
}
async member_illust ({
id,
page = 1,
size = 30,
illust_type = 'illust'
}) {
return this.request('v1/user/illusts', {
params: {
user_id: id,
type: illust_type,
offset: (page - 1) * size
}
})
}
async search ({
word,
page = 1,
size = 30,
order = 'date_desc',
mode = 'partial_match_for_tags',
include_translated_tag_results = true
}) {
return this.request('v1/search/illust', {
params: {
word,
search_target: mode,
sort: order,
offset: (page - 1) * size,
include_translated_tag_results
}
})
}
async search_user ({
word,
page = 1,
size = 30
}) {
return await this.request(
'v1/search/user',
{
params: {
word,
offset: (page - 1) * size
}
}
)
}
async related ({
id,
page = 1,
size = 30
}) {
return await this.request(
'v2/illust/related',
{
params: {
illust_id: id,
offset: (page - 1) * size
}
}
)
}
async illustRecommended (params = {}) {
return await this.request('v1/illust/recommended', params)
}
}
/**
*
* @param time
*/
function timeToSeconds (time) {
let seconds = 0
let timeArray = time.split(' ')
for (let i = 0; i < timeArray.length; i++) {
let unit = timeArray[i].charAt(timeArray[i].length - 1)
let value = parseInt(timeArray[i].substring(0, timeArray[i].length - 1))
switch (unit) {
case 's':
seconds += value
break
case 'm':
seconds += value * 60
break
case 'h':
seconds += value * 60 * 60
break
case 'd':
seconds += value * 60 * 60 * 24
break
default:
break
}
}
return seconds
}
/**
*
*/
function getNoonTomorrow () {
const now = moment() // 获取当前时间
const noonToday = moment().startOf('day').add(12, 'hours') // 获取今天中午12点的时间
const noonTomorrow = moment().add(1, 'day').startOf('day').add(12, 'hours') // 获取明天中午12点的时间
let time = now < noonToday
? noonToday.diff(now, 'hours')
: noonTomorrow.diff(now, 'hours')
if (time > 12) time = 12
return time + 'h'
}