Files
par/docs/nexusphp.groovy

192 lines
7.8 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// NexusPHP 站点通用脚本
// 示例站点: PThome / HDSky / OurBits 等
// ==================== PAR 校验入口 ====================
Map parse(String content, String url) {
def doc = jsoup.parse(content)
// ── 首页 / 用户信息 ──
if (url.contains('/index.php') && !url.contains('?')) {
return parseUserInfo(doc)
}
// ── 搜索页 ──
if (url.contains('/torrents.php')) {
return parseSearch(doc)
}
// ── 种子详情 ──
if (url.contains('/details.php')) {
return parseDetail(doc)
}
// ── 签到 ──
if (url.contains('/attendance.php')) {
return parseSign(doc)
}
// ── 控制面板 ──
if (url.contains('/usercp.php')) {
return parseUserPanel(doc)
}
return [pageName: '未知页面', message: "未配置解析: ${url}"]
}
private Map parseUserInfo(doc) {
def fields = [:]
def infoEl = utils.selectFirst(doc, '#info_block')
if (infoEl) {
def text = utils.text(infoEl)
// 提取关键信息
def uploadM = text =~ /上传[::]\s*([0-9.]+)\s*(TB|GB|MB|KB|B)?/
def downloadM = text =~ /下载[::]\s*([0-9.]+)\s*(TB|GB|MB|KB|B)?/
def ratioM = text =~ /分享率[::]\s*([\d.]+)/
def bonusM = text =~ /魔力[值::]*\s*([\d,]+)/
if (uploadM) fields.upload = uploadM[0][1] + (uploadM[0][2] ?: '')
if (downloadM) fields.download = downloadM[0][1] + (downloadM[0][2] ?: '')
if (ratioM) fields.ratio = ratioM[0][1]
if (bonusM) fields.bonus = bonusM[0][1]
}
return [pageName: '用户信息', fields: fields, message: fields.isEmpty() ? '未提取到数据' : '解析成功']
}
private Map parseSearch(doc) {
def rows = []
doc.select('table.torrents > tbody > tr:has(td.rowfollow):not(:has(th)):not(:has(.colhead))').each { row ->
def titleEl = utils.selectFirst(row, 'td.embedded a[href*=details]')
def catEl = utils.selectFirst(row, 'td.rowfollow a[href*=cat] img, td.rowfollow a[href*=torrents] img')
def freeEl = utils.selectFirst(row, 'td.rowfollow font.free')
def tds = row.select('td.rowfollow')
rows << [
title: utils.text(titleEl),
detailUrl: utils.attr(titleEl, 'href'),
category: utils.attr(catEl, 'alt'),
free: utils.text(freeEl),
size: tds.size() > 4 ? utils.text(tds.get(4)) : '',
seeders: tds.size() > 5 ? utils.text(tds.get(5)) : '',
leechers: tds.size() > 6 ? utils.text(tds.get(6)) : '',
completed: tds.size() > 7 ? utils.text(tds.get(7)) : '',
comments: tds.size() > 8 ? utils.text(tds.get(8)) : '',
publishDate: tds.size() > 3 ? utils.text(tds.get(3)) : '',
torrentUrl: utils.attr(utils.selectFirst(row, 'td.rowfollow a[href*=download]'), 'href'),
]
}
return [pageName: '搜索', extracted: rows, message: "共 ${rows.size()} 条"]
}
private Map parseDetail(doc) {
return [
pageName: '种子详情',
fields: [
title: utils.text(utils.selectFirst(doc, '#top h1, h1')),
subtitle: utils.text(utils.selectFirst(doc, 'td.embedded b, #subtitle')),
size: utils.text(utils.selectFirst(doc, 'td.rowhead:contains(大小) + td, b:contains(大小) + span')),
seeders: utils.text(utils.selectFirst(doc, 'td:contains(做种者) + td')),
leechers: utils.text(utils.selectFirst(doc, 'td:contains(下载者) + td')),
publishDate:utils.text(utils.selectFirst(doc, 'td.rowhead:contains(日期) + td')),
]
]
}
private Map parseSign(doc) {
def text = utils.text(doc.body())
def matched = text ==~ /.*(签到成功|已签到|重复签到|获得.*魔力值).*/
return [
pageName: '签到',
fields: [result: text.length() > 200 ? text.substring(0, 200) + '...' : text],
message: matched ? '签到成功' : '签到状态未知'
]
}
private Map parseUserPanel(doc) {
def fields = [:]
doc.select('td.text').each { td ->
def t = utils.text(td)
if (t.contains('分享率')) fields.shareRate = t.replaceAll(/[^0-9.]/, '')
if (t.contains('魔力')) fields.bonus = t.replaceAll(/[^0-9.,]/, '')
}
return [pageName: '控制面板', fields: fields, message: fields.isEmpty() ? '未提取' : '解析成功']
}
// ==================== mediabot 运行时入口 ====================
Map sign(PluginContext ctx) {
if (!ctx.site.auth) return [message: "未配置认证", loginRequired: true]
try {
def conn = ctx.httpGet('/attendance.php')
def body = conn.body()
def matched = body ==~ /.*(签到成功|已签到|重复签到|获得.*魔力值).*/
return [success: matched, message: matched ? '已签到' : '签到失败']
} catch (e) {
return [message: "签到异常: ${e.message}"]
}
}
Map sync(PluginContext ctx) {
if (!ctx.site.auth) return [message: "未配置认证", loginRequired: true]
try {
def conn = ctx.httpGet('/index.php')
def doc = jsoup.parse(conn.body())
def infoEl = utils.selectFirst(doc, '#info_block')
if (!infoEl) return [message: "未找到用户信息区域"]
def text = utils.text(infoEl)
def result = [:]
def uploadM = text =~ /上传[::]\s*([0-9.]+)\s*(TB|GB|MB|KB|B)?/
def downloadM = text =~ /下载[::]\s*([0-9.]+)\s*(TB|GB|MB|KB|B)?/
def ratioM = text =~ /分享率[::]\s*([\d.]+)/
def bonusM = text =~ /魔力[值::]*\s*([\d,]+)/
if (uploadM) result.upload = parseTraffic(uploadM[0][1] as double, uploadM[0][2])
if (downloadM) result.download = parseTraffic(downloadM[0][1] as double, downloadM[0][2])
if (ratioM) result.shareRate = ratioM[0][1] as double
if (bonusM) result.bonus = bonusM[0][1].replace(',','') as double
return result
} catch (e) {
return [message: "同步失败: ${e.message}"]
}
}
List search(PluginContext ctx, Map params) {
if (!ctx.site.auth) return []
try {
def keyword = params.keyword ?: ''
def page = (params.page ?: 0) as int
def conn = ctx.httpGet("/torrents.php?search=${URLEncoder.encode(keyword,'UTF-8')}&page=${page}")
def doc = jsoup.parse(conn.body())
def rows = []
doc.select('table.torrents > tbody > tr:has(td.rowfollow):not(:has(th)):not(:has(.colhead))').each { row ->
def titleEl = utils.selectFirst(row, 'td.embedded a[href*=details]')
def catEl = utils.selectFirst(row, 'td.rowfollow a[href*=cat] img, td.rowfollow a[href*=torrents] img')
def tds = row.select('td.rowfollow')
rows << [
title: utils.text(titleEl),
detailUrl: "${ctx.siteUrl}${utils.attr(titleEl,'href')}",
category: utils.attr(catEl, 'alt'),
size: tds.size() > 4 ? parseHtmlSize(utils.text(tds.get(4))) : 0,
seeders: tds.size() > 5 ? utils.toInt(utils.text(tds.get(5))) ?: 0 : 0,
leechers: tds.size() > 6 ? utils.toInt(utils.text(tds.get(6))) ?: 0 : 0,
grabs: tds.size() > 7 ? utils.toInt(utils.text(tds.get(7))) ?: 0 : 0,
publishDate: tds.size() > 3 ? utils.text(tds.get(3)) : '',
]
}
return rows
} catch (e) {
return []
}
}
// ==================== 工具方法 ====================
private long parseTraffic(double val, String unit) {
if (!unit) return val as long
def m = [B:1,KB:1024,MB:1048576,GB:1073741824,TB:1099511627776]
return (val * (m[unit.toUpperCase()] ?: 1)) as long
}
private long parseHtmlSize(String s) {
def m = s =~ /([\d.]+)\s*(TB|GB|MB|KB|B)?/
return m ? parseTraffic(m[0][1] as double, m[0][2]) : 0
}