🌈八叉书库

https://八叉书库.luoyacheng.ip-ddns.com

laizhiyu (6509)3天前

修复,洛娅橙大佬的八叉书源,AI修复,尽量修复
二维码导入
{
    "bookSourceComment": "八叉书库(杰奇CMS)。①线路:默认 https:\/\/8xsk.cc,登录面板有「🚀一键选站」「🌐站点快切」;②年龄确认:已在 gethd\/gethdi\/loginCheckJs 内置 Cookie age18_ok=1;③正文:手机版是 decodeURIComponent(escape(atob(...))) 加密,源内自动解密;④登录:用 cookie.getCookie() 从 CookieJar 读取(原 headers().values 会抛异常),验证码地址五路兜底,「🔍登录诊断」7 步排障;⑤★书架全部改纯 HTTP(不用浏览器):「📚加入网站书架」= POST \/modules\/article\/addbookcase.php {bid,ajax_request};「🗑移出网站书架」= POST \/modules\/article\/delbookcase.php;「♻️同步网站书架」= GET \/modules\/article\/bookcase.php 解析 aid= 逐本深链加入;⑥「🌐打开本书↓」「🌐打开网站」改为优先调系统浏览器(Legado 内置 WebView 可能被墙);⑦会员章节登录后自动购买(价格0免费);⑧目录每页20章自动翻页(含3秒反爬延时)。",
    "bookSourceGroup": "刘备",
    "bookSourceName": "🌈八叉书库",
    "bookSourceType": 0,
    "bookSourceUrl": "https:\/\/八叉书库.luoyacheng.ip-ddns.com",
    "bookUrlPattern": "https:\/\/.+\/book\/\\d+\\.html",
    "concurrentRate": "1200",
    "customButton": false,
    "customOrder": 3,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "eventListener": false,
    "exploreUrl": "@js:\nlet banben=\"1.8.6\";\neval(String(source.loginUrl));\nlet surl = `{{getsurl()}}`;\nlet jmtxt=java.base64Decode('6YO95biC5r+A5oOFLOaJkueBsOS5seS8pizmg4XoibLmrabkvqAs57u\/5bi95oOF5oCALOa3t+S5seS\/ruS7mSzosIPmlZnlhbvmiJA=').split(',');\nvar obj = {\n\t最近更: {\n\t\t\"url\": \"\/sort\/0\/{{page}}.html\",\n\t\t\"fg\": 9},\n\t\t'新(肉)':{\n\t\t\"url\": \"\/sort\/7\/{{page}}.html\",\n\t\t\"fg\": 1},\n\t最近入库: {\n\t\t\"url\": \"\/top\/postdate\/{{page}}.html\",\n\t\t\"fg\": 10},\n\t\t\n\t最近完: {\n\t\t\"url\": \"\/modules\/article\/articlelist.php?fullflag=1&page={{page}}\",\n\t\t\"fg\": 9,\n\t\t\"wrap\": true},\n\t\t'结(肉)':{\n\t\t\"url\": \"\/modules\/article\/articlelist.php?sortid=7&fullflag=1&page={{page}}\",\n\t\t\"fg\": 1},\n\t签约新书: {\n\t\t\"url\": \"\/top\/signtime\/{{page}}.html\",\n\t\t\"fg\": 10},\n\t\t\n\t字数榜: {\n\t\t\"url\": \"\/top\/words\/{{page}}.html\",\n\t\t\"wb\": true},\n\t收藏榜: {\n\t\t\"url\": \"\/top\/goodnum\/{{page}}.html\"},\n\t新书榜: {\n\t\t\"url\": \"\/top\/newhot\/{{page}}.html\"},\n\t\t\n\t总点击: {\n\t\t\"url\": \"\/top\/allvisit\/{{page}}.html\",\n\t\t\"wb\": true},\n\t月点击: {\n\t\t\"url\": \"\/top\/monthvisit\/{{page}}.html\"},\n\t周点击: {\n\t\t\"url\": \"\/top\/weekvisit\/{{page}}.html\"},\n\t\t\n\t总推荐: {\n\t\t\"url\": \"\/top\/allvote\/{{page}}.html\",\n\t\t\"wb\": true},\n\t月推荐: {\n\t\t\"url\": \"\/top\/monthvote\/{{page}}.html\"},\n\t周推荐: {\n\t\t\"url\": \"\/top\/weekvote\/{{page}}.html\"},\n\t[jmtxt[0]]: {\n\t\t\"url\": \"\/modules\/article\/articlefilter.php?order=lastupdate&page={{page}}&sortid=7&typeid=701\",\n\t\t\"wb\": true},\n\t[jmtxt[1]]: {\n\t\t\"url\": \"\/modules\/article\/articlefilter.php?order=lastupdate&page={{page}}&sortid=7&typeid=702\"},\n\t[jmtxt[2]]: {\n\t\t\"url\": \"\/modules\/article\/articlefilter.php?order=lastupdate&page={{page}}&sortid=7&typeid=703\"},\n\t[jmtxt[3]]: {\n\t\t\"url\": \"\/modules\/article\/articlefilter.php?order=lastupdate&page={{page}}&sortid=7&typeid=704\",\n\t\t\"wb\": true},\n\t[jmtxt[4]]: {\n\t\t\"url\": \"\/modules\/article\/articlefilter.php?order=lastupdate&page={{page}}&sortid=7&typeid=705\"},\n\t[jmtxt[5]]: {\n\t\t\"url\": \"\/modules\/article\/articlefilter.php?order=lastupdate&page={{page}}&sortid=7&typeid=706\"},\n\t\t玄幻奇幻: {\n\t\t\"url\": \"\/sort\/1\/{{page}}.html\",\n\t\t\"wb\": true},\n\t都市青春: {\n\t\t\"url\": \"\/sort\/2\/{{page}}.html\"},\n\t武侠仙侠: {\n\t\t\"url\": \"\/sort\/3\/{{page}}.html\"},\n\t\t搜索: {\n\t\t\turl: \t`{{\n\t\t\t\tlet surl = getsurl();\n\t\t\t\tif (page == 1) {\n\t\t\t\t\ttry {\n\t\t\t\t\tlet [cb,cf] = colorz();\n\t\t\t\t\tsvg=\\`<svg width=\"990\" height=\"300\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><rect width=\"100%\" height=\"150%\" fill=\"#\\${cb}\"\/><text fill=\"#\\${cf}\" text-anchor=\"middle\" dominant-baseline=\"middle\" stroke=\"#000000\" stroke-width=\"1\" font-weight=\"bold\"><tspan x=\"50%\" dy=\"30%\" font-size=\"100\">请输入搜索关键词<\/tspan><tspan x=\"50%\" dy=\"50%\" font-size=\"50\">此处搜索不支持完整书名<\/tspan><\/text><\/svg>\\`;\n\tsvg=java.base64Encode(svg);\n\tkeyy=java.getVerificationCode('data:image\/svg+xml;base64,'+svg);\n\t\t\t\t\t} catch(err) {keyy='系统';\n\t\t\t\t\tPackages.java.lang.Thread.sleep(500);\n\t\t\t\t\tjava.toast('使用默认关键词“系统”');}\n\t\t\t\t\tsource.put('keyy',keyy);\n\t\t\t\t\t\t}\n\t\t\t\telse keyy = source.get('keyy');\n\t\t\t\turl =\tsurl+'\/modules\/article\/search.php?searchkey='+keyy+'&searchtype=all&page='+page;\n\t\t\t\tlet option={};\n\t\t\t\toption[\"headers\"]=gethd();\n\t\t\t\turl+','+JSON.stringify(option);\n\t\t\t}}`,\n\t\t\t\"fb\": 0.3,\n\t\t\t\"wb\": true},\n\t我的书架: {\n\t\t\"url\": `{{\n\t\t\tif (page == 1) {\n\t\tlet surl = getsurl();\n\t\tlet option={};option[\"headers\"]=gethd();\n\t\tlet ck=source.getLoginHeader()||'';\n\t\tif (ck) option[\"headers\"][\"Cookie\"]=ck;\n\t\tck ? surl+'\/modules\/article\/bookcase.php,'+JSON.stringify(option) : (java.longToast('请登录账号'),'data:;base64,QQ,{\"type\":\"fx\"}');\n\t\t} else 'data:;base64,QQ,{\"type\":\"fx\"}';\n\t\t}}`,\n\t\t\"fb\": 0.3}\n\t}\n\nvar sort = [];\npush = (title, url, size, wrap, cc) => sort.push({\n    \"title\": title,\n    \"url\": url.startsWith(\"{{\") ? url : url ? surl + url + `{{',{\"headers\":'+JSON.stringify(gethd())+\"}\"}}`: \"\",\n    \"style\": {\n      layout_flexGrow: size || 1,\n      layout_flexBasisPercent: cc || -1,\n      layout_wrapBefore: wrap ? true : false\n     }\n  });\n\nfor (x in obj) {\npush(x, obj[x].url, obj[x].fg, obj[x].wb, obj[x].fb);\n}\n\n\/\/版本号\npush(\"版本号:\"+banben+\" 点我获取地址\", `{{if (page==1) {webfb();gengxin();}\n'data:;base64,QQ,{\"type\":\"fx\"}'}}`, 1, true);\npush(\"测速\/更新\",`{{eval(String(source.loginUrl));gengxin(); Packages.java.lang.Thread.sleep(100); ceshi(); 'data:;base64,QQ,{\"type\":\"fx\"}'}}`, 1);\n\nJSON.stringify(sort);",
    "jsLib": "var fbyurl='https:\/\/8xsk.org'; \/\/发布页链接\n\n\/\/ ===== 登录头兼容函数(★putLoginHeader 必须传 JSON 才会进 getHeaderMap(true))=====\n\/\/ 从 getLoginHeader() 提取纯 Cookie 串;兼容三种形态:JSON {\"Cookie\":...} \/ 纯Cookie串 \/ 空\nfunction ckget(so){\n\tlet h = '';\n\ttry { h = String(so.getLoginHeader() || ''); } catch(e) {}\n\tif (!h) return '';\n\tif (h.indexOf('{') == 0) {\n\t\ttry {\n\t\t\tlet o = JSON.parse(h);\n\t\t\tif (o && o['Cookie']) return String(o['Cookie']);\n\t\t} catch(e2) {}\n\t}\n\treturn h;\n}\n\nfunction webfb(){\nconst {java, source} = this;\n\tjava.toast('正在打开发布页…');\n\tlet option={};\n\toption[\"headers\"]=gethd();\n\toption['timeout']=15000;\n\tlet htm='';\n\t\/\/★不走 webView(该通道被网络环境阻断),纯 HTTP 即可拿到发布页\n\ttry { htm = String(java.connect(fbyurl+','+JSON.stringify(option)).body() || ''); } catch(e) {\n\t\tjava.longToast('请求发布页失败:' + String(e).substring(0,80)); return;\n\t}\n\tif (\/^java\\.net|^error\/.test(htm) || htm == '') {\n\t\tjava.log('发布页访问异常:'+htm);\n\t\tjava.longToast('网络连接异常\\n可点「🚀一键选站」自动测速');\n\t\treturn;\n\t}\n\t\/\/★选择器兜底:不同模板可能是 #url-list>li>a 或普通 a[href]\n\tlet urls = [];\n\ttry {\n\t\tlet j = Packages.org.jsoup.Jsoup.parse(htm);\n\t\tlet as = j.select('#url-list>li>a');\n\t\tif (as.size() == 0) as = j.select('a[href^=http]');\n\t\tfor (let i = 0; i < as.size(); i++) {\n\t\t\tlet u = String(as.get(i).attr('href') || '');\n\t\t\tif (u && u.indexOf('8xsk') >= 0 || u.indexOf('bachashuku') >= 0 || u.indexOf('18xsk') >= 0) {\n\t\t\t\tif (urls.indexOf(u) < 0) urls.push(u);\n\t\t\t}\n\t\t}\n\t} catch(e2) {}\n\tjava.log('发布页找到 ' + urls.length + ' 个站点:' + urls.join(' '));\n\n\t\/\/ 注入复制按钮 + 当前站点高亮\n\tlet cur = this.getsurl();\n\thtm = htm.replace('<\/body>', `<script>\nfunction copyt(t) {\n  var texta = document.createElement('textarea');\n  texta.value = t; texta.style.position = 'fixed'; texta.style.left = '-9999px';\n  document.body.appendChild(texta); texta.select();\n  document.execCommand('copy');\n  alert('已复制\\n' + t);\n}\ndocument.querySelectorAll('a[href^=\"http\"]').forEach(function(x){\n  if (x.nextElementSibling && x.nextElementSibling.tagName === 'BUTTON') return;\n  x.insertAdjacentHTML('afterend', '<button style=\"margin:4px;padding:4px 10px;border-radius:4px;border:1px solid #888;background:#2a6;color:#fff\" onclick=\"copyt(\\'' + x.href + '\\')\">复制<\/button>');\n});\n<\/script>` + '<\/body>');\n\n\ttry {\n\t\tjava.startBrowser('data:text\/html;base64,' + java.base64Encode(htm), '获取网址');\n\t} catch(e3) {\n\t\tjava.longToast('打开浏览器失败:' + String(e3).substring(0,60));\n\t}\n}\n\nfunction gets(x, r) {\nreturn r==undefined?\nthis.java.getString(x)+'':\nPackages.io.legado.app.model.analyzeRule.AnalyzeRule().getString(x,r)+'';}\nfunction gete(x, r) {\nreturn r==undefined?\nthis.java.getElement(x): Packages.io.legado.app.model.analyzeRule.AnalyzeRule().setContent(r).getElement(x);}\n\nfunction colorz() {\n\tconst {java, source} = this;\n\tlet cb = '151E27', cf = 'dddddd';\n\t\/\/★getLoginInfoMap() 在部分环境返回普通 JS 对象(无 .get),需双通道\n\tfunction pick(m, k) {\n\t\tif (!m) return '';\n\t\ttry { if (typeof m.get == 'function') return String(m.get(k) || ''); } catch(e) {}\n\t\ttry { return String(m[k] || ''); } catch(e2) {}\n\t\treturn '';\n\t}\n\tlet m = {};\n\ttry { m = source.getLoginInfoMap() || {}; } catch(e) {}\n\tlet v1 = pick(m, '封面背景颜色');\n\tlet v2 = pick(m, '封面字体颜色');\n\tif (v1 && v1 != 'null' && v1 != 'undefined') cb = v1;\n\tif (v2 && v2 != 'null' && v2 != 'undefined') cf = v2;\n\treturn [cb, cf];\n}\n\nu_a = 'Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36 Edg\/135.0.0.0';\nx_r_w = ''; \/\/com.microsoft.emmx\ns_c_u_p = `\"Windows\"`;\ns_c_u = `\"Microsoft Edge\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\"`;\n\nfunction gethdi(url) {\nconst {java, source, cookie} = this;\n\/\/★空值直接返回,避免拼出 \"undefined,{...}\"\nlet _u = String(url==null?'':url);\nif (!_u || _u=='undefined' || _u=='null') return '';\nif (_u.indexOf(',{') >= 0) return _u;\nlet surl=this.getsurl();\nlet hd={};\n\/\/★年龄确认闸门:不带 age18_ok=1 时全站只返回「年龄确认」页\nhd[\"Cookie\"]=\"age18_ok=1\";\nhd[\"Accept-Language\"]=\"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7\";\nhd[\"Referer\"]=surl;\nif (\/http.{4}www\/.test(surl))\nhd[\"User-Agent\"]=u_a;\nelse {\nlet ua=java.getWebViewUA()+'';\nhd[\"User-Agent\"]=ua;}\nhd[\"Sec-Fetch-Dest\"]=\"image\";\nhd[\"Sec-Fetch-Mode\"]=\"no-cors\";\nhd[\"Sec-Fetch-Site\"]=\"cross-site\";\nreturn url+','+JSON.stringify({\"headers\":hd});\n}\nfunction gethd() {\nconst {java, source, cookie} = this;\nlet surl=this.getsurl();\nlet hd={};\n\/\/★年龄确认闸门:CookieJar 里必须常驻 age18_ok=1\n\/\/★不能再用 cookie.removeCookie(surl) —— 它会把 age18_ok 一起清掉,\n\/\/★导致 java.ajax 走 CookieJar 时拿不到该 Cookie 而被 302 到 age_verify.php\ntry {\n\tlet ck = String(cookie.getCookie(surl) || '');\n\tif (ck.indexOf('age18_ok') < 0) {\n\t\tcookie.setCookie(surl, ck ? (ck + '; age18_ok=1') : 'age18_ok=1');\n\t}\n} catch(e) { java.log('写 age18_ok 失败:'+e); }\nhd[\"Cookie\"]=\"age18_ok=1\";\nhd[\"Accept-Language\"]=\"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7\";\nhd[\"Referer\"]=surl;\nif (\/http.{4}www\/.test(surl))\nhd[\"User-Agent\"]=u_a;\nelse {\nlet ua=java.getWebViewUA()+'';\nlet v=(ua.match(\/Chrome\\\/(\\d+)\/)||[,'136'])[1];\nhd[\"User-Agent\"]=ua;\nhd[\"Sec-CH-UA-Platform\"]=`\"Android\"`;\nhd[\"Sec-CH-UA\"]=`\"Chromium\";v=\"${v}\", \"Android WebView\";v=\"${v}\", \"Not.A\/Brand\";v=\"99\"`;\nhd[\"Sec-CH-UA-Mobile\"]=\"?1\";}\nreturn hd;\n}\n\/\/ ★可用站点池(来自发布页 https:\/\/8xsk.org 实测,2026-10-20)\nvar BX_POOL = ['https:\/\/8xsk.cc','https:\/\/18xsk.com','https:\/\/18xsk.cc','https:\/\/bachashuku.org','https:\/\/bachashuku.com'];\n\/\/ ★已失效的旧域名(自动回退,避免用户手填后卡死)\nvar BX_DEAD = ['www.8xbook.org','8xbook.org','luoyacheng.ip-ddns.com','ip-ddns.com'];\n\nfunction bxSiteOk(u){\n\tconst {java} = this;\n\ttry {\n\t\tlet hd={'User-Agent':this.gethd()['User-Agent']||'Mozilla\/5.0','Cookie':'age18_ok=1'};\n\t\tlet b=String(java.connect(u+','+JSON.stringify({headers:hd,timeout:9000})).body());\n\t\treturn b.length>3000 && b.indexOf('年龄确认')<0 && b.indexOf('八叉')>=0;\n\t} catch(e) { return false; }\n}\n\nfunction getsurl() {\n\tconst {java, source} = this;\n\tlet surl=String((source.getLoginInfoMap()||{'网址':''})['网址']||'');\n\tsurl=surl.replace(\/\\\/$\/,'');\n\t\/\/ 空 → 用默认活站点\n\tif (!surl) return 'https:\/\/8xsk.cc';\n\t\/\/ 已知死域名 → 自动回退\n\tlet dead = BX_DEAD.some(function(d){ return surl.indexOf(d)>=0; });\n\tif (dead) {\n\t\tfor (let i=0;i<BX_POOL.length;i++) {\n\t\t\tif (this.bxSiteOk(BX_POOL[i])) { java.log('旧域名已失效,自动切换:'+BX_POOL[i]); return BX_POOL[i]; }\n\t\t}\n\t\treturn 'https:\/\/8xsk.cc';\n\t}\n\treturn surl;\n}\n\n\nfunction decodeEmail(n, c) {\n\t\/\/★输入非法(非 hex \/ 空)时返回空串,不要抛异常\n\ttry {\n\t\tfunction hexToInt(e, t) {\n\t\t\tvar r = e.substr(t, 2);\n\t\t\treturn parseInt(r, 16);\n\t\t}\n\t\tlet s = String(n == null ? '' : n);\n\t\tif (!s || s.length < (c + 4)) return '';\n\t\tvar o = '', a = hexToInt(s, c), i = c + 2;\n\t\tif (isNaN(a)) return '';\n\t\tfor (i; i < s.length; i += 2) {\n\t\t\tvar l = hexToInt(s, i) ^ a;\n\t\t\tif (isNaN(l)) break;\n\t\t\to += String.fromCharCode(l);\n\t\t}\n\t\ttry { o = decodeURIComponent(escape(o)); } catch(e1) {}\n\t\treturn o;\n\t} catch(e) { return ''; }\n}\n\n\nfunction gengxin(){\nconst {java, source} = this;\n\tlet url0='https:\/\/gitee.com\/lyc486\/yuedu2\/commits\/main\/书源\/八叉\/8x.json?browser_history=0&page=1&per=4';\n\tlet rr0=java.get(url0,{'User-Agent': 'Mozilla\/5.0 (Linux; Android 14)','X-Requested-With': 'XMLHttpRequest'}).body();\n\tlet gxlrr = [];\n\ttry { gxlrr = com.jayway.jsonpath.JsonPath.read(rr0, '$.list[0:4].title') || []; } catch(e) {}\n\tlet gxlr = gxlrr[0] || '', gxlr2 = gxlrr[1] || '', gxlr3 = gxlrr[2] || '', gxlr4 = gxlrr[3] || '';\n\tlet banben = '0';\n\ttry { banben = String(source.exploreUrl).match(\/let banben=\"([\\d\\.]+)\"\/)[1]; } catch(e) {}\n\tgxlr=String(gxlr).split(\/^([^\\-]+)-\/);\n\tgxlr[0]=='' ? '' : gxlr=['','0',gxlr[0]];\n\tlet gxts;\n\tif (gxlr[1]>banben){gxts=['更新','0,140,186'];}\n\telse {gxts= ['导入','76,175,80'];};\n\tlet htm = `<html>\n<head>\n<title>更新<\/title>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n<style>\ntable {\n\ttext-align: center;\n\tmargin: 2em auto 0 auto;\n\t}\nth span{font-size: 1.2em;}\n.ann {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\theight: 50vh;\n\t}\nbutton {\n\tbackground-color: rgb(${gxts[1]});\n\tcolor: white;\n\tborder: none;\n\tborder-radius: 4px;\n\theight: 6vh;\n\twidth: 30vw;\n\toverflow: hidden;\n\t}\nbutton span {\n  cursor: pointer;\n  display: inline-block;\n  position: relative;\n  transition: 0.4s;\n}\nbutton span:after {\n  content: '>';\n  position: absolute;\n  opacity: 0;\n  top: 0;\n  right: 30px;\n  transition: 0.2s;\n}\nbutton:active span {\n  padding-right: 20px;\n}\nbutton:active span:after {\n  opacity: 1;\n  right: -40px;\n}\n<\/style>\n<\/head>\n<body>\n<table border=\"1\" cellspacing=\"0\">\n<th colspan=\"2\">最新日志:<span>${gxlr[2]}<\/span><\/th>\n<tr>\n<td>仓库版本:${gxlr[1]}<\/td>\n<td>本地版本:${banben}<\/td>\n<\/tr>\n<tr>\n<td colspan=\"2\" style=\"text-align: left;\">往期日志:${gxlr2}<br>     ${gxlr3}<br>     ${gxlr4}<\/td>\n<\/tr>\n<\/table>\n<div class=\"ann\">\n<a href=\"legado:\/\/import\/importonline?src=https:\/\/gitee.com\/lyc486\/yuedu2\/raw\/main\/书源\/八叉\/8x.json\">\n<button><span>${gxts[0]}<\/span><\/button>\n<\/a>\n<\/div>\n<a href=\"legado:\/\/import\/importonline?src=https:\/\/cdn.jsdelivr.net\/gh\/Luoyacheng\/yuedu@main\/书源\/八叉\/8x.json\" style=\"font-size: 10px;\">备用链接<\/a>\n<\/body>\n<\/html>`;\nif(typeof this.book=='undefined'||gxts[0]=='更新') java.startBrowser(`data:text\/html;base64,${java.base64Encode(htm)}`,'更新');\n}\n\nfunction tbshujia(iuu,iid){\nconst {java, source} = this;\nlet iuux=iuu+'\/modules\/article\/addbookcase.php';\nlet iuuy=String(iuu+'\/book\/'+iid+'.html'); \/\/需要转化否则报错\nlet tm = new Date().getTime();\nlet body = `bid=${iid}&ajax_request=${tm}`;\nlet option={};\noption[\"headers\"]=this.gethd();\noption[\"headers\"][\"Referer\"]=iuuy;\noption[\"method\"]=\"POST\";\noption[\"body\"]=body;\nlet ck=ckget(source);\nif (String(ck).includes(\"jieqiUserInfo\")) {\noption[\"headers\"][\"Cookie\"]=ck;\nlet by = java.connect(iuux+','+JSON.stringify(option));\nlet _bn = '';\ntry { _bn = (typeof this.book != 'undefined' && this.book) ? String(this.book.name || '') : ''; } catch(e) {}\njava.longToast('\\n' + (_bn || '书籍') + ':\\n' + by.body());\nreturn true;}\nreturn false;\n}\n\nfunction hmdjib(id,name){\nconst {java, source, cache} = this;\n\tlet hmd=cache.get('8xhmd')||'{}';\n\thmd=JSON.parse(hmd);\n\tlet i=false;\n\ttry {\n\t\tlet rgstr = source.getLoginInfoMap().get('正则书名黑名单');\n\t\tlet rg = eval(String(rgstr));\n\t\ti=rg.test(name);\n\t\t} catch(err) {}\n\ttry {\n\t\tif (i) {\n\t\tlet rgstr = source.getLoginInfoMap().get('正则书名白名单');\n\t\tlet rg = eval(String(rgstr));\n\t\t i=!rg.test(name);\n\t\t }\n\t\t} catch(err) {}\n\t\ti = i||(id in hmd);\n\t\treturn i;\n\t}\n\nOranges = `<path d=\"M646.4 763.6c150.1-15.8 267.1-142.7 267.1-297 0-164.9-133.7-298.7-298.7-298.7-154.8 0-282.1 117.8-297.2 268.7\" fill=\"#FA9F23\" p-id=\"5624\"><\/path><path d=\"M648.6 784.8l-4.5-42.4c67.7-7.1 130.3-39 176.3-89.7 46.3-51.1 71.7-117.2 71.7-186.1 0-74.1-28.8-143.7-81.2-196.1-52.4-52.4-122-81.2-196.1-81.2-69.2 0-135.5 25.6-186.6 72.2-50.8 46.3-82.5 109.2-89.3 177.3l-42.5-4.2c7.8-78.6 44.4-151.2 103-204.6 59-53.7 135.5-83.3 215.4-83.3 85.5 0 165.8 33.3 226.3 93.7 60.4 60.4 93.7 140.8 93.7 226.3 0 79.6-29.4 155.9-82.8 214.8-53 58.3-125.2 95.1-203.4 103.3z\" fill=\"#333333\" p-id=\"5625\"><\/path><path d=\"M161.5 832.4c116.6 116.6 305.7 116.6 422.4 0s116.6-305.7 0-422.4\" fill=\"#FA9F23\" p-id=\"5626\"><\/path><path d=\"M372.7 941.2c-85.5 0-165.8-33.3-226.3-93.7l30.2-30.2c52.4 52.4 122 81.2 196.1 81.2s143.7-28.8 196.1-81.2c52.4-52.4 81.2-122 81.2-196.1s-28.8-143.7-81.2-196.1l30.2-30.2c60.4 60.4 93.7 140.8 93.7 226.3S659.4 787 599 847.5c-60.5 60.4-140.8 93.7-226.3 93.7z\" fill=\"#333333\" p-id=\"5627\"><\/path><path d=\"M161.480941 832.416461a170.7 298.7 44.999 1 0 422.418218-422.432964 170.7 298.7 44.999 1 0-422.418218 422.432964Z\" fill=\"#FBE0C3\" p-id=\"5628\"><\/path><path d=\"M249.1 885.8c-9.3 0-18.4-0.7-27.2-2-30.5-4.6-55.9-16.8-75.5-36.4-19.6-19.6-31.8-45-36.4-75.5-4.2-28-1.7-59 7.4-92.4 18.1-66.2 60.5-135.2 119.5-194.2S364.9 384 431.1 366c33.3-9.1 64.4-11.6 92.4-7.4 30.5 4.6 55.9 16.8 75.5 36.4s31.8 45 36.4 75.5c4.2 28 1.7 59-7.4 92.4C609.9 629 567.5 698 508.5 757s-128 101.4-194.2 119.5c-22.9 6.2-44.7 9.3-65.2 9.3z m18-370.2c-53.8 53.8-92.4 116.1-108.5 175.2-15 55-8.6 99.9 18 126.5s71.5 33 126.5 18c59.2-16.1 121.4-54.7 175.2-108.5 53.8-53.8 92.4-116.1 108.5-175.2 15-55 8.6-99.9-18-126.5s-71.5-33-126.5-18c-59.1 16.2-121.4 54.7-175.2 108.5z\" fill=\"#333333\" p-id=\"5629\"><\/path><path d=\"M373.3 641.5h148.4c-16.2 23.8-35.8 47.6-58.5 70.2-28.9 28.9-59.7 52.9-90 71.1l0.1-141.3z m187.1-193.4c27 37.3 15.7 103.4-25.2 172.1H388.3l172.1-172.1zM351.9 656.6v138.2c-61.4 31.7-118.7 38.6-152.4 14.2l152.4-152.4z m-152.5-15.1h137.4L184.6 793.8c-24-33.9-16.9-91.1 14.8-152.3z m173.9-182.6c68.5-40.9 134.5-52.4 172-25.9l-172 172V458.9z m-91.1 71.8c22.5-22.5 46.1-42 69.7-58.1v147.6H211.4c17.6-29.1 40.4-58.6 67.8-86.5l3-3z\" fill=\"#F5A74A\" p-id=\"5630\"><\/path><path d=\"M625.8 266.5l-13.5 9.9c-1.9 1.4-4.6 1-6-0.9-0.6-0.9-0.9-1.9-0.8-3l1.8-16.7L592 249c-2.2-1-3.1-3.5-2.2-5.6 0.4-1 1.2-1.8 2.2-2.2l15.3-6.8-1.8-16.7c-0.3-2.3 1.4-4.4 3.8-4.7 1.1-0.1 2.1 0.2 3 0.8l13.5 9.9 13.5-9.9c1.9-1.4 4.6-1 6 0.9 0.6 0.9 0.9 1.9 0.8 3l-1.8 16.7 15.3 6.8c2.2 1 3.1 3.5 2.2 5.6-0.4 1-1.2 1.8-2.2 2.2l-15.3 6.8 1.8 16.7c0.3 2.3-1.4 4.4-3.8 4.7-1.1 0.1-2.1-0.2-3-0.8l-13.5-9.9z\" fill=\"#333333\" p-id=\"5631\"><\/path><path d=\"M799.4 313.5m-12.8 0a12.8 12.8 0 1 0 25.6 0 12.8 12.8 0 1 0-25.6 0Z\" fill=\"#333333\" p-id=\"5632\"><\/path><path d=\"M828.4 348.3m-12.8 0a12.8 12.8 0 1 0 25.6 0 12.8 12.8 0 1 0-25.6 0Z\" fill=\"#333333\" p-id=\"5633\"><\/path>`;\n\nfunction jinghuafm() {\nconst {java, source} = this;\t\nfunction splitString(str) {\n    const length = str.length;\n    if (length < 15) {\n        return length<9 ? [str] : [\n            str.slice(0, 7),\n            str.slice(7)\n        ].filter(s => s);\n    } else if (length < 20) {\n        return [\n            str.slice(0, 7),\n            str.slice(7,13),\n            str.slice(13)\n        ].filter(s => s);\n    }  else if (length < 24) {\n        return [\n            str.slice(0, 7),\n            str.slice(7,13),\n            str.slice(13,18),\n            str.slice(18)\n        ].filter(s => s);\n    } else {\n        return [\n            str.slice(0, 7),\n            str.slice(7, 13),\n            str.slice(13, 18),\n            str.slice(18, 22),\n            str.slice(22)\n        ].filter(s => s);\n    }\n}\nfunction pingString(ss) {\nlet rr='';\nlet l1=ss.length;\nfor (let i1=0; i1<l1; i1++) {\n\tlet c1=ss[i1], l2=c1.length;\n\tfor (let i2=0; i2<l2; i2++) {\n\t\tlet c2=c1.charAt(i2);\n\t\trr += `<tspan x=\"${100+180*ii}\" y=\"${180+170*(i2+i1)}\">${c2}<\/tspan>`;\n\t\t}\n\t\tii++;\n\t}\n\treturn rr;\n}\n\nlet [cb,cf] = colorz();\n\n\/\/★登录面板按钮里 this.book 可能为 undefined,需保护\nlet bkName = '';\ntry { bkName = (typeof this.book != 'undefined' && this.book) ? String(this.book.name || '') : ''; } catch(e) {}\nif (!bkName) {\n\ttry { bkName = String(cache.getFromMemory('8xid') ? Object.values(cache.getFromMemory('8xid'))[0] : ''); } catch(e2) {}\n}\nif (!bkName) bkName = '八叉书库';\nlet bknm=String(bkName).replace(\/\\.{2,}\/,'…');\nbknm=bknm.match(\/[a-z][a-z\\+一-ꀀ\\d…]+|[一-ꀀ\\da-z]{2,}(?:\\.\\d+[一-ꀀ]*|[…?])?|[一-ꀀ]\/gi);\nlet hz='';\nlet ii=0;\n\nfor (let char of bknm) {\n\thz += pingString(splitString(char) );\n\t}\n\nlet svg = `<svg class=\"icon\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" p-id=\"5623\" width=\"100%\" height=\"100%\">\n<rect width=\"100%\" height=\"150%\" fill=\"#${cb}\"\/>\n<g transform=\"scale(0.4) translate(1600,2500)\">${Oranges}<\/g>\n<text fill=\"#${cf}\" stroke=\"#000000\" stroke-width=\"2\" font-weight=\"bold\" font-size=\"150\" text-anchor=\"middle\" dominant-baseline=\"middle\">\n${hz}\n<\/text>\n<\/svg>`;\nsvg=java.base64Encode(svg);\nreturn 'data:image\/svg+xml;base64,'+svg;\n}\n",
    "lastUpdateTime": "1792471864977",
    "loginCheckJs": "\/\/★年龄确认闸门:保证 CookieJar 里常驻 age18_ok=1\n\/\/★否则详情\/目录\/正文走 java.ajax 时会被 302 到 age_verify.php\n(function(){\n  try {\n    var u = String(result.url());\n    var m = u.match(\/^https?:\\\/\\\/[^\\\/]+\/);\n    if (m) {\n      var org = m[0];\n      var ck = String(cookie.getCookie(org) || '');\n      if (ck.indexOf('age18_ok') < 0) {\n        cookie.setCookie(org, ck ? (ck + '; age18_ok=1') : 'age18_ok=1');\n      }\n    }\n  } catch(e) {}\n  \/\/ 检测到被年龄确认拦截 → 明确提示\n  try {\n    var b = String(result.body ? (typeof result.body==='function'?result.body():result.body) : '');\n    if (b.indexOf('年龄确认') >= 0 && b.indexOf('已成年') >= 0) {\n      java.log('被年龄确认拦截:' + String(result.url()));\n    }\n  } catch(e2) {}\n})();\n\nvar pres = result.raw().priorResponse();\nvar isRedirect = pres!=null&&pres.isRedirect();\nif (isRedirect) {\n\tlet url=String(result.url()).match(\/^https?:\\\/\\\/[^\\\/]+\\\/\/)[0];\n\tif (!\/luoyacheng\/.test(url)) {\n\t\tlet Info=source.getLoginInfoMap()||{};\n\t\tInfo['网址'] = url;\n\t\tsource.putLoginInfo(JSON.stringify(Info));\t}\n}\nvar code = result.code();\nif (code == 403) {\n\tjava.toast('\\n访问被拒绝');\n\t}\nelse if (code == 520) {\n\tjava.toast('\\nIP被拉黑');\n\t}\nresult\n",
    "loginUi": "[\n    {\n        name: \"网址\",\n        type: \"text\",\n        default: \"https:\/\/8xsk.cc\"\n    },\n    {\n        name: \"🚀一键选站\",\n        type: \"button\",\n        action: \"bxpick()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"账号\",\n        type: \"text\"\n    },\n    {\n        name: \"密码\",\n        type: \"password\"\n    },\n    {\n        name: \"💍网址\",\n        type: \"button\",\n        action: \"webfb()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"🌐站点快切\",\n        type: \"button\",\n        action: \"bxs(0)\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"🔍登录诊断\",\n        type: \"button\",\n        action: \"lgdiag()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"🔺登 录🔺\",\n        type: \"button\",\n        action: \"Login()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"♤登出\",\n        type: \"button\",\n        action: \"re()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n    \tname: \"💬信息\",\n    \ttype: \"button\",\n    \taction: \"sm()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1,\n            \"layout_wrapBefore\": true\n        }\n    \t},\n    \t{\n        name: \"🧾延迟测试\",\n        type: \"button\",\n        action: \"ceshi()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"⏭️启用下载\",\n        type: \"button\",\n        action: \"downl()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"🌐打开本书↓\",\n        type: \"button\",\n        action: \"webb()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1,\n            \"layout_wrapBefore\": true\n        }\n    },\n    {\n        name: \"📚加入网站书架\",\n        type: \"button\",\n        action: \"addsh()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"🗑移出网站书架\",\n        type: \"button\",\n        action: \"delsh()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"❌屏蔽\",\n        type: \"button\",\n        action: \"hmd()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"🧹清理\",\n        type: \"button\",\n        action: \"hmdre()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"🗑清缓存\",\n        type: \"button\",\n        action: \"clrc()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"🔧重置设置\",\n        type: \"button\",\n        action: \"reset()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"书名\",\n        type: \"text\"\n    },\n    \t{\n        name: \"🆕版本更新\",\n        type: \"button\",\n        action: \"gengxin()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"🔄自动同步开关\",\n        type: \"button\",\n        action: \"shujia()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"♻️同步网站书架\",\n        type: \"button\",\n        action: \"webbs(1)\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"正则书名黑名单\",\n        type: \"text\"\n    },\n    {\n        name: \"正则书名白名单\",\n        type: \"text\"\n    },\n    \t{\n        name: \"🌐更新日志\",\n        type: \"button\",\n        action: \"gitee()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1,\n            \"layout_wrapBefore\": true\n        }\n    },\n    \t{\n        name: \"🌐打开网站\",\n        type: \"button\",\n        action: \"webbs(2)\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"🔁书名\",\n        type: \"button\",\n        action: \"shuming()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"🗻净化封面\",\n        type: \"button\",\n        action: \"jhfm()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"🌐书源介绍\",\n        type: \"button\",\n        action: \"let option={};option['headers'] = {'User-Agent': java.getWebViewUA()};java.startBrowser('https:\/\/luoyacheng.cloudns.ch\/#八叉,'+JSON.stringify(option),'介绍');\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"🤔反馈\",\n        type: \"button\",\n        action: \"let option={};option['headers'] = {'User-Agent': java.getWebViewUA()};java.startBrowser('https:\/\/github.com\/Luoyacheng\/yuedu\/discussions\/categories\/a-八叉?discussions_q=,'+JSON.stringify(option),'8x反馈');\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    {\n        name: \"封面背景颜色\",\n        type: \"text\"\n    },\n    {\n        name: \"封面字体颜色\",\n        type: \"text\"\n    },\n    \t{\n        name: \"🌐网页登录\",\n        type: \"button\",\n        action: \"weblogin()\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    },\n    \t{\n        name: \"⚒️调试模式\",\n        type: \"button\",\n        action: \"bfqfun2('调试模式')\",\n        \"style\": {\n            \"layout_flexGrow\": 0,\n            \"layout_flexBasisPercent\": -1\n        }\n    }\n]",
    "loginUrl": "\n\/\/ ===== 取表单值:result 优先,退回 getLoginInfoMap(loginUrl 环境可能没有 result)=====\nfunction lgForm(){\n\tconst {java, source} = this;\n\tlet u='', p='';\n\ttry { u = String(result['账号'] || ''); } catch(e) {}\n\ttry { p = String(result['密码'] || ''); } catch(e) {}\n\tif (!u || !p) {\n\t\ttry {\n\t\t\tlet m = source.getLoginInfoMap() || {};\n\t\t\tif (!u) u = String(m['账号'] || '');\n\t\t\tif (!p) p = String(m['密码'] || '');\n\t\t} catch(e2) {}\n\t}\n\treturn {u: u.replace(\/^\\s+|\\s+$\/g,''), p: p};\n}\n\nfunction login(){}\nfunction Login(){\n\tconst {java, source, cookie} = this;\n\tif (String(source.getLoginHeader()).includes(\"jieqiUserInfo\")) {\n\t\tjava.longToast('已登录\\n如需换号请先点「♤登出」');\n\t\treturn;\n\t}\n\tlet surl = getsurl();\n\t\/\/★先看 CookieJar 里是否已经有登录态(用户可能之前已登录成功,只是登录头没同步)\n\tlet preCk = '';\n\ttry { preCk = String(cookie.getCookie(surl) || ''); } catch(e) {}\n\tif (preCk.indexOf('jieqiUserInfo') >= 0 && preCk.indexOf('jieqiUserInfo=deleted') < 0) {\n\t\tsource.putLoginHeader(JSON.stringify({Cookie: preCk}));\n\t\tjava.longToast('\\n✅ 检测到已登录的 Cookie,已自动同步\\nVIP模式💎');\n\t\treturn;\n\t}\n\tsource.removeLoginHeader();\n\tjava.toast('正在获取验证码…');\n\n\t\/\/ ---- 1. 打开登录页,拿验证码地址 ----\n\tlet option = {};\n\toption[\"headers\"] = gethd();\n\tlet lgHtml = '';\n\ttry { lgHtml = String(java.ajax(surl + '\/login.php,' + JSON.stringify(option))); } catch(e) {\n\t\tjava.longToast('登录页打不开:' + String(e).substring(0,60)); return;\n\t}\n\tif (!lgHtml || lgHtml.length < 500) {\n\t\tjava.longToast('登录页异常(' + (lgHtml?lgHtml.length:0) + 'B)\\n请先点「🚀一键选站」'); return;\n\t}\n\n\t\/\/ ---- 2. 解析验证码地址 ----\n\tlet codeUrl = '';\n\ttry {\n\t\t\/\/★八叉的验证码地址不在 img@src(是空的),而在:\n\t\t\/\/  input#checkcode 的 onfocus = \"this.form.imgccode.src='https:\/\/...\/checkcode.php?rand=NNN'\"\n\t\t\/\/  img[name=imgccode] 的 onclick = \"this.src='...\/checkcode.php?rand='+Math.random()\"\n\t\tlet wj = Packages.org.jsoup.Jsoup.parse(lgHtml);\n\t\tlet cands = [\n\t\t\tString(wj.select('#checkcode').attr('onfocus') || ''),\n\t\t\tString(wj.select('img[name=imgccode]').attr('onclick') || ''),\n\t\t\tString(wj.select('#t_imgccode').attr('onclick') || ''),\n\t\t\tString(wj.select('img[name~=img.*code]').attr('onclick') || ''),\n\t\t\tString(wj.select('img[name~=img.*code]').attr('onfocus') || '')\n\t\t];\n\t\tfor (let ci = 0; ci < cands.length; ci++) {\n\t\t\tlet oc = cands[ci];\n\t\t\tif (!oc) continue;\n\t\t\tlet mm = oc.match(\/['\"]([^'\"]*checkcode\\.php[^'\"]*)['\"]\/i);\n\t\t\tif (!mm) continue;\n\t\t\tcodeUrl = mm[1];\n\t\t\tif (codeUrl.indexOf('http') != 0) codeUrl = surl + '\/' + codeUrl.replace(\/^[\/]+\/, '');\n\t\t\tcodeUrl = codeUrl.replace(\/[+].*$\/, '');   \/\/ 去掉 +Math.random()\n\t\t\tif (codeUrl) break;\n\t\t}\n\t\t\/\/ 兜底:直接扫 HTML 里的 checkcode.php?rand=数字\n\t\tif (!codeUrl) {\n\t\t\tlet m2 = lgHtml.match(\/checkcode\\.php\\?rand=[0-9]*\/i);\n\t\t\tif (m2) codeUrl = surl + '\/' + m2[0];\n\t\t}\n\t\t\/\/ 再兜底:自己拼(站点固定路径)\n\t\tif (!codeUrl) codeUrl = surl + '\/checkcode.php?rand=' + Math.floor(Math.random()*1e9);\n\t} catch(e) { java.log('解析验证码地址失败:' + e); }\n\n\tif (!codeUrl) {\n\t\tjava.longToast('💔 未找到验证码地址\\n请改用「🌐网页登录」'); return;\n\t}\n\tjava.log('验证码地址:' + codeUrl);\n\n\t\/\/ ---- 3. 让用户输入验证码 ----\n\tlet code = '';\n\ttry {\n\t\tcode = String(java.getVerificationCode(codeUrl + ',' + JSON.stringify(option)) || '').replace(\/^\\s+|\\s+$\/g, '');\n\t} catch(e) {\n\t\tjava.longToast('验证码弹窗失败:' + String(e).substring(0,60)); return;\n\t}\n\tif (!code) { java.longToast('未输入验证码,已取消'); return; }\n\n\tlet f = lgForm();\n\tlet un = f.u;\n\tlet pw = f.p;\n\tif (!un || !pw) { java.longToast('💔 账号和密码不能为空'); return; }\n\n\t\/\/ ---- 4. POST 登录(表单字段与站点 frmlogin 一致)----\n\tlet body = 'username=' + encodeURIComponent(un)\n\t         + '&password=' + encodeURIComponent(pw)\n\t         + '&checkcode=' + encodeURIComponent(code)\n\t         + '&usecookie=2592000&act=login&submit=';\n\tlet opt2 = {};\n\topt2[\"headers\"] = gethd();\n\topt2[\"headers\"][\"Referer\"] = surl + '\/login.php';\n\topt2[\"method\"] = \"POST\";\n\topt2[\"body\"] = body;\n\tjava.toast('登录中…');\n\n\tlet resp = null, respCode = 0, respBody = '';\n\ttry {\n\t\tresp = java.connect(surl + '\/login.php?do=submit&jumpurl=' + encodeURIComponent(surl + '\/') + ',' + JSON.stringify(opt2));\n\t\trespCode = resp.code();\n\t\trespBody = String(resp.body() || '');\n\t} catch(e) {\n\t\tjava.longToast('登录请求异常:' + String(e).substring(0,80)); return;\n\t}\n\n\t\/\/ ---- 5. ★关键:从 CookieJar 读 Cookie(不再用 headers().values(\"Set-Cookie\"))----\n\tlet ck = '';\n\ttry { ck = String(cookie.getCookie(surl) || ''); } catch(e) {}\n\t\/\/ CookieJar 为空时,退回从响应头读(兼容旧行为)\n\tif (ck.indexOf('jieqiUserInfo') < 0) {\n\t\ttry {\n\t\t\tlet raw = String(resp.headers().get('Set-Cookie') || '');\n\t\t\tif (raw && raw != 'null') {\n\t\t\t\tlet parts = raw.split(',');\n\t\t\t\tlet buf = [];\n\t\t\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\t\t\tlet seg = String(parts[i]).replace(\/;.*$\/, '').replace(\/^\\s+|\\s+$\/g, '');\n\t\t\t\t\tif (seg && seg.indexOf('=') > 0) buf.push(seg);\n\t\t\t\t}\n\t\t\t\tif (buf.length) {\n\t\t\t\t\tck = buf.join('; ');\n\t\t\t\t\tcookie.setCookie(surl, ck);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch(e2) {}\n\t}\n\n\t\/\/ ---- 6. 判定登录结果 ----\n\tlet ok = ck.indexOf('jieqiUserInfo') >= 0 && ck.indexOf('jieqiUserInfo=deleted') < 0;\n\tif (ok) {\n\t\tsource.putLoginHeader(JSON.stringify({Cookie: ck}));\n\t\ttry {\n\t\t\tlet m = source.getLoginInfoMap() || {};\n\t\t\tm['账号'] = un; m['密码'] = pw;\n\t\t\tsource.putLoginInfo(JSON.stringify(m));\n\t\t} catch(e) {}\n\t\tlet nm = '';\n\t\tlet mn = ck.match(\/jieqiUserName%3D([^%]+)\/) || ck.match(\/jieqiUserName=([^;,]+)\/);\n\t\tif (mn) { try { nm = decodeURIComponent(mn[1]); } catch(e3) { nm = mn[1]; } }\n\t\tjava.longToast('\\n✅ 登录成功' + (nm ? (':' + nm) : '') + '\\nVIP模式💎');\n\t\treturn;\n\t}\n\n\t\/\/ ---- 7. 失败诊断:给出可操作原因 ----\n\tlet reason = '';\n\tif (respBody.indexOf('验证码错误') >= 0) reason = '验证码错误(请重新点登录,注意区分大小写)';\n\telse if (respBody.indexOf('验证码') >= 0) reason = '验证码错误';\n\telse if (respBody.indexOf('密码错误') >= 0 || respBody.indexOf('密码不正确') >= 0) reason = '账号或密码错误';\n\telse if (respBody.indexOf('不存在') >= 0) reason = '账号不存在';\n\telse if (respBody.indexOf('frmlogin') >= 0) reason = '仍停在登录页(凭据未通过)';\n\telse if (respCode == 403) reason = '访问被拒绝(403)';\n\telse if (respCode >= 500) reason = '服务端错误(' + respCode + ')';\n\telse if (!respBody) reason = '响应为空';\n\telse reason = '未知(响应 ' + respBody.length + 'B)';\n\tjava.longToast('\\n❌ 登录失败\\n原因:' + reason\n\t\t+ '\\n\\n可改用「🌐网页登录」\\n(在浏览器里登录后自动同步 Cookie)');\n\tjava.log('登录失败:code=' + respCode + ' body=' + respBody.substring(0, 400));\n}\n\nfunction weblogin(){\n\tconst {java, source, cookie} = this;\n\tif (String(source.getLoginHeader()).includes(\"jieqiUserInfo\")) {\n\t\tjava.longToast('已登录\\n如需换号请先点「♤登出」');\n\t\treturn;\n\t}\n\tlet nn = getsurl();\n\tsource.removeLoginHeader();\n\tlet option = {};\n\toption[\"headers\"] = gethd();\n\toption[\"headers\"][\"User-Agent\"] = java.getWebViewUA();\n\tjava.toast('正在打开登录页…');\n\ttry {\n\t\tjava.startBrowserAwait(nn + '\/login.php,' + JSON.stringify(option), '八叉登录', false);\n\t} catch(e) {\n\t\tjava.longToast('打开浏览器失败:' + String(e).substring(0,60)); return;\n\t}\n\tPackages.java.lang.Thread.sleep(300);\n\t\/\/★关键:从 CookieJar 读,而不是响应头\n\tlet ck = '';\n\ttry { ck = String(cookie.getCookie(nn) || ''); } catch(e) {}\n\tif (ck.indexOf('jieqiUserInfo') >= 0 && ck.indexOf('jieqiUserInfo=deleted') < 0) {\n\t\tsource.putLoginHeader(JSON.stringify({Cookie: ck}));\n\t\tjava.longToast('\\n✅ 网页登录成功\\nVIP模式💎');\n\t} else {\n\t\tjava.longToast('\\n❌ 未检测到登录 Cookie\\n\\n可能原因:\\n1) 浏览器里没登录成功\\n2) 登录后提前返回了\\n\\n可再点一次「🌐网页登录」,登录成功后停留 2 秒再返回');\n\t}\n}\n\nfunction ceshi(){\n\tconst {java, source, cookie} = this;\n\tlet nn = getsurl();\n\tlet option = {};\n\toption[\"headers\"] = gethd();\n\tjava.toast('测试线路:' + nn);\n\tlet lines = [];\n\tlet t0 = new Date().getTime();\n\ttry {\n\t\tlet web = java.connect(nn + ',' + JSON.stringify(option));\n\t\tlet ms = new Date().getTime() - t0;\n\t\tlet body = String(web.body() || '');\n\t\tlet code = web.code();\n\t\tlines.push('线路:' + nn);\n\t\tlines.push('状态码:' + code);\n\t\tlines.push('耗时:' + (ms\/1000).toFixed(2) + ' 秒');\n\t\tlines.push('大小:' + body.length + ' B');\n\t\tif (code == 403) lines.push('❌ 访问被拒绝');\n\t\telse if (code == 520) lines.push('❌ IP 被拉黑');\n\t\telse if (body.length < 3000) lines.push('❌ 返回异常(可能被年龄确认拦截)');\n\t\telse if (body.indexOf('年龄确认') >= 0) lines.push('❌ 被年龄确认拦截(Cookie 未生效)');\n\t\telse if (body.indexOf('八叉') >= 0) lines.push('✅ 正常');\n\t\telse lines.push('⚠️ 内容异常');\n\t\t\/\/ 顺带报告登录态\n\t\tlet ck = '';\n\t\ttry { ck = String(cookie.getCookie(nn) || ''); } catch(e) {}\n\t\tlines.push('Cookie:' + (ck.indexOf('jieqiUserInfo') >= 0 ? '已登录💎' : '访客🔒'));\n\t} catch(err) {\n\t\tlines.push('❌ 连接失败');\n\t\tlines.push(String(err).substring(0, 100));\n\t}\n\tjava.longToast(lines.join('\\n'));\n}\n\nfunction re(){\n\tconst {java, source, cookie} = this;\n\tlet surl = getsurl();\n\tjava.toast('正在登出…');\n\t\/\/ 1. 通知站点登出\n\ttry {\n\t\tlet ck = ckget(source);\n\t\tlet opt = {};\n\t\topt[\"headers\"] = gethd();\n\t\tif (ck) opt[\"headers\"][\"Cookie\"] = ck;\n\t\tlet url = \/http.{4}www\/.test(surl) ? (surl + '\/logout.php') : (surl + '\/userdetail.php');\n\t\tjava.ajax(url + ',' + JSON.stringify(opt));\n\t} catch(e) {}\n\t\/\/ 2. 清登录头\n\ttry { source.removeLoginHeader(); } catch(e) {}\n\t\/\/ 3. 清 CookieJar(只保留 age18_ok)\n\ttry {\n\t\tcookie.setCookie(surl, 'age18_ok=1');\n\t} catch(e) {}\n\t\/\/ 4. 清缓存\n\ttry { cache.deleteMemory('8xid'); } catch(e) {}\n\ttry { cache.deleteMemory('8xhmd'); } catch(e) {}\n\t\/\/ 5. 清表单\n\ttry {\n\t\tlet m = source.getLoginInfoMap() || {};\n\t\tm['账号'] = ''; m['密码'] = '';\n\t\tsource.putLoginInfo(JSON.stringify(m));\n\t} catch(e) {}\n\tjava.longToast('\\n已登出,回到访客模式🔒');\n}\n\nfunction sm(){\n\tconst {java, source, cookie} = this;\n\tlet surl = getsurl();\n\tlet banben = '?';\n\ttry { banben = String(source.exploreUrl).match(\/let banben=\"([\\d\\.]+)\"\/)[1]; } catch(e) {}\n\tlet hdr = ckget(source);\n\tlet jar = '';\n\ttry { jar = String(cookie.getCookie(surl) || ''); } catch(e) {}\n\tlet hasH = hdr.indexOf('jieqiUserInfo') >= 0;\n\tlet hasC = jar.indexOf('jieqiUserInfo') >= 0 && jar.indexOf('jieqiUserInfo=deleted') < 0;\n\tlet nm = '';\n\tlet mn = (hasC ? jar : hdr).match(\/jieqiUserName%3D([^%]+)\/) || (hasC ? jar : hdr).match(\/jieqiUserName=([^;,]+)\/);\n\tif (mn) { try { nm = decodeURIComponent(mn[1]); } catch(e) { nm = mn[1]; } }\n\tlet mode = (hasH || hasC) ? ('VIP模式💎' + (nm ? ('  ' + nm) : '')) : '访客模式🔒';\n\tlet t = '当前线路:' + surl + '\\n版本:' + banben + '\\n模式:' + mode\n\t\t+ '\\n登录头:' + (hasH ? '已设置' : '未设置')\n\t\t+ '\\nCookie:' + (hasC ? '有登录态' : '无');\n\tjava.longToast(t);\n}\n\nfunction webck(){\n\tconst {java, source, cookie} = this;\n\tlet nn = getsurl();\n\tlet nnm = nn.replace('www.','');\n\tlet ck = ckget(source);\n\t\/\/★关键:cookie.setCookie 是「整体替换」语义,直接写登录头会把 age18_ok 清掉\n\t\/\/★导致浏览器打开后又是「年龄确认」页 → 表现为「打开网站\/书架失效」\n\ttry {\n\t\tlet cur = String(cookie.getCookie(nnm) || '');\n\t\tlet merged = '';\n\t\tif (cur && cur.indexOf('age18_ok') >= 0) merged = cur;\n\t\telse merged = cur ? (cur + '; age18_ok=1') : 'age18_ok=1';\n\t\tif (ck && merged.indexOf('jieqiUserInfo') < 0) {\n\t\t\t\/\/ 把登录头的键合并进去(保留 age18_ok)\n\t\t\tlet parts = ck.split(';');\n\t\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\t\tlet kv = String(parts[i]).replace(\/^\\s+|\\s+$\/g, '');\n\t\t\t\tif (!kv || kv.indexOf('=') <= 0) continue;\n\t\t\t\tlet k = kv.substring(0, kv.indexOf('='));\n\t\t\t\tif (k == 'age18_ok') continue;\n\t\t\t\tif (merged.indexOf(k + '=') >= 0) continue;\n\t\t\t\tmerged += '; ' + kv;\n\t\t\t}\n\t\t}\n\t\tcookie.setCookie(nnm, merged);\n\t\ttry { cache.put('8xbff', source.getLoginInfo()); } catch(e1) {}\n\t\ttry { cache.put('8xbff2', ck); } catch(e1) {}\n\t} catch(e) {}\n\treturn nnm;\n}\n\nfunction webb(){\n\tconst {java, source, cache, cookie} = this;\n\t\/\/ 1. 取书籍 id(缓存 → 当前 book)\n\tlet id = '', bkName = '';\n\ttry {\n\t\tlet m = cache.getFromMemory('8xid');\n\t\tif (m) { let k = Object.keys(m)[0]; id = String(k); bkName = String(m[k] || ''); }\n\t} catch(e) {}\n\tif (!id) {\n\t\ttry {\n\t\t\tlet bu = String((typeof book != 'undefined' && book) ? (book.bookUrl || '') : '');\n\t\t\tlet mm = bu.match(\/book[\/](\\d+)\/);\n\t\t\tif (mm) { id = mm[1]; bkName = String(book.name || ''); }\n\t\t} catch(e2) {}\n\t}\n\tif (!id) {\n\t\tjava.longToast('\\n未获取到书籍 ID\\n\\n请先打开一本书的详情页\\n或在书架里点进书籍后重试');\n\t\treturn;\n\t}\n\t\/\/ 2. 同步 Cookie(保证 age18_ok 在)\n\tlet site = webck();\n\tlet jar = String(cookie.getCookie(site) || 'age18_ok=1');\n\tlet bkUrl = site + '\/book\/' + id + '.html';\n\t\/\/ 3. 优先系统浏览器(内置 WebView 可能被墙)\n\tlet opened = false;\n\ttry {\n\t\tjava.openUrl(bkUrl);\n\t\topened = true;\n\t\tjava.toast('已用系统浏览器打开《' + (bkName || id) + '》');\n\t} catch(e3) {}\n\tif (!opened) {\n\t\ttry {\n\t\t\tlet ua = java.getWebViewUA();\n\t\t\tjava.startBrowser(bkUrl + ',' + JSON.stringify({headers:{'User-Agent':ua,'Cookie':jar}}), '八叉');\n\t\t\topened = true;\n\t\t} catch(e4) {}\n\t}\n\tif (!opened) java.longToast('打开失败\\n请手动复制网址:\\n' + bkUrl);\n}\n\n\/\/ ===== 📚 把当前书籍加入网站书架(纯 HTTP,不用浏览器)=====\nfunction addsh(){\n\tconst {java, source, cache, cookie} = this;\n\t\/\/ 取书籍 id\n\tlet id = '', bkName = '';\n\ttry {\n\t\tlet m = cache.getFromMemory('8xid');\n\t\tif (m) { let k = Object.keys(m)[0]; id = String(k); bkName = String(m[k] || ''); }\n\t} catch(e) {}\n\tif (!id) {\n\t\ttry {\n\t\t\tlet bu = String((typeof book != 'undefined' && book) ? (book.bookUrl || '') : '');\n\t\t\tlet mm = bu.match(\/book[\/](\\d+)\/);\n\t\t\tif (mm) { id = mm[1]; bkName = String(book.name || ''); }\n\t\t} catch(e2) {}\n\t}\n\tif (!id) {\n\t\tjava.longToast('\\n未获取到书籍 ID\\n\\n请先打开一本书的详情页'); return;\n\t}\n\tlet site = webck();\n\tlet jar = String(cookie.getCookie(site) || '');\n\tif (jar.indexOf('jieqiUserInfo') < 0) {\n\t\tjava.longToast('\\n❌ 请先登录\\n\\n加入网站书架需要登录账号'); return;\n\t}\n\tjava.toast('正在加入网站书架…');\n\tlet ua = java.getWebViewUA();\n\tlet tm = new Date().getTime();\n\tlet body = 'bid=' + id + '&ajax_request=' + tm;\n\tlet opt = {\n\t\theaders: {'User-Agent': ua, 'Cookie': jar, 'Referer': site + '\/book\/' + id + '.html'},\n\t\tmethod: 'POST', body: body, timeout: 25000\n\t};\n\tlet msg = '';\n\ttry {\n\t\tlet r = java.connect(site + '\/modules\/article\/addbookcase.php,' + JSON.stringify(opt));\n\t\tmsg = String(r.body() || '').replace(\/<[^>]+>\/g, '').replace(\/\\s+\/g, ' ').trim();\n\t} catch(e3) {\n\t\tjava.longToast('加入失败:' + String(e3).substring(0,80)); return;\n\t}\n\tjava.log('addbookcase 返回:' + msg);\n\tif (msg.indexOf('恭喜') >= 0 || msg.indexOf('已经加入') >= 0 || msg.indexOf('已在') >= 0) {\n\t\tjava.longToast('\\n✅ ' + msg + '\\n\\n《' + (bkName || id) + '》');\n\t} else {\n\t\tjava.longToast('\\n⚠️ 返回:' + (msg || '空响应') + '\\n\\n可点「♻️同步书架」核对');\n\t}\n}\n\n\/\/ ===== 📚 从网站书架移除当前书籍 =====\nfunction delsh(){\n\tconst {java, source, cache, cookie} = this;\n\tlet id = '';\n\ttry {\n\t\tlet m = cache.getFromMemory('8xid');\n\t\tif (m) { id = String(Object.keys(m)[0]); }\n\t} catch(e) {}\n\tif (!id) {\n\t\ttry {\n\t\t\tlet bu = String((typeof book != 'undefined' && book) ? (book.bookUrl || '') : '');\n\t\t\tlet mm = bu.match(\/book[\/](\\d+)\/);\n\t\t\tif (mm) id = mm[1];\n\t\t} catch(e2) {}\n\t}\n\tif (!id) { java.longToast('未获取到书籍 ID'); return; }\n\tlet site = webck();\n\tlet jar = String(cookie.getCookie(site) || '');\n\tif (jar.indexOf('jieqiUserInfo') < 0) { java.longToast('请先登录'); return; }\n\tlet ua = java.getWebViewUA();\n\tlet tm = new Date().getTime();\n\tlet body = 'bid=' + id + '&ajax_request=' + tm;\n\tlet opt = {\n\t\theaders: {'User-Agent': ua, 'Cookie': jar, 'Referer': site + '\/book\/' + id + '.html'},\n\t\tmethod: 'POST', body: body, timeout: 25000\n\t};\n\tlet msg = '';\n\ttry {\n\t\tlet r = java.connect(site + '\/modules\/article\/delbookcase.php,' + JSON.stringify(opt));\n\t\tmsg = String(r.body() || '').replace(\/<[^>]+>\/g, '').replace(\/\\s+\/g, ' ').trim();\n\t} catch(e3) {\n\t\tjava.longToast('移除失败:' + String(e3).substring(0,80)); return;\n\t}\n\tjava.log('delbookcase 返回:' + msg);\n\tjava.longToast('\\n返回:' + (msg || '空响应'));\n}\n\nfunction webbs(x){\n\tconst {java, source, cookie} = this;\n\tlet nm = webck();\n\tlet jar = String(cookie.getCookie(nm) || 'age18_ok=1');\n\tlet ua = java.getWebViewUA();\n\n\t\/\/ ============ x=1:同步网站书架(纯 HTTP,不用浏览器)============\n\tif (x == 1) {\n\t\t\/\/ 1. 检查登录\n\t\tif (jar.indexOf('jieqiUserInfo') < 0) {\n\t\t\tjava.longToast('\\n❌ 请先登录\\n\\n同步书架需要登录账号\\n点「🔺登 录🔺」或「🌐网页登录」');\n\t\t\treturn;\n\t\t}\n\t\tjava.toast('正在读取网站书架…');\n\t\tlet b = '';\n\t\ttry {\n\t\t\tb = String(java.connect(nm + '\/modules\/article\/bookcase.php,{\"headers\":{\"User-Agent\":\"' + ua + '\",\"Cookie\":\"' + jar + '\"},\"timeout\":25000}').body() || '');\n\t\t} catch(e) {\n\t\t\tjava.longToast('读取书架失败:' + String(e).substring(0,60)); return;\n\t\t}\n\t\tif (!b || b.length < 1000) {\n\t\t\tjava.longToast('书架页异常(' + (b?b.length:0) + 'B)\\n请检查网络或换线路'); return;\n\t\t}\n\t\tif (b.indexOf('frmlogin') >= 0) {\n\t\t\tjava.longToast('❌ 未登录或登录已失效\\n请重新登录'); return;\n\t\t}\n\t\t\/\/ 2. 解析书架里的书\n\t\t\/\/★真实结构:div.c_row.cf 卡片 → div.row_text h4 a 是书名(href 带 aid=N)\n\t\t\/\/★aid 才是书籍 id;bid 是站点内部 id,不能用来拼 \/book\/\n\t\tlet ids = [], names = [], seen = {};\n\t\ttry {\n\t\t\tlet j = Packages.org.jsoup.Jsoup.parse(b);\n\t\t\tlet cards = j.select('div.c_row.cf');\n\t\t\tif (cards.size() > 0) {\n\t\t\t\tfor (let i = 0; i < cards.size(); i++) {\n\t\t\t\t\tlet c = cards.get(i);\n\t\t\t\t\tlet a = c.select('div.row_text h4 a').first();\n\t\t\t\t\tif (a == null) a = c.select('a[href*=aid=]').first();\n\t\t\t\t\tif (a == null) continue;\n\t\t\t\t\tlet href = String(a.attr('href') || '');\n\t\t\t\t\tlet mm = href.match(\/[?&]aid=(\\d+)\/);\n\t\t\t\t\tif (!mm) continue;\n\t\t\t\t\tlet id = mm[1];\n\t\t\t\t\tif (seen[id]) continue;\n\t\t\t\t\tseen[id] = 1;\n\t\t\t\t\tids.push(id);\n\t\t\t\t\tnames.push(String(a.text() || '').replace(\/^\\s+|\\s+$\/g, ''));\n\t\t\t\t}\n\t\t\t}\n\t\t\t\/\/ 兜底:没有 c_row 卡片时全页扫\n\t\t\tif (!ids.length) {\n\t\t\t\tlet as = j.select('a[href*=aid=]');\n\t\t\t\tfor (let i2 = 0; i2 < as.size(); i2++) {\n\t\t\t\t\tlet a2 = as.get(i2);\n\t\t\t\t\tlet mm2 = String(a2.attr('href') || '').match(\/[?&]aid=(\\d+)\/);\n\t\t\t\t\tif (!mm2 || seen[mm2[1]]) continue;\n\t\t\t\t\tseen[mm2[1]] = 1;\n\t\t\t\t\tids.push(mm2[1]);\n\t\t\t\t\tnames.push(String(a2.text() || '').replace(\/^\\s+|\\s+$\/g, ''));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch(e2) {\n\t\t\tjava.log('解析书架失败:' + e2);\n\t\t}\n\t\t\/\/ 兜底:正则扫\n\t\tif (!ids.length) {\n\t\t\tlet re = \/[?&]aid=(\\d+)\/g, m3;\n\t\t\twhile ((m3 = re.exec(b)) !== null) {\n\t\t\t\tif (!seen[m3[1]]) { seen[m3[1]] = 1; ids.push(m3[1]); names.push(''); }\n\t\t\t}\n\t\t}\n\t\tif (!ids.length) {\n\t\t\tjava.longToast('书架里没有书\\n\\n可先点「🌐打开网站」在网页里加书'); return;\n\t\t}\n\t\t\/\/ 3. 逐本加入 Legado 书架\n\t\tlet okN = 0;\n\t\tfor (let k = 0; k < ids.length; k++) {\n\t\t\tlet bkUrl = nm + '\/book\/' + ids[k] + '.html';\n\t\t\tlet dl = 'legado:\/\/import\/addToBookshelf?src=' + encodeURIComponent(bkUrl) + ',{\"origin\":\"https:\/\/八叉书库.luoyacheng.ip-ddns.com\"}';\n\t\t\ttry { java.openUrl(dl); okN++; } catch(e3) {}\n\t\t\t\/\/ 间隔,避免深链连发被系统吞\n\t\t\tif (k < ids.length - 1) { try { Packages.java.lang.Thread.sleep(700); } catch(e4) {} }\n\t\t}\n\t\tlet preview = [];\n\t\tfor (let p = 0; p < Math.min(3, ids.length); p++) {\n\t\t\tpreview.push('· ' + (names[p] || ids[p]));\n\t\t}\n\t\tif (ids.length > 3) preview.push('· …等 ' + ids.length + ' 本');\n\t\tjava.longToast('\\n✅ 网站书架共 ' + ids.length + ' 本\\n已发起加入 ' + okN + ' 本\\n────────\\n'\n\t\t\t+ preview.join('\\n') + '\\n────────\\n若未全部加入,可重复点此按钮');\n\t\treturn;\n\t}\n\n\t\/\/ ============ x=2:打开网站(优先系统浏览器)============\n\tlet target = nm;\n\tlet opt = JSON.stringify({headers:{'User-Agent': ua, 'Cookie': jar}});\n\tlet opened = false;\n\t\/\/ 优先用系统浏览器(内置 WebView 可能被墙)\n\ttry {\n\t\tjava.openUrl(target);\n\t\topened = true;\n\t\tjava.toast('已用系统浏览器打开');\n\t} catch(e) {}\n\tif (!opened) {\n\t\ttry { java.startBrowser(target + ',' + opt, '八叉'); opened = true; } catch(e2) {}\n\t}\n\tif (!opened) java.longToast('打开失败\\n请手动在浏览器访问:\\n' + target);\n}\n\nfunction hmd(){\n\tlet id = cache.getFromMemory('8xid');\n\tif (id) {\n\tlet hmd = cache.get('8xhmd')||'{}';\n\thmd = JSON.parse(hmd);\n\tlet idd = Object.keys(id)[0];\n\tif (idd in hmd) {\n\t\tdelete hmd[idd];\n\t\tcache.put('8xhmd',JSON.stringify(hmd),0);\n\t\tjava.longToast('\\n已将书籍:'+Object.values(id)[0]+'\\n移除黑名单😅');\n\t\t}\telse {\n\thmd = Object.assign({},hmd,id);\n\tcache.put('8xhmd',JSON.stringify(hmd),0);\n\tjava.longToast('\\n已将书籍:'+Object.values(id)[0]+'\\n加入黑名单😤');}\n\t\n\t} else {java.toast('\\n请在书籍界面点击或刷新书籍');}\n\t\n}\n\nfunction hmdre(){\n\tlet hmd=cache.get('8xhmd')||'{}';\n\thmd=JSON.parse(hmd);\n\tlet ss=Object.entries(hmd);\nlet [cb,cf] = colorz();\nlet svg=`<svg width=\"990\" height=\"300\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n<style>\ntext {\n\twhite-space: pre;\n\tword-wrap: break-word;}\n<\/style>\n<rect width=\"100%\" height=\"150%\" fill=\"#${cb}\"\/>\n<text fill=\"#${cf}\" text-anchor=\"middle\" dominant-baseline=\"middle\" stroke=\"#000000\" stroke-width=\"1\" font-weight=\"bold\">\n<tspan x=\"50%\" dy=\"30%\" font-size=\"100\">\n是否清空黑名单<tspan font-size=\"30\">(${ss.length})<\/tspan>\n<\/tspan>\n<tspan x=\"42%\" dy=\"25%\" font-size=\"60\">输入“确认”进行清空<\/tspan>\n<tspan x=\"50%\" dy=\"20%\" font-size=\"50\">输入“名单”打印一份名单到日志<\/tspan>\n<tspan x=\"50%\" dy=\"20%\" font-size=\"50\">输入书籍ID单独移出黑名单<tspan font-size=\"30\">(名单查看)<\/tspan><\/tspan>\n<\/text>\n<\/svg>`;\nsvg=java.base64Encode(svg);\n\ntry {\n\/\/拦截验证码报错\nlet ysr=java.getVerificationCode('data:image\/svg+xml;base64,'+svg);\nPackages.java.lang.Thread.sleep(100);\nif (ysr=='确认') {\n\tcache.delete('8xhmd');\n\tjava.toast('\\n已清空');\n}\nelse if (ysr=='名单') {\n\tjava.log('\\n💔8x黑名单:\\n'+ss.join('\\n'));\n\tcache.put('8xbff',source.getLoginInfo());\n\tcache.put('8xbff2',ckget(source));\n}\nelse if (\/\\d{3,}\/.test(ysr)) {\n\tif (ysr in hmd) {\n\tjava.toast('\\n已将书籍:'+hmd[ysr]+'\\n移除黑名单');\n\tdelete hmd[ysr];\n\tcache.put('8xhmd',JSON.stringify(hmd),0);\n\t} else {java.toast('移出失败');}\n}\nelse if (ysr=='恢复') {\n\tlet ryr=cache.get('8xbff');\n\tlet ryr2=cache.get('8xbff2');\n\tsource.putLoginInfo(ryr);\n\tsource.putLoginHeader(JSON.stringify({Cookie: ryr2}));\n\tjava.toast('\\n已恢复账号信息');\n}\n\n} catch(err) {\n\tjava.toast('清理面板未打开(可重试)');\n}\n}\n\nfunction downl() {\nlet dn = source.get('8xdn');\nif(dn!=1) {source.put('8xdn',1);java.toast('已支持下载');}\nelse {source.put('8xdn',0);java.toast('已优化加载');}\n\t}\nfunction shujia() {\nlet dn = source.get('8xsj');\nif(dn!=1) {source.put('8xsj',1);java.toast('🟢已启用同步网站书架');}\nelse {source.put('8xsj',0);java.toast('⚫已关闭同步网站书架');}\n\t}\nfunction shuming() {\nlet sm = source.get('8xsm');\nif(sm!=1) {source.put('8xsm',1);java.toast('🟢书名跟随更新');}\nelse {source.put('8xsm',0);java.toast('⚫书名保持初始');}\n\t}\nfunction jhfm() {\nlet jhf = source.get('8xjhf');\nif(jhf!=1) {source.put('8xjhf',1);java.toast('😈净化封面');}\nelse {source.put('8xjhf',0);java.toast('😍显示封面');}\n\t}\n\nfunction gitee() {\nlet option={};\noption[\"headers\"] = {\"User-Agent\": java.getWebViewUA()};\njava.startBrowser('https:\/\/gitee.com\/lyc486\/yuedu2\/commits\/main\/书源\/八叉\/8x.json'+','+JSON.stringify(option),'日志');\n}\nfunction bfqfun2(x) {\nlet xx = source.get('bx'+x);\n\/\/默认关闭\nif(xx!=1) {source.put('bx'+x,1);java.toast('🟢已开启 '+x);}\nelse {source.put('bx'+x,0);java.toast('⚫已关闭 '+x);}\n\t}\n\n\/\/ ===== 🚀一键选站:逐个测速发布页列出的站点,选最快的写入「网址」 =====\nfunction bxpick(){\n\tconst {java, source} = this;\n\tlet pool = ['https:\/\/8xsk.cc','https:\/\/18xsk.com','https:\/\/18xsk.cc','https:\/\/bachashuku.org','https:\/\/bachashuku.com'];\n\tlet ua = this.gethd()['User-Agent'] || 'Mozilla\/5.0';\n\tjava.toast('正在测速 ' + pool.length + ' 个站点…');\n\tlet ok = [], bad = [];\n\tfor (let i = 0; i < pool.length; i++) {\n\t\tlet u = pool[i];\n\t\tlet t0 = new Date().getTime();\n\t\ttry {\n\t\t\tlet hd = {'User-Agent': ua, 'Cookie': 'age18_ok=1'};\n\t\t\tlet b = String(java.connect(u + ',' + JSON.stringify({headers: hd, timeout: 12000})).body());\n\t\t\tlet ms = new Date().getTime() - t0;\n\t\t\tif (b.length > 3000 && b.indexOf('年龄确认') < 0 && b.indexOf('八叉') >= 0) ok.push({u: u, ms: ms});\n\t\t\telse bad.push(u + ' (无效页 ' + b.length + 'B)');\n\t\t} catch (e) { bad.push(u + ' (不通)'); }\n\t}\n\tok.sort(function(a, b){ return a.ms - b.ms; });\n\tif (!ok.length) {\n\t\tjava.longToast('❌ 所有站点都不通\\n' + bad.join('\\n') + '\\n\\n请检查网络,或点「💍网址」从发布页找新域名');\n\t\treturn;\n\t}\n\tlet best = ok[0];\n\t\/\/ 写回登录信息(getsurl 读的就是这里)\n\tlet Info = source.getLoginInfoMap() || {};\n\tInfo['网址'] = best.u;\n\tsource.putLoginInfo(JSON.stringify(Info));\n\t\/\/ 顺带清掉旧缓存\n\ttry { java.put('8xzx',''); } catch(e) {}\n\tlet lines = ['✅ 已选:' + best.u + '  ' + best.ms + 'ms', '────────'];\n\tfor (let i = 0; i < ok.length; i++) lines.push('✅ ' + ok[i].u + '  ' + ok[i].ms + 'ms');\n\tfor (let j = 0; j < bad.length; j++) lines.push('❌ ' + bad[j]);\n\tlines.push('────────');\n\tlines.push('已写入「网址」,直接返回即可使用');\n\tjava.longToast(lines.join('\\n'));\n}\n\n\/\/ ===== 🔍 登录诊断:一步步检查,定位卡在哪 =====\nfunction lgdiag(){\n\tconst {java, source, cookie} = this;\n\tlet out = [];\n\tlet nn = getsurl();\n\tout.push('【1】线路:' + nn);\n\n\t\/\/ 2. 登录页可达性\n\tlet lgHtml = '';\n\ttry {\n\t\tlet opt = {}; opt[\"headers\"] = gethd();\n\t\tlgHtml = String(java.ajax(nn + '\/login.php,' + JSON.stringify(opt)) || '');\n\t\tout.push('【2】登录页:' + (lgHtml ? (lgHtml.length + ' B') : '失败'));\n\t\tif (lgHtml && lgHtml.indexOf('frmlogin') >= 0) out.push('     ✅ 含登录表单');\n\t\telse if (lgHtml) out.push('     ⚠️ 无登录表单');\n\t} catch(e) { out.push('【2】登录页异常:' + String(e).substring(0,50)); }\n\n\t\/\/ 3. 验证码地址\n\tlet codeUrl = '';\n\ttry {\n\t\tlet wj = Packages.org.jsoup.Jsoup.parse(lgHtml);\n\t\tlet cands = [\n\t\t\tString(wj.select('#checkcode').attr('onfocus') || ''),\n\t\t\tString(wj.select('img[name=imgccode]').attr('onclick') || ''),\n\t\t\tString(wj.select('#t_imgccode').attr('onclick') || ''),\n\t\t\tString(wj.select('img[name~=img.*code]').attr('onclick') || '')\n\t\t];\n\t\tfor (let ci = 0; ci < cands.length; ci++) {\n\t\t\tlet oc = cands[ci];\n\t\t\tif (!oc) continue;\n\t\t\tlet mm = oc.match(\/['\"]([^'\"]*checkcode\\.php[^'\"]*)['\"]\/i);\n\t\t\tif (!mm) continue;\n\t\t\tcodeUrl = mm[1];\n\t\t\tif (codeUrl.indexOf('http') != 0) codeUrl = nn + '\/' + codeUrl.replace(\/^[\/]+\/,'');\n\t\t\tcodeUrl = codeUrl.replace(\/[+].*$\/, '');\n\t\t\tif (codeUrl) break;\n\t\t}\n\t\tif (!codeUrl) { let m2 = lgHtml.match(\/checkcode\\.php\\?rand=[0-9]*\/i); if (m2) codeUrl = nn + '\/' + m2[0]; }\n\t\tout.push('【3】验证码地址:' + (codeUrl || '未找到'));\n\t} catch(e) { out.push('【3】解析失败:' + String(e).substring(0,50)); }\n\n\t\/\/ 4. 验证码图片可达性\n\tif (codeUrl) {\n\t\ttry {\n\t\t\tlet r = java.connect(codeUrl + ',{\"type\":\"hex\",\"headers\":{\"Cookie\":\"age18_ok=1\"}}');\n\t\t\tlet hex = String(r.body() || '');\n\t\t\tlet isPng = hex.substring(0,8) == '89504e47';\n\t\t\tlet isJpg = hex.substring(0,6) == 'ffd8ff';\n\t\t\tlet isGif = hex.substring(0,6) == '474946';\n\t\t\tout.push('【4】验证码图片:' + hex.length + ' hex | ' + (isPng?'PNG':isJpg?'JPEG':isGif?'GIF':'未知'));\n\t\t} catch(e) { out.push('【4】验证码图片失败:' + String(e).substring(0,50)); }\n\t}\n\n\t\/\/ 5. CookieJar 状态\n\tlet jar = '';\n\ttry { jar = String(cookie.getCookie(nn) || ''); } catch(e) {}\n\tout.push('【5】CookieJar:' + (jar ? (jar.length + ' B') : '空'));\n\tout.push('     age18_ok:' + (jar.indexOf('age18_ok') >= 0 ? '有' : '无'));\n\tout.push('     jieqiUserInfo:' + (jar.indexOf('jieqiUserInfo') >= 0 ? '有(已登录)' : '无(访客)'));\n\n\t\/\/ 6. 登录头\n\tlet hdr = ckget(source);\n\tout.push('【6】登录头:' + (hdr ? (hdr.length + ' B') : '空'));\n\tout.push('     含 jieqiUserInfo:' + (hdr.indexOf('jieqiUserInfo') >= 0 ? '是' : '否'));\n\n\t\/\/ 7. 表单值\n\tlet df = lgForm();\n\tout.push('【7】账号:' + (df.u || '(空)'));\n\tout.push('     密码:' + (df.p ? '已填' : '(空)'));\n\n\tjava.log(out.join('\\n'));\n\tjava.longToast(out.join('\\n'));\n}\n\n\/\/ ===== 🌐 快速切换站点(按钮直接用)=====\nfunction bxs(x){\n\tconst {java, source} = this;\n\tlet pool = ['https:\/\/8xsk.cc','https:\/\/18xsk.com','https:\/\/18xsk.cc','https:\/\/bachashuku.org','https:\/\/bachashuku.com'];\n\tlet u = pool[x];\n\tif (!u) { java.toast('无效站点序号'); return; }\n\tlet m = source.getLoginInfoMap() || {};\n\tm['网址'] = u;\n\tsource.putLoginInfo(JSON.stringify(m));\n\ttry { java.put('8xzx',''); } catch(e) {}\n\tjava.longToast('已切换到:' + u);\n}\n\n\/\/ ===== 🧹 清空缓存(不影响登录)=====\nfunction clrc(){\n\tconst {java, source, cache} = this;\n\tlet keys = ['8xid','8xzx','8xzxt','8xhmd','baxxq','bxvip','baxtime','baxtime2'];\n\tlet n = 0;\n\tfor (let i = 0; i < keys.length; i++) {\n\t\ttry { cache.deleteMemory(keys[i]); n++; } catch(e) {}\n\t\ttry { cache.delete(keys[i]); } catch(e) {}\n\t}\n\t\/\/ 清目录缓存(v_{key}_ 前缀)\n\ttry {\n\t\tlet k0 = source.getKey();\n\t\tlet ck = ckget(source);\n\t\tif (ck) source.putLoginHeader(JSON.stringify({Cookie: ck}));   \/\/ 保留登录\n\t} catch(e) {}\n\tjava.longToast('已清理 ' + n + ' 项缓存\\n(登录态与设置保留)');\n}\n\n\/\/ ===== 🔧 重置全部设置(保留账号密码)=====\nfunction reset(){\n\tconst {java, source} = this;\n\tlet keep = source.getLoginInfoMap() || {};\n\tlet acct = String(keep['账号'] || '');\n\tlet pwd = String(keep['密码'] || '');\n\tlet ks = ['8xdn','8xsj','8xsm','8xjhf','bx调试模式'];\n\t\/\/★保留颜色配置\n\tlet c1 = String(keep['封面背景颜色'] || '');\n\tlet c2 = String(keep['封面字体颜色'] || '');\n\tfor (let i = 0; i < ks.length; i++) {\n\t\ttry { source.put(ks[i], '0'); } catch(e) {}\n\t}\n\t\/\/ 清登录头与 Cookie,但保留账号密码与网址\n\ttry { source.removeLoginHeader(); } catch(e) {}\n\ttry {\n\t\tlet site = getsurl();\n\t\tcookie.setCookie(site, 'age18_ok=1');\n\t} catch(e) {}\n\ttry {\n\t\tlet m = { '账号': acct, '密码': pwd, '网址': String(keep['网址'] || '') };\n\t\tif (c1) m['封面背景颜色'] = c1;\n\t\tif (c2) m['封面字体颜色'] = c2;\n\t\tsource.putLoginInfo(JSON.stringify(m));\n\t} catch(e) {}\n\tjava.longToast('已重置:\\n· 所有开关回到默认\\n· 已清除登录态\\n· 账号密码与网址保留');\n}\n",
    "respondTime": 180000,
    "ruleBookInfo": {
        "author": "<js>\nlet r='';\ntry {\n  let j = Packages.org.jsoup.Jsoup.parse(String(result));\n  \/\/★作者链接有两种:authorpage.php?id= \/ authorarticle.php?author=\n  \/\/★必须排除 newmessage.php(「作者认领本书」「成为本书编辑」)\n  let as = j.select('div.row_textl a[href*=authorpage], div.row_textl a[href*=authorarticle]');\n  for (let i=0;i<as.size();i++) {\n    let a = as.get(i);\n    let href = String(a.attr('href'));\n    if (href.indexOf('newmessage')>=0) continue;\n    let t = String(a.text()).replace(\/^\\s+|\\s+$\/g,'');\n    if (!t) continue;\n    if (t.indexOf('编辑')>=0 || t.indexOf('认领')>=0 || t.indexOf('>>>')>=0) continue;\n    r = t; break;\n  }\n} catch(err) {}\nif (!r) r = String(java.getString('class.divbox cf@span.1@a@text')||'');\nif (!r) {\n  try {\n    let g = String(java.getString('div.row_textl p.gray@text')||'');\n    let m = g.match(\/([^\\s|]+)\\s*著\/);\n    if (m) r = m[1];\n  } catch(e2) {}\n}\nr = String(r).replace(\/^\\s+|\\s+$\/g,'');\nr || String(book.author||'')\n<\/js>",
        "coverUrl": "#content img.0@src\n<js>\nlet jhf=source.get('8xjhf');\nresult=gethdi(result);\nif (jhf=='1') result=jinghuafm();\nresult\n<\/js>",
        "init": "<js>\n\/\/重定向和导入书架处理\nlet i = String(book.bookUrl).includes('https:\/');\nif (i) {\n\tbook.bookUrl = `data:;base64,${java.base64Encode(baseUrl.replace(\/,{or.+\/,''))},{\"type\":\"bax\"}`;\n\t\/\/注意入库origin\n\t}\n\n\/\/告诉目录是由详情来的\njava.put('sx1',1);\nlet nn=getsurl();\n\n\/\/发书评功能\nlet bkbl = book.getVariable(\"custom\");\n\/\/解书籍地址\nlet ml = String(book.bookUrl).replace(\/.+,(.+),.+\/,'$1');\nlet mlj = java.base64Decode(ml);\nlet [, bkid=''] = mlj.match(\/book\\\/(\\d+)\/)||[];\n\/\/↑获取书id\njava.put('bkid',bkid);\n\nif (bkbl=='🔒'||bkbl=='') {\n\/\/带🔒或书籍变量为空会经过这里\n\/\/处理加书架功能\nlet sj=java.get('sj');\nlet sj2=source.get('8xsj'); \/\/启用同步的标识\nif (sj=='2'&&sj2=='1') {java.put('sj',3);tbshujia(nn,bkid);}\nelse if (sj=='1') {java.put('sj',2);}\n\n} else if (bkbl=='目录') {\n\t\/\/清空最新章储存强制刷新目录\n\tjava.put('8xzx','');\n\t\/\/清空输入的书籍变量\n\tbook.putCustomVariable('');\n} else {\nlet pfurl = nn+'\/modules\/article\/rating.php';\nlet plurl = nn+'\/modules\/article\/reviews.php';\nlet ck = ckget(source);\nlet cki = ck.match(\/UserToken%3D([^%]+)\/)||['',''];\ntk = cki[1];\n\/\/↑获取链接和tk\n\nif (bkid != '' && tk != '') {\nlet tm = new Date().getTime();\nlet bkfs = (bkbl.match(\/^(\\d|10)分$\/) || ['','0'])[1];\nlet pfbody = `score=${bkfs}&id=${bkid}&ajax_request=${tm}`;\nlet plbody = `ptitle=&act=newpost&jieqi_token=${tk}&Submit=&pcontent=${bkbl}&aid=${bkid}&ajax_request=${tm}`;\n\nlet headers = gethd();\nheaders['Referer']=mlj;\nheaders[\"Cookie\"]=ck;\nlet url='',body='';\nif (bkfs != 0) {url=pfurl;body=pfbody;}\nelse {url=plurl;body=plbody;}\n\/\/清空输入的书籍变量\nbook.putCustomVariable('');\nlet by = java.connect(url+','+JSON.stringify(\n    \t{\"method\": \"POST\",\n    \t\"header\":headers,\n    \t\"body\":body})\n    \t).body();\njava.longToast(\"\\n\"+java.htmlFormat(by));\n\/\/随机延时0-1秒\nlet tim = Math.floor(Math.random()*1001);\nPackages.java.lang.Thread.sleep(tim);\ncache.deleteMemory('baxxq'+bkid);\n\t} else if (tk == '') {java.toast('未登录');}\n}\n\n\/\/先发评论再访问刷新\nif (!i) {\n\t\/\/跳转时,src为源码\n\tlet tt = java.hexDecodeToString(src);\n\t\/\/不要直接访问tt\n\ttt=String(tt).replace(\/http.{4}[A-z\\d\\.]+\/,'');\nlet option={};\noption[\"headers\"]=gethd();\n\tconst startTime=new Date().getTime();\n\tresult = cache.getFromMemory('baxxq'+bkid) || java.ajax(nn+tt+','+JSON.stringify(option))+'';\n\tconst endTime=new Date().getTime();\n\tlet speed = (endTime - startTime)\/1000;\n\tif (speed>39) {java.toast('\\n详情页访问延迟过长:'+speed.toFixed()+'秒');}\t\n}\n\tjava.setContent(result);\n\n\/\/内容判断\n\/\/★手机版:h4.mbs 内含 <span class=\"fr fss\">(作者认领\/编辑链接)→ 必须用 ownText 排除\n\/\/★PC 版:class.divbox cf 里 span.0\/span.1\nlet name='';\ntry {\n\tlet jn = Packages.org.jsoup.Jsoup.parse(String(result));\n\tlet e1 = jn.select('h4.mbs').first();\n\tif (e1 != null) name = String(e1.ownText());\n\tif (!name) name = java.getString('class.divbox cf@span.0@text')+'';\n} catch(e) { name = java.getString('class.divbox cf@span.0@text')+''; }\nname = String(name).replace(\/^《|》$\/g,'').replace(\/^\\s+|\\s+$\/g,'');\n\nlet author='';\ntry {\n\tlet ja = Packages.org.jsoup.Jsoup.parse(String(result));\n\tlet as = ja.select('div.row_textl a[href*=authorpage], div.row_textl a[href*=authorarticle]');\n\tfor (let ai=0; ai<as.size(); ai++) {\n\t\tlet ae = as.get(ai);\n\t\tlet ah = String(ae.attr('href'));\n\t\tif (ah.indexOf('newmessage')>=0) continue;\n\t\tlet at = String(ae.text()).replace(\/^\\s+|\\s+$\/g,'');\n\t\tif (!at) continue;\n\t\tif (at.indexOf('编辑')>=0 || at.indexOf('认领')>=0 || at.indexOf('>>>')>=0) continue;\n\t\tauthor = at; break;\n\t}\n\tif (!author) author = java.getString('class.divbox cf@span.1@a@text')+'';\n} catch(e2) { author = java.getString('class.divbox cf@span.1@a@text')+''; }\nif (String(author)=='' || String(author)=='undefined') {\n\tlet g = gets(\".gray@text\");\n\t[,author] = (String(g).match(\/([^\\s|]+)\\s*著\/) || ['','']);\n}\nauthor = String(author).replace(\/^\\s+|\\s+$\/g,'');\nif (name) {\n\tcache.putMemory('baxxq'+bkid,result);\n\tbook.name=String(book.name)||name;\n\tbook.author=String(book.author)||author;\n\/\/放入书名\nlet Info=source.getLoginInfoMap()||{};\nInfo['书名'] = book.name;\nsource.putLoginInfo(JSON.stringify(Info));\n\/\/放入ID和书名。正式版该语法会报错\ncache.putMemory('8xid',{[bkid]: book.name});\n\n\tif (book.name!=name) {\n\t\t\tlet sm=source.get('8xsm'),sm1=java.get('sm1');\n\t\t\t\/\/sm功能标记,sm1判断标记\n\t\t\tif (sm1=='') {\n\t\t\t\/\/书名不同,仅记录\n\t\t\tjava.put('sm1',1);\n\t\t\t} else if (sm=='1') {\n\t\t\t\tbook.name=name;\n\t\t\t\tjava.toast(\"已自动更新书名\");\n\t\t\t} else if (sm1=='1') {\n\t\t\t\tjava.longToast(\"详情页获取的书名:\\n《\"+name+\"》\");\n\t\t\t\tjava.log(\"书名:\"+name);\n\t\t\t\tjava.put('sm1',2);\n\t\t\t}\n\t\t\t\n}\telse if (book.author!=author) {\n\t\/\/ book.author=author;\n\t\/\/仅输出日志\njava.log(\"获取的作者不同,作者:\"+author);}\n\t\n\t\/\/书名和作者正常由此过\n\t}\nelse {\n\t\/\/书名加载失败\n\tgengxin();\n\tjava.toast(\"详情页加载失败,请刷新重试\");\n}\n\tresult\n<\/js>",
        "intro": "{{\n\/\/书名过长时在简介显示\nlet length = 0;\nlet fuhao = ',.?!:;()[]\"';\nlet tt='&#10;';\nfor (char of String(book.name)) {\n\tif (fuhao.includes(char) ) length += 0.5;\n\telse length += 1;\n\t}\n\/\/大于18个全角字符\nif (length > 18) tt+='📙书名:'+book.name;\n\nlet zx = java.get('8xzxt');\n\nlet rr=[];\nif (\/http.{4}www\/.test(getsurl())) {\nlet temp=java.getStringList('class.tabcontent@class.tabvalue.1@td[0:3,9,10]@text');\nrr=Object.values(temp);\n\/\/处理rr\n[rr[0], rr[2]] = [rr[2], rr[0]];\nrr[0] = '🕰️最新:' + String(zx).replace(\/^正文|^\\s+\/,'') + String(rr[0]).replace('最后更新:',' ').replaceAll('-','\/') + String(rr[1]).replace(\/连载状态:(.+)\/,' ($1)');\ndelete rr[1];\nrr[2] = String(rr[2]).replace(\/作品分类:\\s*\/,'🗂️分类:') + String(rr[3]).replace(\/作品性质:(.+)\/,' ($1)');\ndelete rr[3];\nrr[4] = String(rr[4]).replace(\/总点击数:\\s*\/,'🎯点击:') + String(rr[5]).replace(\/本月点击:(.+)\/,' $1 (月)');\ndelete rr[5];\n} else {rr[0]='🕰️最新:' + String(zx).replace(\/^正文|^\\s+\/,'')+'\\n'+'🎯点击:'+(gets(\".gray@ownText\").match(\/著 (\\d+)\\s*\\|\/)||['',''])[1];}\n\n\/\/评分\nlet pf=java.getString('.ratediv@text');\nlet r0 ='\\n' + rr.join('\\n') + '\\n' + String(pf).replace(\/评分:\\s*\/,'💡评分:');\n\/\/简介\nlet r1=java.getString('.tabcontent .tabvalue:eq(0) div@html||#introa@textNodes');\nif (r1==\"\") {r1='';}\nelse {\n r1 = '\\n🗨️简介:\\n' + String(r1).replace(\/[\\u3000\\u2003]{2}\/g,'\\n').replace('「','“').replace('」','”').replace(\/\\>\\s*内容简介\/,'')\n\t}\n\/\/评论\nlet r2=String(java.getString('#content>.c_row@html||.moretop+.ullist>li@html'));\nlet xi = book.getVariable(\"custom\")==\"🔒\";\nif (r2==''||xi) {r2='';}\nelse {\n\tif (\/http.{4}www\/.test(getsurl()))\n\tr2 = \"\\u200b\\n📝评论:\"+r2\n\t.replace(\/<div class=\"c_subject\"[\\S\\s]+?div>|<span class=\"fr\"[\\S\\s]+?span>|点击:.+(?=回复)\/g,' ')\n\t.replace(\/回复:.+c_value\">0\/g,'')\n\t.replace(\/<p[>\\ ].+<\\\/p>\/g,'\\u200b');\n\telse r2 = \"\\u200b\\n📝评论:\"+r2\n\t.replace(\/<li><a\/g,'<li><p>\\u200b<\/p><a')\n\t.replace(\/<\\\/em><b>\/g,'<\/em> 👨<b>');\n\t}\n\ttt+r0 + r1 + r2\n}}",
        "kind": "b:contains(标签:)~a@text||div.row_textl a[href*=tagarticle]@text\n<js>\nlet s=String(result);\n\/\/手机版会是「#后宫 #下克上」,PC 版是「标签:#a #b」\nif (\/^\\[.*\\]$\/.test(s)) s=s.slice(1,-1);\ns=s.replace(\/[\\\\#\\\\,]\/g,'').replace(\/^\\s+|\\s+$\/g,'');\ns.split(\/\\s+\/);\n<\/js>",
        "lastChapter": "<js>\nlet zxe=gete(\".divbox h3 a||span:contains(最新:)+a||div.c_row.nw span.note~a\");\nlet zxh=String(zxe.attr(\"href\")||'').replaceAll(\"http.{4}[^\\\\\\\\\/]+\",'');\nlet zx = java.get('8xzx');\nif (zx==zxh) java.put('ww',0);\nelse {java.put('8xzx',zxh);java.put('ww',1);}\njava.put(\"8xzxt\",zxe.text())\n<\/js>",
        "name": "<js>\nlet r='';\ntry {\n  let j = Packages.org.jsoup.Jsoup.parse(String(result));\n  let e = j.select('h4.mbs').first();\n  if (e != null) r = String(e.ownText());\n} catch(err) {}\nif (!r) r = String(java.getString('class.divbox cf@span.0@text')||'');\nr = String(r).replace(\/^《|》$\/g,'').replace(\/^\\s+|\\s+$\/g,'');\nr || String(book.name||'')\n<\/js>",
        "tocUrl": "li:matches(^点击阅读$|^目录$) a@href||ul.tabb a:matchesWholeOwnText(目录)@href\n<js>\nif(result.length>3)\n`data:;base64,${java.base64Encode(result)},{\"type\":\"ml\"}`;\nelse {\nlet ml = String(book.bookUrl).replace(\/.+,(.+),.+\/,'$1');\nlet mlj = java.base64Decode(ml);\n`data:;base64,${java.base64Encode(mlj.replace('.html','_1.html'))},{\"type\":\"ml\"}`;}\n<\/js>",
        "wordCount": "<js>\nlet r1=`{{@@.tabcontent .tabvalue.1@html##全文字数:(\\\\d+)##$1###}}`;\nlet r2=`{{@@.gray@ownText##(\\\\d{2,})字##$1###}}`;\nlet r3=`{{@@div.row_textl p.gray@text##(\\\\d{2,})字##$1###}}`;\nr1||r2||r3\n<\/js>"
    },
    "ruleContent": {
        "content": "<js>\nconst startTime3=new Date().getTime();\nlet bin = book.durChapterIndex;\nlet cin = chapter.index;\nlet inn = cin - bin;\nlet dn = source.get('8xdn');\nlet sleep = Packages.java.lang.Thread.sleep;\n\/\/java.log(chapter.title+'inn:'+inn);\nif ((inn==-4||inn==4)&&dn!=1) {java.longToast('\\n批量下载需要在书源登录界面点「下载功能」开启\\n(阅读预下载建议设为 2~3)');}\n\nlet nn=getsurl();\n\nlet tim = Math.floor(Math.random()*2001);\n\/\/★正文解密函数(atob+escape,首次访问与购买后共用)\nfunction decContent(r){\n\tif (String(r).indexOf('atob') < 0) return r;\n\tlet b64 = String(r).match(\/atob\\(\\s*[^A-Za-z0-9+\/]{0,4}([A-Za-z0-9+\/=]{100,})\/);\n\tif (!b64) return '';\n\ttry {\n\t\tlet dec = String(java.base64Decode(b64[1]));\n\t\ttry { dec = decodeURIComponent(escape(dec)); } catch(e1) {}\n\t\tdec = dec.replace(\/<br\\s*\\\/?>\/gi, '<br>');\n\t\treturn dec;\n\t} catch(e2) { java.log('正文解密失败:'+e2); return ''; }\n}\nlet ii = 1;\n\/\/手动限制并发,下载时(0-2*30秒随机)\n\nfunction delayl(ll){\n\t\/\/★下载模式(8xdn=1)直接放行:批量下载不应有任何排队等待\n\t\/\/  旧版这里 5s全局锁 + 0~10s随机 + while(dn!=1) 死等 → 单章 40 秒\n\tif (dn==1) return;\n\tsleep(tim);\nlet dtm=Date.now()+(3000+tim)*10;\n\twhile (dn!=1) {\nPackages.java.util.concurrent.locks.LockSupport.parkNanos(this,30*1e6);\n\t\tlet innx = cin - book.durChapterIndex;\n\tif (innx==0) return;\n\telse if (innx==-1&&Date.now()<dtm) continue;\n\telse {ii=0;return;}\n\t\t}\n}\n\nlet bkid = java.get('bkid'); \/\/★提前定义:节流与缓存键都要用\n\/\/★节流策略:下载模式(8xdn=1)完全跳过;阅读模式保留预下载保护\n\/\/  旧版把「下载」也当阅读预下载处理,导致每章等 1~7.5s 甚至直接返回空\nif (dn==1) {\n\t\/\/批量下载:只做站点最低限度的礼貌间隔,不做长等待\n\tif (inn>0) sleep(300);\n} else switch(inn) {\n\tcase 0: break;\n\tcase 1: sleep(1000+tim); break;\n\tcase 2: sleep(4500+tim); break;\n\tcase 3: sleep(7500+tim); break;\n\tcase -1: delayl(-1); break;\n\tcase -2: delayl(-2); break;\n\tcase -3: delayl(-3); break;\n\tdefault: ii=0; break;\n}\n\n\/\/延时结束\n\nif (inn==0||inn==1) {\n\/\/放入书名和id\ncache.putMemory('8xid',{[bkid]: book.name});\nlet Info=source.getLoginInfoMap()||{};\nInfo['书名'] = book.name;\nsource.putLoginInfo(JSON.stringify(Info));\n\/\/ 本地阅读进度\nlet sjj=source.get('sj'+bkid)+'';\nif (java.get('sj')==3||sjj) {\tsource.put('sj'+bkid,book.author+','+book.durChapterTitle+','+book.durChapterIndex);}\n}\n\nlet tt = \/^[0-9a-fA-F]+$\/.test(String(result).trim()) ? java.hexDecodeToString(result) : String(result);\ntt=String(tt).replace(\/https\\:\\\/{2}[A-z\\d\\.]+\/,'');\n\/\/java.log('正文地址:'+tt);\nlet ml = String(book.tocUrl).replace(\/.+,(.+),.+\/,'$1');\nlet mlj = java.base64Decode(ml);\n\nlet [,cid]=tt.match(\/(\\d+)\\.html\/)||[,''];\n\nif (ii) {\n\/\/↓第一次访问正文\ntry {\n\t\t\/\/★阅读限速锁:下载模式(dn==1)完全跳过——批量下载不做排队\/互斥\n\t\tlet baxtime = Number(cache.getFromMemory('baxtime'))||0;\n\t\tif (baxtime<Date.now()) baxtime=Date.now();\n\t\tbaxtime+=2000;\n\tlet dtm=Date.now()+(3000+tim)*10;\n\tcache.putMemory('baxtime',baxtime);\n\tif (dn!=1) {\n\tlet li=(Date.now()<baxtime||cache.getFromMemory('bxvip'+bkid))&&Date.now()<dtm;\n\twhile(li) {\n\t\tPackages.java.lang.Thread.sleep(1000);\n\t\tli=(Date.now()<baxtime||cache.getFromMemory('bxvip'+bkid))&&Date.now()<dtm&&(cin-book.durChapterIndex!=0);\nsource.get('bx调试模式')==1&&java.log(chapter.title+'🍊'+li+'🍊');}\n\tcache.putMemory('bxvip'+bkid,true);\n\t}\n\nlet option={};\noption[\"headers\"]=gethd();\nconst startTime=new Date().getTime();\n\nif (cid) {\n\/\/\tif (bkid < 1000) n = 0;\n\/\/\telse n = bkid.slice(0,-3);\n\/\/\tresult = java.ajax('https:\/\/js.8xsk.org\/'+n+'\/'+bkid+'\/'+cid+'.js'+','+JSON.stringify(option)).replaceAll(String.raw`^document.write\\('|'\\);$`,'');\n\t} else {\n\t[,cid]=tt.match(\/cid=(\\d+)\/)||[,''];\n\tlet ck=ckget(source);\n\tif (ck) option[\"headers\"][\"Cookie\"]=ck;}\n\tresult = java.ajax(nn+tt+','+JSON.stringify(option));\n\t\/\/★手机版正文是 decodeURIComponent(escape(atob(\"...\"))) 加密,需源内解密\n\tif (String(result).indexOf('atob') >= 0) {\n\t\tlet b64 = String(result).match(\/atob\\(\\s*[^A-Za-z0-9+\/]{0,4}([A-Za-z0-9+\/=]{100,})\/);\n\t\tif (b64) {\n\t\t\ttry {\n\t\t\t\tlet dec = String(java.base64Decode(b64[1]));\n\t\t\t\t\/\/escape\/decodeURIComponent 等价:把 %XX 还原成 UTF-8\n\t\t\t\ttry { dec = decodeURIComponent(escape(dec)); } catch(e1) {}\n\t\t\t\tdec = dec.replace(\/<br\\s*\\\/?>\/gi, '<br>');\n\t\t\t\tresult = dec;\n\t\t\t} catch(e2) { java.log('正文解密失败:'+e2); result = ''; }\n\t\t} else { result = ''; }\n\t} else {\n\t\tresult = java.getString('[id*=\"content\"]@html',result);\n\t}\n\t\nconst endTime=new Date().getTime();\nlet speed = (endTime - startTime)\/1000;\nif (dn!=1&&speed>39) java.toast('\\n正文页访问延迟过长:'+speed.toFixed()+'秒');\n\/\/复原手机版链接,干掉字数统计。重新刷新时不为这个链接,不会导致二次加载\n\n\/\/拦截超时报错\n} catch (e) {if (\/timeout\/.test(e)) java.toast('正文超时'); else {inn==0&&java.toast('正文异常:'+e);}\njava.log('八叉正文出错:'+e);}\n\/\/ if ii 结束\n\t} else {result='';}\n\/\/inn>1时为空,软件会自动重新获取\n\/\/★手机版没有 .mt.mb\/.tc.mb\/.block.mt,补 button 与 #asummary 兜底\nlet rr = java.getString('class.mt mb@a@text||class.tc mb@a@text||class.block mt@button@text||button@text',result);\n\/\/内容识别\nif (rr.includes(\"登录\")) {\n\tif (dn!=1) java.toast(\"💎请重新登录书源账号\");\n\tresult='';}\nelse if (rr.includes(\"充值\")||rr.includes(\"确定订阅\")||rr.includes(\"订阅本章节\")) {\nlet url = nn+'\/modules\/obook\/buychapter.php';\nlet ck = ckget(source)+'';\nlet [,tk] = ck.match(\/UserToken%3D([^%]+)\/)||[,''];\nlet body = `buytype=0&act=buy&jieqi_token=${tk}&cid=${cid}&submit=`;\n\nlet option={};\noption[\"headers\"]=gethd();\noption[\"headers\"]['Referer']=tt;\noption[\"headers\"][\"Cookie\"]=ck;\noption[\"method\"]=\"POST\";\noption[\"body\"]=body;\nif (tk && nn && cid) {\n\ttry {\n\tsleep(2100-tim);\n\tlet pt = java.connect(url+','+JSON.stringify(option));\n  let lc = pt.url()+'';\n  if (lc.includes('\/read\/')||lc.includes('\/obook\/')) {\n  \tif(dn!=1)java.toast(\"已为你自动购买\");\n  \tresult = decContent(pt.body());}\n  \telse if (lc.includes('buychapter')) {\n  \t\tif(dn!=1)java.toast('购买失败\\n检测到未重定向');\n  \t\tlet pb = decContent(pt.body());\n  \t\tresult = pb || java.getString('[id*=\"content\"]@html',pt.body());}\n  \telse if(dn!=1)java.toast('购买异常');\n \t} catch (err) {if(dn!=1)java.longToast(\"💔VIP内容获取失败\"); result='';}\n} else if (!tk) {if(dn!=1)java.toast(\"💎书源未获取到登录信息\"); result='';} else if(dn!=1)java.longToast('nn或cid为空');\n} else {\n\/\/无VIP登录\n}\ncache.deleteMemory('bxvip'+bkid);\/\/不能put(false,java和js转化问题;读模式下若无锁也安全删除\n\nconst endTime3=new Date().getTime();\nsource.get('bx调试模式')==1&&java.toast(nn+tt+\"时间\"+(endTime3-startTime3)\/1000);\n\/\/检查\nif ([19,339].includes(cin)&&Math.abs(inn)<4&&dn!=1) gengxin();\nresult\n<\/js>",
        "replaceRegex": "##(?:\\s|^)本站发布页.{0,9}8xsk.org|8xsk.org(?=\\s|$)|八叉书库|八叉~书库|八叉书[,~]库|(?:833330|8xsk|八叉)([^\\.])(?:xyz|yxz|org|书库)|八~叉|书~库##$1"
    },
    "ruleExplore": {
        "author": "<js>\nlet r='';\ntry {\n  if (baseUrl.includes('search.php?')) {\n    r = String(java.getString(String(source.ruleSearch.author))||'');\n  } else if (baseUrl.includes('\/bookcase.php')) {\n    java.put('sj',3);\n    let sj = String(source.get('sj'+String(java.get('bkid')))||'');\n    r = sj.split(',')[0]||'';\n  } else {\n    let self = (typeof result.select=='function') ? result : null;\n    let p = self ? self.select('p.gray').first() : null;\n    if (p != null) {\n      let h = String(p.html());\n      let seg = h.split(\/<br[ ]*[\/]?>\/i);\n      let l0 = String(seg[0]||'').replace(\/<[^>]+>\/g,'').replace(\/[ ]+\/g,' ').trim();\n      let parts = l0.split('|');\n      r = String(parts[1]||'').trim();\n    }\n    if (!r) r = String(java.getString('td.1@a.-1@span@ownText')||'').replace(\/^[\/]\/,'');\n  }\n  if (String(r) == '[email protected]') {\n    let hex = String(java.getString('.__cf_email__.0@data-cfemail')||'');\n    r = decodeEmail(hex,0);\n  }\n} catch(e) {}\nr\n<\/js>",
        "bookList": "<js>\nif (baseUrl.startsWith('http:\/\/localhost') ) {\n\t\tresult = [];\n}\nelse if (baseUrl.includes('search.php?') ) {\nlet r1 = java.getString('#content .blockcontent@text');\nif (r1.includes(\"错误原因\")) {java.longToast(r1);result = [];}\nelse {result = java.getElement('#jieqi_page_contents>div');}\n\t}\nelse if (baseUrl.includes('\/book\/') ){\njava.toast('暂不支持重定向结果');\nresult=[];\n\t}\nelse if (baseUrl.includes('\/bookcase.php') ) {\n\t\/\/我的书架\n\t\tlet ts= java.getString('table@caption@ownText||#jieqi_page_contents@ownText');\n\tts=String(ts).replace(\/.*(可收藏 .+已收藏[^,。]+).*$\/,'\\n$1');\n\tsource.get('sjts')!=ts&&ts&&java.toast('\\n'+ts);\n\tsource.put('sjts',ts);\n\tPackages.java.lang.Thread.sleep(100);\n\tif (\/http.{4}www\/.test(this.getsurl()))\n\tresult = java.getElement('table tr:has(a)');\n\telse result = java.getElements('#content div[class=\"c_row cf\"]');\n\t}\nelse {\nresult = java.getElements('#jieqi_page_contents>tr||#content div[class=\"c_row cf\"]');\n}\nresult\n<\/js>",
        "bookUrl": "@js:book.bookUrl",
        "coverUrl": "<js>\nlet jhf=source.get('8xjhf'), img;\nif (jhf=='1') img=jinghuafm();\nelse {\n\/\/正常封面\nif (\/articlelist|sort\\\/\/.test(baseUrl)) {\n\t\/\/含有封面,注意“sort”\n\timg = java.getString('img@src');\n\t}\nelse if (baseUrl.includes('search.php?') ) {\n\t\/\/搜索\n\timg = java.getString(String(source.ruleSearch.coverUrl));\n\t}\nelse{\n\t\/\/拼接封面\n\tlet id = java.get('bkid')+'', n;\n  if (id < 1000) n = 0;\n  else n = id.slice(0,-3);\nimg = `https:\/\/image.8xsk.org\/${n}\/${id}\/${id}s.jpg`;\n}\n\n}\ngethdi(img)\n<\/js>",
        "intro": "<js>\nif (baseUrl.includes('search.php?'))\nresult=java.getString(source.ruleSearch.intro+[])+'';\nelse if (\/http.{4}www\/.test(this.getsurl()))\nresult = gets('td.3@text||.c_description@text');\nelse\nresult = gets('td.2@text')||gets(\".gray@textNodes\").match(\/\\n(.+)\/)[1];\nif (baseUrl.includes(\"top\/words\")) {'⠀\\n字数:'+(result\/10000).toFixed(2)+'万'}\nelse if (baseUrl.includes(\"top\/goodnum\")) {'⠀\\n收藏数:'+result}\n\/\/新书榜\nelse if (baseUrl.includes(\"top\/newhot\")) {\n\tresult = result>9999 ? \t(result\/10000).toFixed(2)+'万' : result;\n\t'⠀\\n点击数:'+result}\nelse if (baseUrl.includes(\"top\/signtime\")) {\n  let t = parseInt(result)*1000;\n  let time = java.timeFormatUTC(t,\"⠀\\n签约时间:yy\/MM\/dd HH:mm:ss\",8*60*60*1000);\n  time}\nelse if (baseUrl.includes(\"top\/lastupdate\")) {'⠀\\n字数:'+(result\/10000).toFixed(2)+'万'}\nelse if (baseUrl.includes(\"top\/postdate\")) {'⠀\\n入库时间:'+result}\nelse if (baseUrl.includes(\"top\/allvisit\")) {\n\tresult = result>999 ? \t(result\/10000).toFixed(2)+'万' : result;\n\t'⠀\\n总点击:'+result}\nelse if (baseUrl.includes(\"top\/monthvisit\")) {\n\tresult = result>999 ? \t(result\/10000).toFixed(2)+'万' : result;\n\t'⠀\\n月点击:'+result}\nelse if (baseUrl.includes(\"top\/weekvisit\")) {\n\tresult = result>9999 ? \t(result\/10000).toFixed(2)+'万' : result;\n\t'⠀\\n周点击:'+result}\nelse if (baseUrl.includes(\"top\/allvote\")) {'⠀\\n总推荐:'+result}\nelse if (baseUrl.includes(\"top\/monthvote\")) {'⠀\\n月推荐:'+result}\nelse if (baseUrl.includes(\"top\/weekvote\")) {'⠀\\n周推荐:'+result}\nelse if (\/articlelist|sort\\\/|search\\.php\/.test(baseUrl)) {\n\/\/含简介(搜索会经此)\nresult.replace('「','“').replace('」','”').replace(\/\\s+\/g,' ')}\nelse if (baseUrl.includes('\/bookcase.php') ) {\n\tlet sj=source.get('sj'+java.get('bkid'));\n\tsj=sj.split(',')[1]||'';\n\t(sj==''||sj=='null')?'':'\\u200b\\n当前阅读进度:'+sj;\n\t}\nelse {''}\n<\/js>",
        "kind": "<js>\nlet out=[];\ntry {\n  if (baseUrl.includes('search.php?')) {\n    out = java.getStringList(String(source.ruleSearch.kind)) || [];\n  } else if (baseUrl.includes('\/bookcase.php')) {\n    out = java.getStringList('td.4@text') || [];\n  } else {\n    \/\/★手机版:卡片 p.gray 第一行是「分类 | 作者」\n    let j = Packages.org.jsoup.Jsoup.parse(String(result));\n    let cards = j.select('#content div.c_row.cf');\n    let idx = -1;\n    \/\/找出当前 result 是第几张卡:result 是单个 Element 时直接用它\n    let self = (typeof result.select=='function') ? result : null;\n    let p = self ? self.select('p.gray').first() : null;\n    if (p == null) p = j.select('p.gray').first();\n    if (p != null) {\n      let h = String(p.html());\n      let seg = h.split(\/<br[ ]*[\/]?>\/i);\n      let l0 = String(seg[0]||'').replace(\/<[^>]+>\/g,'').replace(\/[ ]+\/g,' ').trim();\n      let parts = l0.split('|');\n      let k = String(parts[0]||'').trim();\n      if (k) out = [k];\n    }\n    if (out.length == 0) out = java.getStringList('td[4,5]@text||span:contains(更新:)+span@text') || [];\n  }\n} catch(e) {}\nout\n<\/js>",
        "lastChapter": "<js>\nif (baseUrl.includes('search.php?') ) {\tjava.getString(String(source.ruleSearch.lastChapter));}\nelse if (baseUrl.includes('\/bookcase.php') ) {\ngets('td.2@a.-1@text||h4+.gray:containsOwn(最新:)>a@text');}\nelse if (\/http.{4}www\/.test(this.getsurl())){\ngets('td.1@a.-1@text||class.c_subject@a.-1@text');}\nelse {}\n<\/js>",
        "name": "<js>\nlet rs='';\nif (baseUrl.includes('search.php?') ) {\nresult=String(java.getString('.c_subject@text||h4@text')).replace(\/\\\"\/g,'');\n\/\/获取id目的\nrs = java.getString('a.0@href');\n}\nelse if (baseUrl.includes('\/bookcase.php') ) {\n\tresult =  java.getString('td.1@a.-1@text||h4@a@text');\n\trs = java.getString('td.1@a.-1@href||h4@a@href');\n\t\/\/我的书架需要转化链接\n\trs=getsurl()+'\/book\/'+rs.match(\/aid=(\\d+)\/)[1]+'.html';\n}\nelse {\n\t\tresult =  gets('td.0@a.-1@text||class.c_subject@a.1@text||h4@text')||gets('td.1@a.-1@ownText');\n\trs = java.getString('td.0@a.-1@href||class.c_subject@a.1@href||class.db cf@href||td.1@a.-1@href');\n}\n\n\/\/获取id,黑名单时书名为空\nlet id = (rs.match(\/book\\\/(\\d+)\/)||['',''])[1];\njava.put('bkid',id);\nlet i = hmdjib(id,result);\nif (i) result='';\nelse book.bookUrl=`data:;base64,${java.base64Encode(rs)},{\"type\":\"\"}`;\nresult\n<\/js>",
        "wordCount": "<js>\nif (\/articlelist|sort\\\/\/.test(baseUrl)) {\n  java.getString('span:contains(字数:)+span@text');\n}\nelse if (\/articlefilter\/.test(baseUrl)) {\n\tif (\/http.{4}www\/.test(this.getsurl()))\n\tjava.getString('td.3@text');\n\telse java.getString('td.2@text');\n}\nelse if (baseUrl.includes('search.php?') ) {\tjava.getString(String(source.ruleSearch.wordCount));\n\t}\n<\/js>"
    },
    "ruleSearch": {
        "author": "<js>\nlet r='';\ntry {\n  let self = (typeof result.select=='function') ? result : null;\n  let p = self ? self.select('p.gray').first() : null;\n  if (p != null) {\n    let h = String(p.html());\n    let seg = h.split(\/<br[ ]*[\/]?>\/i);\n    let l0 = String(seg[0]||'').replace(\/<[^>]+>\/g,'').replace(\/[ ]+\/g,' ').trim();\n    r = String(l0.split('|')[0]||'').trim();\n  }\n  if (!r) r = String(java.getString('span:contains(作者:)+span@text')||'');\n} catch(e) {}\nr\n<\/js>",
        "bookList": "<js>\nlet r1 = java.getString('#content .blockcontent@text');\nif (r1.includes(\"错误原因\")) {java.longToast(r1);result=[];}\nelse {\n\tresult = java.getElement('#jieqi_page_contents>div');\n}\nresult\n<\/js>",
        "bookUrl": "@js:book.bookUrl",
        "checkKeyWord": "https:\/\/8xbook.org\/book\/22531.html",
        "coverUrl": "img.0@src\n<js>\nlet jhf=source.get('8xjhf');\nresult=gethdi(result);\nif (jhf=='1') result=jinghuafm();\nresult\n<\/js>",
        "intro": "<js>\nlet r='';\ntry {\n  let self = (typeof result.select=='function') ? result : null;\n  let p = self ? self.select('p.gray').first() : null;\n  if (p != null) {\n    let h = String(p.html());\n    let seg = h.split(\/<br[ ]*[\/]?>\/i);\n    if (seg.length > 1) {\n      r = String(seg.slice(1).join(' ')).replace(\/<[^>]+>\/g,'').replace(\/\\s+\/g,' ').trim();\n    }\n  }\n  if (!r) r = String(java.getString('.c_description@text')||'');\n  r = String(r).replace('「','“').replace('」','”');\n} catch(e) {}\nr\n<\/js>",
        "kind": "<js>\nlet out=[];\ntry {\n  let self = (typeof result.select=='function') ? result : null;\n  let p = self ? self.select('p.gray').first() : null;\n  let k = '';\n  if (p != null) {\n    let h = String(p.html());\n    let seg = h.split(\/<br[ ]*[\/]?>\/i);\n    let l0 = String(seg[0]||'').replace(\/<[^>]+>\/g,'').replace(\/[ ]+\/g,' ').trim();\n    let parts = l0.split('|');\n    k = String(parts[1]||'').trim();\n  }\n  if (!k) k = String(java.getString('span:contains(关键字:)+span@text')||'');\n  k = k.replace(\/[\\[\\]#,]\/g,'').replace(\/^\\s+|\\s+$\/g,'');\n  if (k) out = k.split(\/\\s+\/);\n} catch(e) {}\nout\n<\/js>",
        "lastChapter": "span:contains(最新章节:)+span@text",
        "name": ".c_subject@text||h4@text\n<js>\nlet rs=java.getString('a.0@href');\n\/\/获取id,黑名单时书名为空\nlet id = (rs.match(\/book\\\/(\\d+)\/)||['',''])[1];\nlet i = hmdjib(id,result);\nif (i) result='';\nelse book.bookUrl=`data:;base64,${java.base64Encode(rs)},{\"type\":\"\"}`;\nresult\n<\/js>",
        "wordCount": "span:contains(字数:)+span@text"
    },
    "ruleToc": {
        "chapterList": "<js>\nconst startTime3=new Date().getTime();\nconst dyy=baseUrl.endsWith('\"ml\"}');\nlet tim0 = Math.floor(Math.random()*51);\nPackages.java.lang.Thread.sleep(tim0);\nlet nn=getsurl();\nlet sj=java.get('sj');\nif (sj=='') java.put('sj',1); \/\/书架标识\nlet ww = java.get('ww'),\nsx1 = java.get('sx1'); \/\/ww是否有新章,sx刷新标识从目录来(已在书架才会触发)\njava.put('sx1',0); \/\/还原详情到目录标识\nlet sj2=source.get('8xsj'); \/\/启用同步的标识\nlet bkid=java.get('bkid');\nlet tt = java.hexDecodeToString(result);\ntt=String(tt).replace(\/https\\:\\\/{2}[A-z\\d\\.]+\/,'');\nif (sj!='3'&&sx1!='1'&&sj2=='1'&&bkid!=''&&dyy) {tbshujia(nn,bkid)&&java.put('sj',3);}\n\/\/不为3未加书架,不为1不过详情,为1开启功能,且参数不为空\nlet sjj=source.get('sj'+bkid)+'';\nif (sjj&&book.durChapterIndex==0) {\n\t\/\/反向同步进度\n\tlet num=sjj.split(',')[2];\n\tnum>0&&(book.durChapterIndex=num);}\nelse if (java.get('sj')==3&&!sjj) {\n\t\/\/标记进源储存\nsource.put('sj'+bkid,book.author+','+book.durChapterTitle+','+book.durChapterIndex);}\n\nfunction mlurl(){\n\tlet tim=(Math.random()*1001|0)+(dyy?500:0);\n\tPackages.java.lang.Thread.sleep(tim);\nlet option={};\noption[\"headers\"]=gethd();\n\tconst startTime=new Date().getTime();\n\tresult = java.ajax(nn+tt+','+JSON.stringify(option))+'';\n\tconst endTime=new Date().getTime();\n\tlet speed = (endTime - startTime)\/1000;\n\tspeed>39&&java.toast(book.name+'\\n目录页访问延迟过长:'+speed.toFixed()+'秒');\n\tif (\/^java\\.net|^error\/.test(result))\n\tjava.toast(book.name+'目录链接访问出错'+result),java.log('目录访问出错'+result);\n\telse {\n\t\tresult=java.getString('.main@html',result)+'';\n\t\tlet tim1 = Math.floor(Math.random()*13);\n\t\tlet tim11 = Math.floor(Math.random()*5);\n\t\tif (result.length<1339)\n\t\tjava.toast(book.name+'目录内容出错'),java.log('目录内容出错:'+result);\n\t\telse if (result.includes(zx)||zx=='')\n\t\tcache.put(`v_${source.getKey()}_${nn+tt}`,result,60*(30+tim11)*(1+tim1));\n\t\telse\n\t\tcache.put(`v_${source.getKey()}_${nn+tt}`,result,3600*(20+tim11)*(10+tim1));}\n}\nfunction delay(dyt){\n\t\t let baxtime = cache.getFromMemory('baxtime');\n\t if (baxtime<Date.now()) baxtime=Date.now();\n\t baxtime+=dyt;\n\tcache.putMemory('baxtime',baxtime);\n\twhile(Date.now()<baxtime) {\n\tPackages.java.lang.Thread.sleep(1000);}\n\t}\n\nresult=cache.get(`v_${source.getKey()}_${nn+tt}`)+'';\nlet zx='';\nif (result.length>399) {\n\tif (ww==1) {\n\tlet wwz=java.getString('.pages a:contains(下一页)@href',result)+'';\n\tif (wwz.length<3) {\n\tdelay(3000);\n\tmlurl();\n\tjava.put('ww',0);} }\n\n} else {\nlet xqy = cache.getFromMemory('baxxq'+bkid);\nif (!xqy) {\ndelay(3000);\nlet option={};\noption[\"headers\"]=gethd();\nlet xq = String(book.bookUrl).replace(\/.+,(.+),.+\/,'$1');\nxq = java.base64Decode(xq);\nxq=String(xq).replace(\/http.{4}[A-z\\d\\.]+\/,'');\n\tconst startTime2=new Date().getTime();\nxqy=java.ajax(nn+xq+','+JSON.stringify(option))+'';\n\tconst endTime2=new Date().getTime();\n\tlet speed2 = (endTime2 - startTime2)\/1000;\n\tif (speed2>39) {java.toast(book.name+'\\n详情页访问延迟过长:'+speed2.toFixed()+'秒');}\t\n}\nif (!\/^java\\.net|^error\/.test(xqy))\nzx=gete(\".divbox h3 a||span:contains(最新:)+a\",xqy).attr(\"href\").replaceAll(\"http.{4}[^\\\\\/]+\",'')+''||java.get('8xzx');\n\nif (sx1!='1') {\n\t\/\/从目录\n\tdelay(3000);\n\t}\nmlurl();\n}\njava.setContent(result);\nresult=java.getElement('.index dd, .index dt');\nresult=Array.from(result).map(x=>{\n\tlet a=x.select(\"a,dt\").first();\n\tlet u=java.base64Encode(a.attr(\"href\").replaceAll('^http.{4}[^\\\\\/]+',''));\n\treturn {\n\t\"n\":(a.ownText()+'').replace(\/\\([01]\\d-[0123]\\d\\)$\/,''),\n\t\"u\":`data:;base64,${u},{\"type\":\"zw\"}`,\n\t\"i\":(a.attr(\"title\")+'').replace(\/\\d{1,2}\\:\\d{1,2}更新,共(\\d+字).*\/,' $1')+(x.select(\"em\")+''?\" 💎\":\"\"),\n\t\"v\":x.select(\"dt\")};\n});\nconst endTime3=new Date().getTime();\nsource.get('bx调试模式')==1&&java.toast(nn+tt+\"时间\"+(endTime3-startTime3)\/1000);\nresult\n<\/js>",
        "chapterName": "n",
        "chapterUrl": "u",
        "nextTocUrl": ".pages a:matchesWholeOwnText(下一页|>)@href\n<js>\nif (result[0]&&result[0].length()>9) `data:;base64,${java.base64Encode(result[0])},{\"type\":\"page\"}`;\nelse [];\n<\/js>",
        "updateTime": "i"
    },
    "searchUrl": "<js>\nlet nn = getsurl();\nlet url = `${nn}\/modules\/article\/search.php`;\nif (key.startsWith('书名:'))\nurl += `?searchkey=${key.substr(3)}&searchtype=articlename&page=${page}`;\nelse if (key.startsWith('作者:'))\nurl += `?searchkey=${key.substr(3)}&searchtype=author&page=${page}`;\nelse if (key.startsWith('标签:'))\nurl += `?searchkey=${key.substr(3)}&searchtype=keywords&page=${page}`;\nelse\nurl +=\t`?searchkey=${key}&searchtype=all&page=${page}`;\nlet option={};\noption[\"headers\"]=gethd();\nurl+','+JSON.stringify(option);\n<\/js>",
    "variableComment": "\n♦书籍变量:\n  ①填写  🔒 并刷新,关闭本书评论显示\n  ②填写“目录”并刷新,强制刷新目录\n  ③填写分数并刷新,使用分数进行评分\n  ④填写其它并刷新,内容作为评论发送\n分数格式示意“1分”“2分”…“10分”\n\n㊟默认详情页检测到有更新内容时才会刷新目录\n\n!注意区分源变量和书籍变量\n",
    "weight": 0
}
广告