refactor: 默认nexusphp脚本对齐PTTIME风格(http.get+utils+邀请+红色HR)
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
// NexusPHP 站点默认脚本
|
// NexusPHP 站点默认脚本
|
||||||
// 适用: PThome / HDSky / OurBits / HDFans / 聆音 等
|
// 适用: PThome / HDSky / HDFans / OurBits 等
|
||||||
// 覆盖: 搜索 / 详情 / 用户信息 / 签到
|
|
||||||
|
|
||||||
Map parse(String content, String url) {
|
Map parse(String content, String url) {
|
||||||
def doc = jsoup.parse(content)
|
def doc = Jsoup.parse(content)
|
||||||
|
|
||||||
if (url.contains('/torrents.php')) return parseSearch(doc)
|
if (url.contains('/torrents.php')) return parseSearch(doc)
|
||||||
if (url.contains('/details.php')) return parseDetail(doc)
|
if (url.contains('/details.php')) return parseDetail(doc)
|
||||||
@@ -19,56 +18,58 @@ private Map parseUserInfo(doc) {
|
|||||||
def el = utils.selectFirst(doc, '#info_block, td.bottom.clearfix, .userbar')
|
def el = utils.selectFirst(doc, '#info_block, td.bottom.clearfix, .userbar')
|
||||||
if (el) {
|
if (el) {
|
||||||
def t = utils.text(el)
|
def t = utils.text(el)
|
||||||
// 通用正则提取
|
|
||||||
def um = t =~ /上传[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
|
def um = t =~ /上传[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
|
||||||
def dm = t =~ /下载[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
|
def dm = t =~ /下载[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
|
||||||
def rm = t =~ /分享率[::]\s*([\d.,]+)/
|
def rm = t =~ /分享率[::]\s*([\d.,]+)/
|
||||||
def bm = t =~ /魔力[值::]*[^0-9]*([\d.,]+)/
|
def bm = t =~ /魔力[值::]*[^0-9]*([\d.,]+)/
|
||||||
|
def sm = t =~ /⬆\s*(\d+)/
|
||||||
|
def lm = t =~ /⬇\s*(\d+)/
|
||||||
|
def im = t =~ /邀请[::]\s*(\d+)/
|
||||||
if (um) fields.upload = um[0][1] + (um[0][2] ?: '')
|
if (um) fields.upload = um[0][1] + (um[0][2] ?: '')
|
||||||
if (dm) fields.download = dm[0][1] + (dm[0][2] ?: '')
|
if (dm) fields.download = dm[0][1] + (dm[0][2] ?: '')
|
||||||
if (rm) fields.ratio = rm[0][1]
|
if (rm) fields.ratio = rm[0][1]
|
||||||
if (bm) fields.bonus = bm[0][1]
|
if (bm) fields.bonus = bm[0][1]
|
||||||
// PTTIME 特殊:做种/下载计数
|
|
||||||
def sm = t =~ /⬆\s*(\d+)/
|
|
||||||
def lm = t =~ /⬇\s*(\d+)/
|
|
||||||
if (sm) fields.seeding = sm[0][1]
|
if (sm) fields.seeding = sm[0][1]
|
||||||
if (lm) fields.leeching = lm[0][1]
|
if (lm) fields.leeching = lm[0][1]
|
||||||
|
if (im) fields.invites = im[0][1]
|
||||||
|
// H&R 红色数字(未达标)
|
||||||
|
def hrEl = doc.selectFirst('#info_block font[class=fcr], #info_block font[color=red], #info_block .red')
|
||||||
|
if (hrEl) {
|
||||||
|
def hrm = hrEl.text() =~ /(\d+)/
|
||||||
|
if (hrm) fields.hnr = hrm[0][1]
|
||||||
}
|
}
|
||||||
return [pageName: '用户信息', fields: fields]
|
}
|
||||||
|
return [pageName: '用户信息', fields: fields, message: fields.isEmpty() ? '未提取' : '解析成功']
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseSearch(doc) {
|
private Map parseSearch(doc) {
|
||||||
def rows = []
|
def rows = []
|
||||||
// 兼容有/无 tbody 的 NexusPHP 变体
|
|
||||||
doc.select('table.torrents tr:has(td.rowfollow):not(:has(.colhead))').each { row ->
|
doc.select('table.torrents tr:has(td.rowfollow):not(:has(.colhead))').each { row ->
|
||||||
def titleEl = utils.selectFirst(row, 'td.embedded a[href*=details]')
|
def titleEl = row.selectFirst('td.embedded a[href*=details]')
|
||||||
if (!titleEl) return
|
if (!titleEl) return
|
||||||
def catEl = utils.selectFirst(row, 'td.rowfollow a[href*=cat] img, td.rowfollow a[href*=torrents] img, span.category')
|
def catEl = row.selectFirst('td.rowfollow img, td.rowfollow span.category')
|
||||||
def freeEl = utils.selectFirst(row, 'font.promotion')
|
def freeEl = row.selectFirst('font.promotion')
|
||||||
def tds = row.select('td.rowfollow')
|
def tds = row.select('td.rowfollow')
|
||||||
// 按列索引查找:size(含br)、seeders、leechers、completed、time
|
def size = '', seeders = '', leechers = '', completed = '', time = ''
|
||||||
def size = '', seeders = '', leechers = '', completed = '', time = '', publisher = ''
|
|
||||||
for (int i = 0; i < tds.size(); i++) {
|
for (int i = 0; i < tds.size(); i++) {
|
||||||
def t = utils.text(tds.get(i))
|
def cell = tds[i].text()
|
||||||
if (!t) continue
|
if (!cell) continue
|
||||||
if (t.contains('GB') || t.contains('MB') || t.contains('TB') || t.contains('KB')) size = t
|
if (cell.contains('GB')||cell.contains('MB')||cell.contains('TB')||cell.contains('KB')) size = cell
|
||||||
else if (t ==~ /^\d+$/ && !seeders) { seeders = t }
|
else if (cell ==~ /^\d+$/ && !seeders) seeders = cell
|
||||||
else if (t ==~ /^\d+$/ && seeders) { leechers = t }
|
else if (cell ==~ /^\d+$/ && seeders && !leechers) leechers = cell
|
||||||
else if (t ==~ /^\d+$/ && seeders && leechers) { completed = t }
|
else if (cell ==~ /^\d+$/ && leechers) completed = cell
|
||||||
else if (t ==~ /^\d+年|^\d+月|^\d+天|^\d+时/) time = t
|
else if (cell ==~ /^\d+年|^\d+月|^\d+天|^\d+时/) time = cell
|
||||||
else if (seeders && !leechers) publisher = t
|
|
||||||
}
|
}
|
||||||
rows << [
|
rows << [
|
||||||
title: utils.text(titleEl),
|
title: titleEl.text(),
|
||||||
detailUrl: utils.attr(titleEl, 'href'),
|
detailUrl: titleEl.attr('href'),
|
||||||
category: utils.attr(catEl, 'alt') ?: utils.attr(catEl, 'title') ?: '',
|
category: catEl?.attr('alt') ?: catEl?.attr('title') ?: '',
|
||||||
free: utils.text(freeEl),
|
free: freeEl?.text(),
|
||||||
size: size,
|
size: size,
|
||||||
seeders: seeders,
|
seeders: seeders,
|
||||||
leechers: leechers,
|
leechers: leechers,
|
||||||
completed: completed,
|
completed: completed,
|
||||||
publishDate: time,
|
publishDate: time,
|
||||||
publisher: publisher,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
return [pageName: '搜索', extracted: rows, message: "共 ${rows.size()} 条"]
|
return [pageName: '搜索', extracted: rows, message: "共 ${rows.size()} 条"]
|
||||||
@@ -76,16 +77,16 @@ private Map parseSearch(doc) {
|
|||||||
|
|
||||||
private Map parseDetail(doc) {
|
private Map parseDetail(doc) {
|
||||||
return [pageName: '种子详情', fields: [
|
return [pageName: '种子详情', fields: [
|
||||||
title: utils.text(utils.selectFirst(doc, '#top h1, h1')),
|
title: doc.selectFirst('#top h1, h1')?.text(),
|
||||||
subtitle: utils.text(utils.selectFirst(doc, 'td.embedded b')),
|
subtitle: doc.selectFirst('td.embedded b')?.text(),
|
||||||
size: utils.text(utils.selectFirst(doc, 'td:has(b:contains(大小)) + td, td.rowhead:contains(大小) + td')),
|
size: doc.selectFirst('td:has(b:contains(大小)) + td, td.rowhead:contains(大小) + td')?.text(),
|
||||||
seeders: utils.text(utils.selectFirst(doc, 'td:contains(做种者) + td')),
|
seeders: doc.selectFirst('td:contains(做种者) + td')?.text(),
|
||||||
leechers: utils.text(utils.selectFirst(doc, 'td:contains(下载者) + td')),
|
leechers: doc.selectFirst('td:contains(下载者) + td')?.text(),
|
||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map parseSign(doc) {
|
private Map parseSign(doc) {
|
||||||
def t = utils.text(doc.body())
|
def t = doc.body().text()
|
||||||
def matched = t ==~ /.*(签到成功|已签到|重复签到|获得.*魔力值).*/
|
def matched = t ==~ /.*(签到成功|已签到|重复签到|获得.*魔力值).*/
|
||||||
return [pageName: '签到', fields: [result: t.take(200)], message: matched ? '成功' : '未知']
|
return [pageName: '签到', fields: [result: t.take(200)], message: matched ? '成功' : '未知']
|
||||||
}
|
}
|
||||||
@@ -93,86 +94,91 @@ private Map parseSign(doc) {
|
|||||||
private Map parseUserPanel(doc) {
|
private Map parseUserPanel(doc) {
|
||||||
def fields = [:]
|
def fields = [:]
|
||||||
doc.select('td.text').each { td ->
|
doc.select('td.text').each { td ->
|
||||||
def t = utils.text(td)
|
def t = td.text()
|
||||||
if (t.contains('分享率')) fields.shareRate = t.replaceAll(/[^0-9.]/, '')
|
if (t.contains('分享率')) fields.shareRate = t.replaceAll(/[^0-9.]/, '')
|
||||||
if (t.contains('魔力')) fields.bonus = t.replaceAll(/[^0-9.,]/, '')
|
if (t.contains('魔力')) fields.bonus = t.replaceAll(/[^0-9.,]/, '')
|
||||||
}
|
}
|
||||||
return [pageName: '控制面板', fields: fields]
|
return [pageName: '控制面板', fields: fields, message: fields.isEmpty() ? '未提取' : '解析成功']
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== mediabot 运行时 ====================
|
||||||
|
|
||||||
|
Map sign(PluginContext ctx) {
|
||||||
|
if (!ctx.auth) return [message: '未配置认证', loginRequired: true]
|
||||||
|
try {
|
||||||
|
def url = ctx.siteUrl.replaceAll('/+\$', '') + '/attendance.php'
|
||||||
|
def body = http.get(url, ctx.auth)
|
||||||
|
def matched = body ==~ /.*(签到成功|已签到|重复签到|获得.*魔力值).*/
|
||||||
|
return [success: matched, message: matched ? '已签到' : '失败']
|
||||||
|
} catch (e) { return [message: "签到异常: ${e.message}"] }
|
||||||
}
|
}
|
||||||
|
|
||||||
Map sync(PluginContext ctx) {
|
Map sync(PluginContext ctx) {
|
||||||
if (!ctx.site.auth) return [message: "未配置认证", loginRequired: true]
|
if (!ctx.auth) return [message: '未配置认证', loginRequired: true]
|
||||||
try {
|
try {
|
||||||
def conn = ctx.httpGet('/index.php')
|
def url = ctx.siteUrl.replaceAll('/+\$', '') + '/index.php'
|
||||||
def doc = jsoup.parse(conn.body())
|
def body = http.get(url, ctx.auth)
|
||||||
|
def doc = Jsoup.parse(body)
|
||||||
def el = utils.selectFirst(doc, '#info_block, td.bottom.clearfix, .userbar')
|
def el = utils.selectFirst(doc, '#info_block, td.bottom.clearfix, .userbar')
|
||||||
if (!el) return [message: "未找到用户信息"]
|
if (!el) { ctx.incomplete = true; return [message: '未找到用户信息'] }
|
||||||
def t = utils.text(el)
|
def t = utils.text(el)
|
||||||
def result = [:]
|
def result = [:]
|
||||||
def um = t =~ /上传[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
|
def um = t =~ /上传[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
|
||||||
def dm = t =~ /下载[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
|
def dm = t =~ /下载[::]\s*([0-9.,]+)\s*(TB|GB|MB|KB|B)?/
|
||||||
def rm = t =~ /分享率[::]\s*([\d.,]+)/
|
def rm = t =~ /分享率[::]\s*([\d.,]+)/
|
||||||
def bm = t =~ /魔力[值::]*[^0-9]*([\d.,]+)/
|
def bm = t =~ /魔力[值::]*[^0-9]*([\d.,]+)/
|
||||||
if (um) result.upload = parseTraffic(um[0][1].replace(',','') as double, um[0][2])
|
|
||||||
if (dm) result.download = parseTraffic(dm[0][1].replace(',','') as double, dm[0][2])
|
|
||||||
if (rm) result.shareRate = rm[0][1].replace(',','') as double
|
|
||||||
if (bm) result.bonus = bm[0][1].replace(',','') as double
|
|
||||||
// 做种数
|
|
||||||
def sm = t =~ /⬆\s*(\d+)/
|
def sm = t =~ /⬆\s*(\d+)/
|
||||||
|
def im = t =~ /邀请[::]\s*(\d+)/
|
||||||
|
if (um) result.upload = utils.parseTraffic(um[0][1] + (um[0][2] ?: ''))
|
||||||
|
if (dm) result.download = utils.parseTraffic(dm[0][1] + (dm[0][2] ?: ''))
|
||||||
|
if (rm) result.shareRatio = rm[0][1].replace(',','') as double
|
||||||
|
if (bm) result.bonus = bm[0][1].replace(',','') as double
|
||||||
if (sm) result.seeding = sm[0][1] as int
|
if (sm) result.seeding = sm[0][1] as int
|
||||||
|
if (im) result.invites = im[0][1] as int
|
||||||
|
def hrEl = doc.selectFirst('#info_block font[class=fcr], #info_block font[color=red], #info_block .red')
|
||||||
|
if (hrEl) {
|
||||||
|
def hrm = hrEl.text() =~ /(\d+)/
|
||||||
|
if (hrm) result.hnr = hrm[0][1] as int
|
||||||
|
}
|
||||||
|
if (!result.upload || !result.download) ctx.incomplete = true
|
||||||
return result
|
return result
|
||||||
} catch (e) { return [message: "同步失败: ${e.message}"] }
|
} catch (e) { ctx.incomplete = true; return [message: "同步失败: ${e.message}"] }
|
||||||
}
|
|
||||||
|
|
||||||
Map sign(PluginContext ctx) {
|
|
||||||
if (!ctx.site.auth) return [message: "未配置认证", loginRequired: true]
|
|
||||||
try {
|
|
||||||
def conn = ctx.httpGet('/attendance.php')
|
|
||||||
def matched = conn.body() ==~ /.*(签到成功|已签到|重复签到|获得.*魔力值).*/
|
|
||||||
return [success: matched, message: matched ? '已签到' : '失败']
|
|
||||||
} catch (e) { return [message: "签到异常: ${e.message}"] }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List search(PluginContext ctx, Map params) {
|
List search(PluginContext ctx, Map params) {
|
||||||
if (!ctx.site.auth) return []
|
if (!ctx.auth) return []
|
||||||
try {
|
try {
|
||||||
def keyword = params.keyword ?: ''
|
def keyword = params.keyword ?: ''
|
||||||
def page = (params.page ?: 0) as int
|
def page = (params.page ?: 0) as int
|
||||||
def conn = ctx.httpGet("/torrents.php?search=${URLEncoder.encode(keyword,'UTF-8')}&page=${page}")
|
def url = "${ctx.siteUrl.replaceAll('/+\$', '')}/torrents.php?search=${URLEncoder.encode(keyword,'UTF-8')}&page=${page}"
|
||||||
def doc = jsoup.parse(conn.body())
|
def body = http.get(url, ctx.auth)
|
||||||
|
def doc = Jsoup.parse(body)
|
||||||
def rows = []
|
def rows = []
|
||||||
doc.select('table.torrents tr:has(td.rowfollow):not(:has(.colhead))').each { row ->
|
doc.select('table.torrents tr:has(td.rowfollow):not(:has(.colhead))').each { row ->
|
||||||
def titleEl = utils.selectFirst(row, 'td.embedded a[href*=details]')
|
def titleEl = row.selectFirst('td.embedded a[href*=details]')
|
||||||
if (!titleEl) return
|
if (!titleEl) return
|
||||||
def tds = row.select('td.rowfollow')
|
def tds = row.select('td.rowfollow')
|
||||||
def size = 0, seeders = 0, leechers = 0, grabs = 0
|
long size = 0; int seeders = 0, leechers = 0, grabs = 0
|
||||||
boolean hasSize = false, hasSeed = false, hasLeech = false
|
boolean hasSize = false, hasSeed = false, hasLeech = false
|
||||||
for (int i = 0; i < tds.size(); i++) {
|
for (int i = 0; i < tds.size(); i++) {
|
||||||
def t = utils.text(tds.get(i))
|
def cell = tds[i].text()
|
||||||
if (!t) continue
|
if (!cell) continue
|
||||||
if (!hasSize && (t.contains('GB')||t.contains('MB')||t.contains('TB')||t.contains('KB'))) {size=parseHtmlSize(t);hasSize=true}
|
if (!hasSize && (cell.contains('GB')||cell.contains('MB')||cell.contains('TB')||cell.contains('KB'))) {
|
||||||
else if (hasSize && !hasSeed && t ==~ /^\d+$/) {seeders=t as int;hasSeed=true}
|
size = utils.parseHtmlSize(cell); hasSize = true
|
||||||
else if (hasSeed && !hasLeech && t ==~ /^\d+$/) {leechers=t as int;hasLeech=true}
|
} else if (hasSize && !hasSeed && cell ==~ /^\d+$/) {
|
||||||
else if (hasSeed && hasLeech && t ==~ /^\d+$/) {grabs=t as int}
|
seeders = utils.toInt(cell) ?: 0; hasSeed = true
|
||||||
|
} else if (hasSeed && !hasLeech && cell ==~ /^\d+$/) {
|
||||||
|
leechers = utils.toInt(cell) ?: 0; hasLeech = true
|
||||||
|
} else if (hasLeech && cell ==~ /^\d+$/) {
|
||||||
|
grabs = utils.toInt(cell) ?: 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rows << [
|
rows << [
|
||||||
title: utils.text(titleEl),
|
title: titleEl.text(),
|
||||||
detailUrl: "${ctx.siteUrl}${utils.attr(titleEl,'href')}",
|
detailUrl: "${ctx.siteUrl}${titleEl.attr('href')}",
|
||||||
size: size,
|
size: size, seeders: seeders, leechers: leechers, grabs: grabs,
|
||||||
seeders: seeders,
|
|
||||||
leechers: leechers,
|
|
||||||
grabs: grabs,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
return rows
|
return rows
|
||||||
} catch (e) { return [] }
|
} catch (e) { return [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
private long parseTraffic(double val, String unit) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user