mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2026-08-12 17:10:37 +00:00
Compare commits
5 Commits
6732fc7c32
...
8f4873f2e2
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f4873f2e2 | |||
| a8b45a8b8b | |||
| 1a70281c76 | |||
|
|
f5f4ff6eda | ||
|
|
eb1b5ba0ea |
5
.github/workflows/issue-manage.yml
vendored
5
.github/workflows/issue-manage.yml
vendored
@ -19,10 +19,11 @@ jobs:
|
||||
with:
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
## 快速链接
|
||||
感谢您在该仓库提出问题!我们非常重视您的反馈,并希望尽快为您提供帮助。
|
||||
|
||||
在我们处理您的问题之前,您可以先查看以下资源:
|
||||
如果您在一周左右的时间里没有收到我们的回复,请在下方留言,以便我们注意到。我们的大部分审核都是志愿者,有时难免会有疏漏。
|
||||
|
||||
你可能会想要尝试以下方法来获得更快的帮助:
|
||||
- 📖 查看[项目文档](https://neteasecloudmusicapienhanced.js.org)
|
||||
- 💬 加入[QQ交流群](https://qm.qq.com/q/TpeP9Uv2yk)
|
||||
- 🔍 搜索[现有issues](https://github.com/neteasecloudmusicapienhanced/api-enhanced/issues) 看是否有类似问题
|
||||
|
||||
33
.github/workflows/opencode.yml
vendored
Normal file
33
.github/workflows/opencode.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
name: opencode
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
opencode:
|
||||
if: |
|
||||
contains(github.event.comment.body, ' /oc') ||
|
||||
startsWith(github.event.comment.body, '/oc') ||
|
||||
contains(github.event.comment.body, ' /opencode') ||
|
||||
startsWith(github.event.comment.body, '/opencode')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run opencode
|
||||
uses: anomalyco/opencode/github@latest
|
||||
env:
|
||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||
with:
|
||||
model: opencode/deepseek-v4-flash-free
|
||||
253
interface.d.ts
vendored
253
interface.d.ts
vendored
@ -37,6 +37,33 @@ export const enum SubAction {
|
||||
unsub = 0,
|
||||
}
|
||||
|
||||
export function ad_get(
|
||||
params: { type_ids?: string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function ad_listening_rights(
|
||||
params: RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function ad_listening_rights_gain(
|
||||
params: {
|
||||
reqUid?: string
|
||||
creativeType?: string | number
|
||||
exposureTime?: string | number
|
||||
clickTime?: string | number
|
||||
rightsGainMethod?: string | number
|
||||
rightsGainDuration?: string | number
|
||||
extraRightsGainMethod?: string | number
|
||||
extraRightsGainDuration?: string | number
|
||||
nextRightsGainDuration?: string | number
|
||||
source?: string
|
||||
rightsExtJson?: string
|
||||
appInfo?: string
|
||||
installed?: string | number
|
||||
type_ids?: string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function activate_init_profile(
|
||||
params: { nickname: string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
@ -119,6 +146,14 @@ export function album_sublist(
|
||||
params: MultiPageConfig & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function api(
|
||||
params: {
|
||||
uri: string
|
||||
data?: string | Record<string, unknown>
|
||||
crypto?: string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function artist_album(
|
||||
params: { id: string | number } & MultiPageConfig & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
@ -271,10 +306,21 @@ export function batch(
|
||||
params: { [index: string]: unknown } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function captcha_safe_sent(
|
||||
params: { ctcode?: number | string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function captcha_sent(
|
||||
params: { phone: string; ctcode?: number | string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function captcha_sent_v1(
|
||||
params: {
|
||||
phone: number | string
|
||||
ctcode?: number | string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function captcha_verify(
|
||||
params: {
|
||||
ctcode?: number | string
|
||||
@ -387,6 +433,14 @@ export function comment(
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function comment_add(
|
||||
params: {
|
||||
id: string | number
|
||||
type: CommentType
|
||||
content: string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function comment_album(
|
||||
params: {
|
||||
id: string | number
|
||||
@ -395,6 +449,14 @@ export function comment_album(
|
||||
RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function comment_delete(
|
||||
params: {
|
||||
id: string | number
|
||||
type: CommentType
|
||||
cid: string | number
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function comment_dj(
|
||||
params: {
|
||||
id: string | number
|
||||
@ -469,6 +531,15 @@ export function comment_playlist(
|
||||
RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function comment_reply(
|
||||
params: {
|
||||
id: string | number
|
||||
type: CommentType
|
||||
cid: string | number
|
||||
content: string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function comment_video(
|
||||
params: {
|
||||
id: string | number
|
||||
@ -490,6 +561,21 @@ export function daily_signin(
|
||||
params: { type?: DailySigninType } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function decrypt(
|
||||
params: {
|
||||
crypto?: string
|
||||
data?: string
|
||||
hexString?: string
|
||||
isReq?: boolean | string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function device_kickoff(
|
||||
params: { deviceKey: string | number; captcha?: string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function device_list(params: RequestBaseConfig): Promise<Response>
|
||||
|
||||
export function digitalAlbum_ordering(
|
||||
params: {
|
||||
payment: string
|
||||
@ -618,6 +704,10 @@ export function dj_toplist_popular(
|
||||
params: { limit?: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function eapi_decrypt(
|
||||
params: { hexString: string; isReq?: boolean | string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function event(
|
||||
params: { pagesize?: number; lasttime?: number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
@ -626,6 +716,20 @@ export function event_del(
|
||||
params: { evId: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export const enum EventPrivacy {
|
||||
everyone = 0,
|
||||
following = 1,
|
||||
onlyMe = 2,
|
||||
mutualFollowing = 6,
|
||||
}
|
||||
|
||||
export function event_privacy(
|
||||
params: {
|
||||
evId: string | number
|
||||
privacy: EventPrivacy
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function event_forward(
|
||||
params: {
|
||||
forwords: string
|
||||
@ -665,6 +769,8 @@ export function hot_topic(
|
||||
params: MultiPageConfig & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function inner_version(params: RequestBaseConfig): Promise<Response>
|
||||
|
||||
export function like(
|
||||
params: {
|
||||
like?: 'true' | 'false' | boolean
|
||||
@ -678,6 +784,10 @@ export function likelist(
|
||||
params: { uid: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function listentogether_status(
|
||||
params: RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function login(
|
||||
params: { email: string; password: string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
@ -724,6 +834,17 @@ export function lyric_new(
|
||||
params: { id: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function middle_play_do_lottery(
|
||||
params: {
|
||||
activityId?: string | number
|
||||
drawCount?: string | number
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function middle_play_lottery_remain_chance(
|
||||
params: { activityId?: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function msg_comments(
|
||||
params: {
|
||||
uid: string | number
|
||||
@ -946,6 +1067,18 @@ export function register_cellphone(
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function register_checktoken_v2(
|
||||
params: { refresh?: boolean | string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function register_checktoken_v3(
|
||||
params: { refresh?: boolean | string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function register_xeapikey(
|
||||
params: { deviceId?: string; currentKeyVersion?: string } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function related_allvideo(
|
||||
params: { id: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
@ -954,6 +1087,67 @@ export function related_playlist(
|
||||
params: { id: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function relay_play_state_submit(
|
||||
params: {
|
||||
id: string | number
|
||||
sessionId?: string
|
||||
progress?: string | number
|
||||
playMode?: string
|
||||
type?: string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function rep_ugc_activity_collect(
|
||||
params: { activityId?: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function rep_ugc_activity_get(
|
||||
params: RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function rep_ugc_exam_info_get(
|
||||
params: { examType: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function rep_ugc_exam_question_single_get(
|
||||
params: {
|
||||
examType: string | number
|
||||
taskId: string | number
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function rep_ugc_exam_result_get(
|
||||
params: {
|
||||
examType: string | number
|
||||
taskId: string | number
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function rep_ugc_exam_start(
|
||||
params: { examType: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function rep_ugc_exam_submit(
|
||||
params: {
|
||||
examType: string | number
|
||||
taskId: string | number
|
||||
questionId: string | number
|
||||
answer: string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
declare const rep_ugc_user_collect_vip: (
|
||||
params: { activityId?: string | number } & RequestBaseConfig,
|
||||
) => Promise<Response>
|
||||
|
||||
export { rep_ugc_user_collect_vip as 'rep_ugc_user_collect-vip' }
|
||||
|
||||
export function rep_ugc_user_get(params: RequestBaseConfig): Promise<Response>
|
||||
|
||||
export function rep_ugc_user_sign(params: RequestBaseConfig): Promise<Response>
|
||||
|
||||
export function rep_ugc_user_vip(params: RequestBaseConfig): Promise<Response>
|
||||
|
||||
export const enum ResourceType {
|
||||
mv = 1,
|
||||
dj = 4,
|
||||
@ -979,6 +1173,22 @@ export function scrobble(
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function scrobble_v1(
|
||||
params: {
|
||||
id: string | number
|
||||
time: string | number
|
||||
total?: string | number
|
||||
sourceid?: string | number
|
||||
sourceId?: string | number
|
||||
source?: string
|
||||
name?: string
|
||||
artist?: string
|
||||
bitrate?: string | number
|
||||
level?: string
|
||||
vip?: boolean | string
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function search(
|
||||
params: {
|
||||
keywords: string
|
||||
@ -1267,6 +1477,25 @@ export function user_event(
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export interface UserEventAllResponse {
|
||||
code: number
|
||||
events: Record<string, unknown>[]
|
||||
/** Upstream account statistic; it can include events no longer returned. */
|
||||
size: number | null
|
||||
/** Number of unique events in `events`. */
|
||||
retrievedCount: number
|
||||
/** Positive difference between `size` and `retrievedCount`. */
|
||||
unavailableCount: number | null
|
||||
sizeMismatch: boolean | null
|
||||
pageCount: number
|
||||
more: false
|
||||
lasttime: string | number | null
|
||||
}
|
||||
|
||||
export function user_event_all(
|
||||
params: RequestBaseConfig,
|
||||
): Promise<Response<UserEventAllResponse>>
|
||||
|
||||
export function user_followeds(
|
||||
params: {
|
||||
uid: string | number
|
||||
@ -2292,6 +2521,10 @@ export function song_chorus(
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function song_cloud_download(
|
||||
params: { id: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function song_creators(
|
||||
params: {
|
||||
id: string | number
|
||||
@ -2403,6 +2636,18 @@ export function threshold_detail_get(
|
||||
params: RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function thinktank_audit_resource_detail(
|
||||
params: { type?: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function thinktank_audit_resource_update(
|
||||
params: {
|
||||
type?: string | number
|
||||
taskId: string | number
|
||||
judgement: string | number
|
||||
} & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function toplist_detail_v2(params: RequestBaseConfig): Promise<Response>
|
||||
|
||||
export function ugc_album_get(
|
||||
@ -2529,6 +2774,14 @@ export function verify_qrcodestatus(
|
||||
|
||||
export function vip_sign(params: RequestBaseConfig): Promise<Response>
|
||||
|
||||
export function vip_sign_detail(
|
||||
params: { timestamp: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function vip_sign_history(
|
||||
params: { type?: string | number } & RequestBaseConfig,
|
||||
): Promise<Response>
|
||||
|
||||
export function vip_sign_info(params: RequestBaseConfig): Promise<Response>
|
||||
|
||||
export function vip_tasks_v1(
|
||||
|
||||
34
module/event_privacy.js
Normal file
34
module/event_privacy.js
Normal file
@ -0,0 +1,34 @@
|
||||
// 修改本人动态的可见权限
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
|
||||
const PRIVACY_VALUES = new Set([0, 1, 2, 6])
|
||||
|
||||
module.exports = (query, request) => {
|
||||
const eventId = String(query.evId ?? '').trim()
|
||||
const rawPrivacy = String(query.privacy ?? '').trim()
|
||||
const privacy = Number(rawPrivacy)
|
||||
|
||||
if (
|
||||
!eventId ||
|
||||
!rawPrivacy ||
|
||||
!Number.isInteger(privacy) ||
|
||||
!PRIVACY_VALUES.has(privacy)
|
||||
) {
|
||||
return Promise.resolve({
|
||||
status: 400,
|
||||
body: {
|
||||
code: 400,
|
||||
message: 'evId is required and privacy must be one of 0, 1, 2, 6',
|
||||
},
|
||||
cookie: [],
|
||||
})
|
||||
}
|
||||
|
||||
const data = {
|
||||
eventId,
|
||||
privacy,
|
||||
}
|
||||
|
||||
return request(`/api/event/privacy/op`, data, createOption(query))
|
||||
}
|
||||
@ -4,9 +4,10 @@ const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
getcounts: true,
|
||||
time: query.lasttime || -1,
|
||||
limit: query.limit || 30,
|
||||
time: query.lasttime ?? -1,
|
||||
limit: query.limit ?? 30,
|
||||
total: false,
|
||||
fromRN: 'true',
|
||||
}
|
||||
return request(`/api/event/get/${query.uid}`, data, createOption(query))
|
||||
}
|
||||
|
||||
135
module/user_event_all.js
Normal file
135
module/user_event_all.js
Normal file
@ -0,0 +1,135 @@
|
||||
// 获取当前登录用户可被上游枚举的全部动态
|
||||
|
||||
const userAccount = require('./user_account.js')
|
||||
const userEvent = require('./user_event.js')
|
||||
|
||||
const PAGE_SIZE = 100
|
||||
const MAX_PAGES = 1000
|
||||
|
||||
const errorResponse = (message, cookie = []) => ({
|
||||
status: 502,
|
||||
body: {
|
||||
code: 502,
|
||||
message,
|
||||
},
|
||||
cookie,
|
||||
})
|
||||
|
||||
module.exports = async (query, request) => {
|
||||
const accountResult = await userAccount(query, request)
|
||||
const uid =
|
||||
accountResult.body?.account?.id || accountResult.body?.profile?.userId
|
||||
|
||||
if (!uid) {
|
||||
if (
|
||||
accountResult.status !== 200 ||
|
||||
(accountResult.body?.code && accountResult.body.code !== 200)
|
||||
) {
|
||||
return accountResult
|
||||
}
|
||||
|
||||
return {
|
||||
status: 401,
|
||||
body: {
|
||||
code: 401,
|
||||
message: 'A valid login cookie is required',
|
||||
},
|
||||
cookie: accountResult.cookie || [],
|
||||
}
|
||||
}
|
||||
|
||||
const cookies = [...(accountResult.cookie || [])]
|
||||
const events = []
|
||||
const eventIds = new Set()
|
||||
const cursors = new Set()
|
||||
let lasttime = -1
|
||||
let more = true
|
||||
let pageCount = 0
|
||||
let size = null
|
||||
|
||||
while (more) {
|
||||
pageCount += 1
|
||||
|
||||
const pageResult = await userEvent(
|
||||
{
|
||||
...query,
|
||||
uid,
|
||||
lasttime,
|
||||
limit: PAGE_SIZE,
|
||||
},
|
||||
request,
|
||||
)
|
||||
|
||||
cookies.push(...(pageResult.cookie || []))
|
||||
|
||||
if (pageResult.status !== 200 || pageResult.body?.code !== 200) {
|
||||
return {
|
||||
...pageResult,
|
||||
cookie: cookies,
|
||||
}
|
||||
}
|
||||
|
||||
if (pageCount === 1) {
|
||||
const reportedSize = pageResult.body.size
|
||||
const numericSize = Number(reportedSize)
|
||||
size =
|
||||
reportedSize != null && Number.isFinite(numericSize)
|
||||
? numericSize
|
||||
: null
|
||||
}
|
||||
|
||||
for (const event of pageResult.body.events || []) {
|
||||
if (event?.id == null) {
|
||||
events.push(event)
|
||||
continue
|
||||
}
|
||||
|
||||
const eventId = String(event.id)
|
||||
if (!eventIds.has(eventId)) {
|
||||
eventIds.add(eventId)
|
||||
events.push(event)
|
||||
}
|
||||
}
|
||||
|
||||
more = Boolean(pageResult.body.more)
|
||||
lasttime = pageResult.body.lasttime
|
||||
|
||||
if (more) {
|
||||
const cursor = String(lasttime ?? '')
|
||||
if (!cursor || cursors.has(cursor)) {
|
||||
return errorResponse(
|
||||
'Upstream event pagination cursor stalled',
|
||||
cookies,
|
||||
)
|
||||
}
|
||||
cursors.add(cursor)
|
||||
}
|
||||
|
||||
if (more && pageCount >= MAX_PAGES) {
|
||||
return errorResponse(
|
||||
`Upstream event pagination exceeded ${MAX_PAGES} pages`,
|
||||
cookies,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const retrievedCount = events.length
|
||||
const unavailableCount =
|
||||
size == null ? null : Math.max(size - retrievedCount, 0)
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
events,
|
||||
size,
|
||||
retrievedCount,
|
||||
unavailableCount,
|
||||
sizeMismatch: size == null ? null : size !== retrievedCount,
|
||||
pageCount,
|
||||
more: false,
|
||||
lasttime: lasttime ?? null,
|
||||
},
|
||||
cookie: cookies,
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,11 @@
|
||||
import { banner, lyric } from '@neteasecloudmusicapienhanced/api'
|
||||
import { logger } from '../util/logger.js'
|
||||
banner({ type: 0 }).then((res) => {
|
||||
import type { Response } from '@neteasecloudmusicapienhanced/api'
|
||||
import logger from '../util/logger.js'
|
||||
banner({ type: 0 }).then((res: Response) => {
|
||||
logger.info(res)
|
||||
})
|
||||
lyric({
|
||||
id: '33894312',
|
||||
}).then((res) => {
|
||||
}).then((res: Response) => {
|
||||
logger.info(res)
|
||||
})
|
||||
|
||||
@ -826,6 +826,8 @@ tags: 歌单标签
|
||||
|
||||
`lasttime` : 返回数据的 `lasttime` ,默认-1,传入上一次返回结果的 lasttime,将会返回下一页的数据
|
||||
|
||||
接口会传入官方客户端使用的 `fromRN=true`,登录用户读取自己的动态时可获得网易云允许本人查看的非公开动态。返回的 `size` 是网易云账户侧统计值,不保证等于分页后实际可获取的动态数量。
|
||||
|
||||
**接口地址 :** `/user/event`
|
||||
|
||||
**调用例子 :** `/user/event?uid=32953014` `/user/event?uid=32953014&limit=1&lasttime=1558011138743`
|
||||
@ -843,6 +845,38 @@ tags: 歌单标签
|
||||
41、21 分享视频
|
||||
```
|
||||
|
||||
### 获取当前登录用户的全部可枚举动态
|
||||
|
||||
说明 : 登录后调用此接口,会组合 `/user/account` 与 `/user/event` 的原子能力:先从 Cookie 取得当前用户 id,再自动跟随 `lasttime` 游标读取至 `more=false`。接口返回网易云允许当前用户本人查看的公开及非公开动态,不能读取其他用户的私密动态。
|
||||
|
||||
本接口不接受 `limit` 或 `lasttime`,一次请求会完成全部上游分页。账号动态较多时,请预留足够的请求时间。
|
||||
|
||||
数量字段说明:
|
||||
|
||||
- `size`:网易云返回的账户统计数量,可能包含已删除、被屏蔽、资源失效或旧类型且不再下发的记录。
|
||||
- `retrievedCount`:本次实际取得的唯一动态数量,始终等于 `events.length`。
|
||||
- `unavailableCount`:`size - retrievedCount` 的正差值;缺失记录没有返回 id,无法继续读取或修改。
|
||||
- `sizeMismatch`:`size` 与 `retrievedCount` 是否不一致。若上游未返回 `size`,相关字段为 `null`。
|
||||
- `pageCount`:本次实际请求的上游分页数量。
|
||||
|
||||
每条动态的 `privacySetting` 表示当前可见权限:`0` 为所有人,`1` 为我关注的人,`2` 为仅自己,`6` 为互相关注的人。
|
||||
|
||||
**接口地址 :** `/user/event/all`
|
||||
|
||||
**调用例子 :** `/user/event/all`
|
||||
|
||||
### 修改动态可见权限
|
||||
|
||||
说明 : 登录后调用此接口,可以修改当前账号本人发布的单条动态的可见权限。此接口只负责一次原子修改;上游没有批量修改接口。如需批量操作,可先调用 `/user/event/all` 并按 `privacySetting` 筛选,再由调用方逐条调用本接口,同时自行处理限速、失败重试和部分成功。
|
||||
|
||||
**必选参数 :** `evId` : 动态 id
|
||||
|
||||
`privacy` : 目标可见权限。`0` 为所有人,`1` 为我关注的人,`2` 为仅自己,`6` 为互相关注的人
|
||||
|
||||
**接口地址 :** `/event/privacy`
|
||||
|
||||
**调用例子 :** `/event/privacy?evId=6712917601&privacy=0`
|
||||
|
||||
### 转发用户动态
|
||||
|
||||
说明 : 登录后调用此接口 ,可以转发用户动态
|
||||
|
||||
34
test/event_privacy.test.js
Normal file
34
test/event_privacy.test.js
Normal file
@ -0,0 +1,34 @@
|
||||
const assert = require('assert')
|
||||
const eventPrivacy = require('../module/event_privacy')
|
||||
|
||||
describe('event privacy modules', () => {
|
||||
it('updates event privacy with the official client parameters', async () => {
|
||||
let captured
|
||||
const expected = { status: 200, body: { code: 200 }, cookie: [] }
|
||||
const result = await eventPrivacy(
|
||||
{ evId: '123456789012345678', privacy: '0', cookie: { MUSIC_U: 'x' } },
|
||||
async (uri, data, options) => {
|
||||
captured = { uri, data, options }
|
||||
return expected
|
||||
},
|
||||
)
|
||||
|
||||
assert.strictEqual(result, expected)
|
||||
assert.strictEqual(captured.uri, '/api/event/privacy/op')
|
||||
assert.deepStrictEqual(captured.data, {
|
||||
eventId: '123456789012345678',
|
||||
privacy: 0,
|
||||
})
|
||||
assert.strictEqual(captured.options.crypto, '')
|
||||
})
|
||||
|
||||
it('rejects unknown event privacy values before making a request', async () => {
|
||||
let requested = false
|
||||
const result = await eventPrivacy({ evId: '1', privacy: '3' }, async () => {
|
||||
requested = true
|
||||
})
|
||||
|
||||
assert.strictEqual(requested, false)
|
||||
assert.strictEqual(result.status, 400)
|
||||
})
|
||||
})
|
||||
145
test/user_event_all.test.js
Normal file
145
test/user_event_all.test.js
Normal file
@ -0,0 +1,145 @@
|
||||
const assert = require('assert')
|
||||
const userEventAll = require('../module/user_event_all')
|
||||
|
||||
describe('all current user events module', () => {
|
||||
it('resolves the current user and aggregates every upstream page', async () => {
|
||||
const calls = []
|
||||
const pages = [
|
||||
{
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
events: [{ id: 1 }, { id: 2 }],
|
||||
size: 5,
|
||||
more: true,
|
||||
lasttime: 100,
|
||||
},
|
||||
cookie: ['page=1'],
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
events: [{ id: 2 }, { id: 3 }],
|
||||
size: 5,
|
||||
more: true,
|
||||
lasttime: 50,
|
||||
},
|
||||
cookie: ['page=2'],
|
||||
},
|
||||
{
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
events: [{ id: 4 }],
|
||||
size: 1,
|
||||
more: false,
|
||||
lasttime: 0,
|
||||
},
|
||||
cookie: ['page=3'],
|
||||
},
|
||||
]
|
||||
|
||||
const result = await userEventAll(
|
||||
{ cookie: { MUSIC_U: 'x' } },
|
||||
async (uri, data, options) => {
|
||||
calls.push({ uri, data, options })
|
||||
if (uri === '/api/nuser/account/get') {
|
||||
return {
|
||||
status: 200,
|
||||
body: { code: 200, account: { id: 42 } },
|
||||
cookie: ['account=1'],
|
||||
}
|
||||
}
|
||||
return pages.shift()
|
||||
},
|
||||
)
|
||||
|
||||
assert.deepStrictEqual(
|
||||
result.body.events.map((event) => event.id),
|
||||
[1, 2, 3, 4],
|
||||
)
|
||||
assert.strictEqual(result.body.size, 5)
|
||||
assert.strictEqual(result.body.retrievedCount, 4)
|
||||
assert.strictEqual(result.body.unavailableCount, 1)
|
||||
assert.strictEqual(result.body.sizeMismatch, true)
|
||||
assert.strictEqual(result.body.pageCount, 3)
|
||||
assert.strictEqual(result.body.more, false)
|
||||
assert.deepStrictEqual(result.cookie, [
|
||||
'account=1',
|
||||
'page=1',
|
||||
'page=2',
|
||||
'page=3',
|
||||
])
|
||||
|
||||
assert.strictEqual(calls[0].uri, '/api/nuser/account/get')
|
||||
assert.strictEqual(calls[0].options.crypto, 'weapi')
|
||||
assert.deepStrictEqual(
|
||||
calls.slice(1).map((call) => call.data),
|
||||
[
|
||||
{
|
||||
getcounts: true,
|
||||
time: -1,
|
||||
limit: 100,
|
||||
total: false,
|
||||
fromRN: 'true',
|
||||
},
|
||||
{
|
||||
getcounts: true,
|
||||
time: 100,
|
||||
limit: 100,
|
||||
total: false,
|
||||
fromRN: 'true',
|
||||
},
|
||||
{
|
||||
getcounts: true,
|
||||
time: 50,
|
||||
limit: 100,
|
||||
total: false,
|
||||
fromRN: 'true',
|
||||
},
|
||||
],
|
||||
)
|
||||
})
|
||||
|
||||
it('requires a valid login cookie', async () => {
|
||||
const result = await userEventAll({}, async () => ({
|
||||
status: 200,
|
||||
body: { code: 200, account: null, profile: null },
|
||||
cookie: [],
|
||||
}))
|
||||
|
||||
assert.strictEqual(result.status, 401)
|
||||
assert.strictEqual(result.body.code, 401)
|
||||
})
|
||||
|
||||
it('fails instead of returning a partial list when the cursor stalls', async () => {
|
||||
let page = 0
|
||||
const result = await userEventAll({}, async (uri) => {
|
||||
if (uri === '/api/nuser/account/get') {
|
||||
return {
|
||||
status: 200,
|
||||
body: { code: 200, account: { id: 42 } },
|
||||
cookie: [],
|
||||
}
|
||||
}
|
||||
|
||||
page += 1
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
events: [],
|
||||
size: 1,
|
||||
more: true,
|
||||
lasttime: 100,
|
||||
},
|
||||
cookie: [],
|
||||
}
|
||||
})
|
||||
|
||||
assert.strictEqual(page, 2)
|
||||
assert.strictEqual(result.status, 502)
|
||||
assert.match(result.body.message, /cursor stalled/)
|
||||
})
|
||||
})
|
||||
@ -1,31 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2015",
|
||||
"module": "commonjs",
|
||||
"experimentalDecorators": true,
|
||||
"ignoreDeprecations": "6.0",
|
||||
"moduleResolution": "node",
|
||||
"lib": [
|
||||
"esnext",
|
||||
"esnext.asynciterable",
|
||||
"dom"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": [
|
||||
"./*"
|
||||
],
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
"compilerOptions": {
|
||||
"target": "ES2015",
|
||||
"module": "node16",
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node16",
|
||||
"lib": ["esnext", "esnext.asynciterable", "dom"],
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./*"],
|
||||
"@/*": ["./*"],
|
||||
"@neteasecloudmusicapienhanced/api": ["./interface.d.ts"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user