®️爱丽丝书屋

爱丽丝书屋

yezechuandl (3004)1天前

2026.5.24 修复诸多问题 ◎夜泽川◎
2024.7.26 ◎夜泽川◎
//搜索和发现需要封面可复制规则到相应位置,因为封面是从详情取的,所以可能会导致加载变慢甚至超时,自行斟酌。↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
a@href

ul = GetUL() + result;
java.ajax(ul);

[property$=image]@content
二维码导入
{
    "bookSourceComment": "2026.5.24 修复诸多问题 ◎夜泽川◎\n2024.7.26 ◎夜泽川◎\n\/\/搜索和发现需要封面可复制规则到相应位置,因为封面是从详情取的,所以可能会导致加载变慢甚至超时,自行斟酌。↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓\na@href\n<js>\nul = GetUL() + result;\njava.ajax(ul);\n<\/js>\n[property$=image]@content",
    "bookSourceGroup": "自制,零",
    "bookSourceName": "®️爱丽丝书屋",
    "bookSourceType": 0,
    "bookSourceUrl": "爱丽丝书屋",
    "bookUrlPattern": "https?:\\\/\\\/.*?\\..*?(xnxnxn|alice|爱丽丝|als).*?\\.[a-zA-Z]+?\\\/novel\\\/\\d+\\.html",
    "customButton": false,
    "customOrder": 188,
    "enabled": true,
    "enabledCookieJar": true,
    "enabledExplore": true,
    "eventListener": false,
    "exploreUrl": "@js:\neval(String(source.loginUrl));\nlet Cookie = cookie.getCookie(GetUL());\nif (!\/lf_user_auth\/.test(Cookie) && source.getLoginHeader()) {\n    let ck = source.getLoginHeaderMap()['Cookie']\n    cookie.setCookie(GetUL(), ck);\n    java.toast('登录状态掉了,已重新登陆');\n}\nvar result = [];\nvar headers = JSON.stringify({\"headers\":{\"User-Agent\": \"Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/121.0.6167.171 Safari\/537.36\"}});\nvar push = (title, url, size) =>\n    result.push({\n        title: title,\n        url: url ? `{{GetUL()}}${url},` + headers : url,\n        style: {\n            layout_flexGrow: 1,\n            layout_flexBasisPercent: size\n        }\n    });\npush(`当前站源:${GetUL()}`, null, 1)\nif (source.getLoginHeader()) {\n    let jc = source.getLoginHeaderMap();\n    let inf = decodeURIComponent(jc.get(\"Cookie\"));\n    let info = decodeURIComponent(inf).match(\/\\{[\\s\\S]+\\}\/)[0];\n    let username = JSON.parse(info)['username'];\n    let expe = Ajax(`${GetUL()}\/user\/index\/index.html`);\n    let ex = org.jsoup.Jsoup.parse(expe).select('.group , .user-data li');\n    push(`用户◎${username}◎中心`, null, 1);\n    push(\"书架\", \"\/user\/bookshelf?page={{page}}\", 0.45);\n    push(\"阅读记录\", \"\/user\/recentread\/index.html\", 0.45);\n    ex.forEach(xp => {\n        nam = String(xp.text()).replace(\/[^\\u4e00-\\u9fa5a-zA-Z0-9]\/g, \"\");\n        title = \/\\d\/.test(nam) ? nam.replace(\/(\\d+)([^\\d]+)\/, \"$2:$1\") : nam.replace(\/^\/, \"用户等级:\");\n        push(title, null, 0.45);\n    });\n} else {\n    push(\"未登录\", null, 1);\n}\npush(\"--------书库--------\", null, 1);\npush(\"最近更新\", \"\/all{{Sort_()}}.html\", 0.45);\npush(\"原创\", \"\/original.html?page={{page}}\", 0.45);\npush(\"排行\", null, 1);\nvar src = Ajax(`${GetUL()}\/all\/order\/hits+desc.html`);\nvar ph = org.jsoup.Jsoup.parse(src).select('.paihang_ds a');\nph.forEach(ta => {\n    nam = ta.text();\n    ul = ta.attr('href')\n    push(nam, ul, 0.45);\n})\npush(\"分类\", null, 1);\nvar tag = org.jsoup.Jsoup.parse(src).select('.fenlei_ds a');\ntag.forEach(ta => {\n    nam = ta.text();\n    ul = String(ta.attr('href')).replace(\/$\/, \"?page={{page}}\").replace(\/lists\/, \"all\/id\").replace(\/\\.html\/, `{{Sort_()}}$&`);\n    push(nam, ul, 0.25);\n})\nif (source.getLoginInfoMap()?.[\"发现自定义标签🏷️(中 \/ 英逗号分隔)\"] != \"\") {\n\t   push(\"自定义标签🏷️\", null, 1);\n\t   let myTags = String(source.getLoginInfoMap()[\"发现自定义标签🏷️(中 \/ 英逗号分隔)\"]).split(\/[,,]\/);\n\t   myTags.forEach(tag => {\n\t   \tlet url = `\/search.html?q=${tag}&f=tag&sort={{Sort_('search')}}&p={{page}}&serialize=`;\n\t   \tpush(tag, url, 0.25);\n\t   \t});\n}\nJSON.stringify(result);",
    "header": "{\"User-Agent\": \"Mozilla\/5.0 (Android 11; Mobile; rv:147.0) Gecko\/147.0 Firefox\/147.0\"}",
    "jsLib": "\/\/构建验证参数\nfunction calculateChecksum(nonce, cookieString) {\n    var cpk = \"ge_ua_p\";\n    if (!cookieString || !nonce) {\n        return null;\n    }\n    \n    var cookieValue = \"\";\n    var cookieArray = cookieString.toString().split(\";\");\n    \n    for (var i = 0; i < cookieArray.length; i++) {\n        var cookie = cookieArray[i].trim();\n        var parts = cookie.split(\"=\");\n        if (parts.length === 2 && parts[0] === cpk) {\n            cookieValue = parts[1];\n            break;\n        }\n    }\n    \n    if (cookieValue.length === 0) {\n        return null;\n    }\n    \n    var sum = 12345;\n    for (var i = 0; i < cookieValue.length; i++) {\n        var char = cookieValue[i];\n        if (\/^[a-zA-Z0-9]$\/.test(char)) {\n            sum += cookieValue.charCodeAt(i) * (nonce + i);\n        }\n    }\n    \n    return {\n    \t\"body\": \"sum=\" + sum + \"&nonce=\" + nonce,\n    \t\"cookieValue\": cookieValue\n    \t};\n}\n\nfunction Check_(src, cookies = null, url) {\n    const { java } = this;\n    if (!\/Checking your browser\/.test(src)) return src;\n    java.toast('检测到验证,正在自动处理中……');\n    let nonce = parseInt(src.match(\/nonce\\s*=\\s*(\\d+)\/)[1]);\n    cookies = String(cookies);\n    \/\/java.log(`${cookies}|${nonce}`);\n\n    let data = calculateChecksum(nonce, cookies);\n\n    let headers = {\n        \"User-Agent\": \"Mozilla\/5.0 (Android 11; Mobile; rv:140.0) Gecko\/140.0 Firefox\/140.0\",\n        \"Content-Type\": \"application\/x-www-form-urlencoded\",\n        \"Referer\": String(this.GetUL() + \"\/\"),\n        \"X-Ge-Ua-Step\": \"prev\",\n        \"Cookie\": String(\"ge_ua_p=\" + data.cookieValue)\n    };\n    \n    let postResponse = java.post(this.GetUL(), data.body, headers);\n    \n    let success = JSON.parse(postResponse.body()).ok;\n    java.toast(success ? '验证通过' : '验证失败,请重试!');\n\n    if (!success) return src;\n    \n    return java.ajax(url);\n}\n\nfunction Sort_(t) {\n\t   const { source } = this\n    const sgvs = JSON.parse(source.getVariable())?.s || 0;\n    \n    if (!sgvs) return t === 'search' ? 'relevance' : '';\n    const sorts = ['update_time+desc', 'hits+desc', 'word+desc'];\n    const sort = sorts[sgvs - 1];\n    \n    if (t === 'search') {\n        return sort.replace('+', '_');\n    }\n    return '\/order\/' + sort;\n}\n\nfunction GetUL() {\n\tconst { source } = this;\n\tlet sourceul = JSON.parse(source.getVariable()).sourl;\n\treturn sourceul;\n}\n\nfunction timeov(times) {\n    const date = new Date(times);\n    const year = date.getFullYear();\n    const month = String(date.getMonth() + 1).padStart(2, '0');\n    const day = String(date.getDate()).padStart(2, '0');\n    const hours = String(date.getHours()).padStart(2, '0');\n    const minutes = String(date.getMinutes()).padStart(2, '0');\n    const seconds = String(date.getSeconds()).padStart(2, '0');\n    \n    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n}\n\ncodesvg = \"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIAogICAgIHZpZXdCb3g9IjAgMCA3MDAgMjAwIiAKICAgICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIgogICAgIHN0eWxlPSJ3aWR0aDogMTAwJTsgaGVpZ2h0OiAxMDAlOyI+CiAgPHJlY3Qgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0id2hpdGUiLz4KICAKICA8IS0tIOmhtumDqOagh+mimCAtLT4KICA8dGV4dCB4PSI1MCUiIHk9IjQwIiAKICAgICAgICBmb250LXNpemU9IjIwIiAKICAgICAgICBmb250LXdlaWdodD0iYm9sZCIgCiAgICAgICAgdGV4dC1hbmNob3I9Im1pZGRsZSIgCiAgICAgICAgZmlsbD0iIzAwMCI+6YCJ5oup5Y+R6YCB57G75Z6LPC90ZXh0PgogIAogIDwhLS0g6K+E6K665qih5Z2XIC0tPgogIDx0ZXh0IHg9IjI1JSIgeT0iOTAiIAogICAgICAgIGZvbnQtc2l6ZT0iMTYiIAogICAgICAgIGZvbnQtd2VpZ2h0PSJib2xkIiAKICAgICAgICB0ZXh0LWFuY2hvcj0ibWlkZGxlIiAKICAgICAgICBmaWxsPSIjMDAwIj7or4Torro8L3RleHQ+CiAgPHRleHQgeD0iMjUlIiB5PSIxMTAiIAogICAgICAgIGZvbnQtc2l6ZT0iMTQiIAogICAgICAgIGZvbnQtd2VpZ2h0PSJib2xkIiAKICAgICAgICB0ZXh0LWFuY2hvcj0ibWlkZGxlIiAKICAgICAgICBmaWxsPSIjMDAwIj4o5aGr5YaZIuivhOiuuiIpPC90ZXh0PgogIAogIDwhLS0g5Zue5aSN5qih5Z2XIC0tPgogIDx0ZXh0IHg9Ijc1JSIgeT0iOTAiIAogICAgICAgIGZvbnQtc2l6ZT0iMTYiIAogICAgICAgIGZvbnQtd2VpZ2h0PSJib2xkIiAKICAgICAgICB0ZXh0LWFuY2hvcj0ibWlkZGxlIiAKICAgICAgICBmaWxsPSIjMDAwIj7lm57lpI08L3RleHQ+CiAgPHRleHQgeD0iNzUlIiB5PSIxMTAiIAogICAgICAgIGZvbnQtc2l6ZT0iMTQiIAogICAgICAgIGZvbnQtd2VpZ2h0PSJib2xkIiAKICAgICAgICB0ZXh0LWFuY2hvcj0ibWlkZGxlIiAKICAgICAgICBmaWxsPSIjMDAwIj4o5aGr5YaZIkBpZCIpPC90ZXh0PgogIAogIDwhLS0g5bqV6YOo5L+h5oGvIC0tPgogIDx0ZXh0IHg9IjUwJSIgeT0iMTgwIiAKICAgICAgICBmb250LXNpemU9IjE0IiAKICAgICAgICBmb250LXdlaWdodD0iYm9sZCIgCiAgICAgICAgdGV4dC1hbmNob3I9Im1pZGRsZSIgCiAgICAgICAgZmlsbD0iIzAwMCI+5qW85bGCaWTnroDku4vmnIk8L3RleHQ+Cjwvc3ZnPg==\";\n\nfunction Ajax(url) {\n     const { java, cookie } = this;\n     let result = java.ajax(url);\n     result = this.Check_(result, cookie.getCookie(this.GetUL()), url);\n     return result;\n}\n\nconst SALT1 = \"B3wlP9Tzo$0RIdlvX&^sg30^0&feAox%\";\nconst SALT2 = \"Rs4qM7mGrQ6aTMr8HHvv3WikTcY&kW8R\";\n\nconst PRIVATE_KEY = [\n    \"MIIEogIBAAKCAQEAnOUiABBEw9zzOqivp4uJxTd3D5Givmwx2i+JLVdyj9iO2S1E\",\n    \"crWOaO5k6lD4fbL0MnMH+luJhO3ySm1xDZy22ruzvPHhd+Sh3nH56+hOcj1jfpBx\",\n    \"lDPlwyo2nDshY0VFr\/3fonFjepp5PP+eZKYt9YWtxrVMWOc0yNH6HuRA+zwUX28W\",\n    \"RlP\/4vMWi6vEYt0XLt+lTBGqyvwxPYJBYivIehGz4exC7K1bpvX8LJWVARkvEIuf\",\n    \"Y3sQHtC\/BTeYoEsipfZYafTgQHJ+KAOZSq\/CET0USeTt+Evfn6YcbWX577DrRyGt\",\n    \"siJjojMEG5TKdDQWmGKTQb4E2+EpTrQYaCcaowIDAQABAoIBAC8L9noWZshkxPre\",\n    \"Am43RYTB8Q3WGfsH7psCjhvukQfZZFxzWocbMiz8733j8d+ffeJy4\/2K3V3jDDiN\",\n    \"QM1YJOzKREdwMLAG+xL9EnhPHNbc2azmG2jZdxhi3CVVBdoCt7biZeEMJ0xobdqA\",\n    \"vDpqKnXpNAbV7qLqEcX2UQ5aW7H6BdCgGk9HRBKXs\/ll65NZmxORXLoAVg+w7Vzi\",\n    \"XaLP6+43KNXUPLz0EPndDH9VkGlMcyu6q7pWLoz6eN0fNiP4Jfl9PbV4KFlye2xo\",\n    \"4FI+Go8luM0onDL1+bKE5RJHXqfS+ow9hYzBJSz39jyNpiH7j8Hg8mMDPm0VIYtM\",\n    \"sOF\/RgECgYEAzuuziQzrT74ZW27AQqMFQFLvqMmnrhR4CPg0mRq\/PMHSzh+Bs+nS\",\n    \"Gib2d1ulkKIDHPOG9EWKXBOUvHOBmGro+sOS9fnfoJYeNhLmX5K1xcDJpsBOMdZv\",\n    \"euEit2i7yy+KAc26fP+SoCQEHm1mlgZG1vcfJlPDofqwRyBeKHPAkGMCgYEAwhvb\",\n    \"Fw3udE0hws92+9GYmjES8jNauBaP3hlu3lmxcnjlVqlkHbc9PkvddmCsSB\/5TUCH\",\n    \"7qJRgYLo+uov40zNNavXv8cTqWvDrJxTuDFn0OSjeIvqS9kXeVHjpBP6d4CCLAZM\",\n    \"b6owfM8JtBFx9ef9ll5mwBekZDrspEXOgoCQwMECgYBujaILvFpQ7alQn5ibQcxB\",\n    \"dM5VKQCs0oTbjflUP+UjCg+eT1kWDfxSOrT+SnnoD5eINVjKVAk7br7N\/QylqaE2\",\n    \"sZ1oTIu9mdckXu6064aw1HMo46AjooVHatgIlC2ZvpmGoytbM5VceEG3HA5uY4Yf\",\n    \"vkLnUGO6vFzIc7O6+zVMLwKBgFmIab0vkt6YOUtXUIWEvwPYQOnwoBaraX7Dcm0j\",\n    \"KAMqGnanuWMvgxM6ARO6MZ0vCloEuu5qdnfrfzVFUgNhCIKKGgD+fWY3K9FxZfhe\",\n    \"6Yjj\/Tb8Kn0DzJ0MFZk4Ed6PKvvNh\/I1qRnYkZw6M7t+X2y9bF2MSiplN4PqIv\/0\",\n    \"90\/BAoGAQXzOzA3q+vcA9mwKvwXrPiSscmZMekV6RBUxf1riRzTnds9uWSTKz8QM\",\n    \"LpEoNB3tKSB+4raK6xJGJ914b+jc\/B7ayHDksStOLeJLV6t5+bmoKjk6qBrUjTQX\",\n    \"y8x2rsHReaJw0SbZy+4x55nYTi\/0mdzomR7N27EzYtzM7iWk5w0=\",\n].join(\"\\n\");\n\nfunction getAuthHeaders(sourceId, chapterId, t) {\n    const { java } = this;\n    const signStr = SALT1 + t + sourceId + chapterId + SALT2;\n    const token = java.digestHex(signStr, \"SHA-256\");\n\n    return {\n        \"User-Agent\": \"Mozilla\/5.0 (Android 11; Mobile; rv:147.0) Gecko\/147.0 Firefox\/147.0\",\n        \"x-request-timestamp\": t,\n        \"x-request-token\": token,\n        \"X-Requested-With\": \"XMLHttpRequest\"\n    };\n}\n\nfunction decryptChapter(chapterData) {\n    const { java } = this;\n    const rsa = java.createAsymmetricCrypto(\"RSA\/ECB\/PKCS1Padding\");\n    rsa.setPrivateKey(java.base64DecodeToByteArray(PRIVATE_KEY));\n   \n    const AesKeyByte = java.base64DecodeToByteArray(rsa.decryptStr(chapterData.aes_key_encrypt, false));\n    \n    const aes = java.createSymmetricCrypto(\"AES\/CBC\/PKCS5Padding\", AesKeyByte, java.base64DecodeToByteArray(chapterData.iv));\n    \n    return aes.decryptStr(java.base64DecodeToByteArray(chapterData.content_encrypt));\n}\n\nfunction getContent(Data) {\n    const { java, source } = this;\n    const { source_id: sourceId, chapter_id:chapterId, t, sign} = Data;\n    const url = `${this.GetUL()}\/home\/chapter\/info?id=${sourceId}&key=${chapterId}&t=${t}&sign=${sign}`;\n\n    const headers = this.getAuthHeaders(sourceId, chapterId, t);\n    \/\/java.log(JSON.stringify(headers))\n    const response = java.get(url, headers);\n    \/\/java.log(response.body())\n    const json = JSON.parse(response.body());\n\n    if (json.code === 1) {\n        const chapter = json.data.chapter;\n        const content = this.decryptChapter(chapter);\n        return content;\n    } else {\n        return \"加载失败:\" + json.msg;\n    }\n}",
    "lastUpdateTime": "1781600310292",
    "loginCheckJs": "if (\/小说被禁用或已删除\/.test(result.body())) java.toast('小说被禁用或已删除!');\nlet sr = Check_(result.body(), result.headers().get('set-cookie'), result.url());\nresult = Packages.io.legado.app.help.http.StrResponse(result.url(), sr);\nresult;",
    "loginUi": "[   \n    {\n        \"name\": \"获取站源\",\n        \"type\": \"button\",\n        \"action\": \"Geturls()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    },\n    {\n        \"name\": \"站源1\",\n        \"type\": \"button\",\n        \"action\": \"Puturl(0)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.2\n        }\n    },\n    {\n        \"name\": \"站源2\",\n        \"type\": \"button\",\n        \"action\": \"Puturl(1)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.2\n        }\n    },\n    {\n        \"name\": \"站源3\",\n        \"type\": \"button\",\n        \"action\": \"Puturl(2)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.2\n        }\n    },\n    {\n        \"name\": \"站源4\",\n        \"type\": \"button\",\n        \"action\": \"Puturl(3)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.2\n        }\n    },\n    {\n        \"name\": \"用户名\/邮箱\",\n        \"type\": \"text\"\n    },\n    {\n        \"name\": \"密码\",\n        \"type\": \"password\"\n    },\n    {\n        \"name\": \"注册\",\n        \"type\": \"button\",\n        \"action\": \"reg()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"登录\",\n        \"type\": \"button\",\n        \"action\": \"Login()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"登出\",\n        \"type\": \"button\",\n        \"action\": \"out()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"信息\",\n        \"type\": \"button\",\n        \"action\": \"show()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"评论展示开 \/ 关\",\n        \"type\": \"button\",\n        \"action\": \"Comment()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.4\n        }\n    },\n    {\n        \"name\": \"点击切换分类排序方式\",\n        \"type\": \"button\",\n        \"action\": \"\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    },\n    {\n        \"name\": \"默认\",\n        \"type\": \"button\",\n        \"action\": \"Tgch(0)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.2\n        }\n    },\n    {\n        \"name\": \"时间\",\n        \"type\": \"button\",\n        \"action\": \"Tgch(1)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.2\n        }\n    },\n    {\n        \"name\": \"人气\",\n        \"type\": \"button\",\n        \"action\": \"Tgch(2)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.2\n        }\n    },\n    {\n        \"name\": \"字数\",\n        \"type\": \"button\",\n        \"action\": \"Tgch(3)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.2\n        }\n    },\n    {\n        \"name\": \"发现自定义标签🏷️(中 \/ 英逗号分隔)\",\n        \"type\": \"text\"\n    },\n    {\n        \"name\": \"◎↓网络书架↓◎\",\n        \"type\": \"button\",\n        \"action\": \"\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    },\n    {\n        \"name\": \"书籍ID\",\n        \"type\": \"text\"\n    },\n    {\n        \"name\": \"添加\",\n        \"type\": \"button\",\n        \"action\": \"AFBook(true)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"移除\",\n        \"type\": \"button\",\n        \"action\": \"AFBook(false)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"跳转\",\n        \"type\": \"button\",\n        \"action\": \"BookShelf()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"邀请码【⚠️生成一个需消耗50积分】\",\n        \"type\": \"button\",\n        \"action\": \"\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 1\n        }\n    },\n    {\n        \"name\": \"💡生成邀请码\",\n        \"type\": \"button\",\n        \"action\": \"InvitationCode(true)\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    },\n    {\n        \"name\": \"📜打印邀请码\",\n        \"type\": \"button\",\n        \"action\": \"InvitationCode()\",\n        \"style\": {\n            \"layout_flexGrow\": 1,\n            \"layout_flexBasisPercent\": 0.25\n        }\n    }\n]",
    "loginUrl": "var r = {\n\t   \"z\": 0,\n    \"s\": 0,\n    \"co\": 0,\n    \"urls\": [],\n    \"sourl\": \"https:\/\/www.alicesw.com\"\n};\n\ntry {\n    let v = JSON.parse(source.getVariable());\n    if (v === null && typeof v !== 'object' && Array.isArray(v)) {\n        source.setVariable(JSON.stringify(r, null, \"\\t\"));\n        java.toast('已初始化,使用默认站源');\n    }\n} catch (err) {\n    java.log(err.message);\n    source.setVariable(JSON.stringify(r, null, \"\\t\"));\n    java.toast('已初始化,使用默认站源');\n}\n\nvar sgv = JSON.parse(source.getVariable());\nvar sg = source.get(\"list\");\nvar ul = GetUL();\n\nfunction reg() {\n    java.startBrowser(`${ul}\/user\/user\/reg.html`, \"注册账号\");\n}\n\nfunction Comment() {\n    let sgvo = sgv.co;\n    sgv.co = sgv.co ? 0 : 1;\n    source.setVariable(JSON.stringify(sgv, null, \"\\t\"));\n    java.toast(`\\n评论区\\n【${[\"开启\", \"禁用\"][sgvo]} → ${[\"开启\", \"禁用\"][sgv.co]}】`);\n}\n\nfunction login() {}\n\nfunction Tgch(o) {\n    const sort = [\"默认\", \"更新时间\", \"人气\", \"字数\"];\n    const one_ = sgv.s;\n    const msg = (sgv.s = o, source.setVariable(JSON.stringify(sgv, null, \"\\t\")), `排序方式\\n【${sort[one_]}→${sort[o]}】`);\n    java.toast(\"\\n\" + msg);\n}\n\nfunction Geturls() {\n    sgv.urls.length = 0;\n    try {\n        const res = org.jsoup.Jsoup.parse(java.get('https:\/\/xn--vcsx64d.asw4.cc\/', {}).body()).select('.container a');\n        const url1 = java.base64Decode(res[0].attr('href').match(\/['\"](.*?)['\"]\/)[1]);\n        sgv.urls.push(String(url1).replace(\/\\\/?$\/, \"\"));\n        const url2 = java.base64Decode(res[1].attr('href').match(\/['\"](.*?)['\"]\/)[1]);\n        let src = java.get(java.connect(url2).url(), {}).body();\n        src = java.get(org.jsoup.Jsoup.parse(src).select('#redirectButton').attr('href'), {}).body();\n        const seen = new Set();\n        for (let match of src.matchAll(\/targetHref\\:\\s*[\"'](https?[^\"']+)\\\/?[\"']\/g)) {\n            if (!seen.has(match[1])) {\n                sgv.urls.push(match[1]);\n                seen.add(match[1]);\n            }\n        }\n        source.setVariable(JSON.stringify(sgv, null, \"\\t\"));\n\n        if (sgv.urls.length >= 2) {\n            java.toast(`获取成功,共获取到${sgv.urls.length}个站源\\n┎┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┒\\n${sgv.urls.join('\\n')}\\n┖┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┚`);\n        } else {\n            java.toast('获取失败,请重试或检查网络状况');\n        }\n    } catch (e) {\n        java.log(e);\n        java.toast('\\n未知错误,请检查网络或更新阅读版本!');\n    }\n}\n\nfunction Puturl(e) {\n    if (!sgv.urls.length) return java.toast('\\n当前没有站源,请获取后选择');\n    if (e + 1 > sgv.urls.length) return java.toast(`暂无站源${e+1}`);\n    \n    try {\n        const stime = Date.now();\n        java.get(sgv.urls[e], {});\n        const speedtime = (Date.now() - stime) \/ 1000;\n        \n        sgv.sourl = sgv.urls[e];\n        sgv?.booklist?.forEach(item => {\n            item.img = item.img.replace(\/https?:\\\/\\\/[^\\\/]+\/, sgv.urls[e]);\n            item.url = item.url.replace(\/https?:\\\/\\\/[^\\\/]+\/, sgv.urls[e]);\n        });\n        source.setVariable(JSON.stringify(sgv, null, \"\\t\"));\n        \n        \/\/ 切换接口保持登录状态\n        const Cookie = cookie.getCookie(sgv.urls[e]);\n        if (!\/lf_user_auth\/.test(Cookie) && source.getLoginHeader()) {\n            cookie.setCookie(sgv.urls[e], source.getLoginHeaderMap().get('Cookie'));\n        }\n        java.toast(`站源已设置为【站源${e + 1}】:\\n${sgv.urls[e]}\\n♦解析用时${speedtime}秒♦`);\n    } catch (err) {\n        java.log(err.message);\n        java.toast(`\\n【站源${e+1}:${sgv.urls[e]}】\\n已失效,请检查网络状况或切换其他站源`);\n    }\n}\n\nfunction Login() {\n    cookie.removeCookie(ul);\n    const na = result['用户名\/邮箱'];\n    const pa = result['密码'];\n    \n    if (!na || !pa) return;\n    \n    try {\n        const phpid = java.get(`${ul}\/user\/user\/verify.html`, {}).header(\"set-cookies\");\n        const code = java.getVerificationCode(`${ul}\/user\/user\/verify.html`);\n        const response = java.post(`${ul}\/user\/user\/login.html`, \n            `username=${na}&password=${pa}&code=${code}`, \n            { \"x-requested-with\": \"XMLHttpRequest\", \"Cookie\": phpid });\n        \n        const ck = cookie.mapToCookie(response.cookies());\n        const usercode = JSON.parse(response.body()).msg;\n        java.toast(usercode);\n        \n        if (ck) {\n            source.putLoginHeader(JSON.stringify({\"Cookie\": ck}));\n            cookie.replaceCookie(GetUL(), ck);\n        } else {\n            source.removeLoginHeader();\n            cookie.removeCookie(GetUL());\n        }\n    } catch (e) {\n        java.log(e);\n        java.toast('\\n登录出错,请切换其他站源重试!');\n    }\n}\n\nfunction out() {\n    source.removeLoginHeader();\n    cookie.removeCookie(GetUL());\n    java.toast(\"已退出登录状态\");\n}\n\nfunction show() {\n    const jc = source.getLoginHeaderMap();\n    let us, id, codes;\n    \n    if (jc) {\n        const info = decodeURIComponent(decodeURIComponent(jc.get(\"Cookie\")).match(\/\\{[\\s\\S]+\\}\/)[0]);\n        const userInfo = JSON.parse(info);\n        us = userInfo.username;\n        id = userInfo.uid;\n        codes = \"✔️已登录\";\n    } else {\n        us = id = \"无\";\n        codes = \"❌未登录\";\n    }\n    \n    java.longToast(`\\n◎用户:${us}◎\\n◎用户ID:${id}◎\\n◎登录状态:${codes}◎`);\n}\n\n\nfunction InvitationCode(c) {\n    if (!source.getLoginHeaderMap()) return java.toast(\"未登录,请登录后获取\");\n    \n    let text = \"\";\n    if (c) {\n        java.post(ul + \"\/user\/user\/createinvitecode.html\", \"create_num=1\", \n            {\"Cookie\": source.getLoginHeaderMap()[\"Cookie\"]});\n        text += \"\\n已消耗50积分生成一个邀请码\";\n    }\n    \n    const verify_list = org.jsoup.Jsoup.parse(java.ajax(ul + \"\/user\/user\/invitecode\")).select('.verify_list .item');\n    java.log(\"\\n\" + String(verify_list.html()).replace(\/<[^>]+?>\\s*|复制| (?!\\d)\/g, \"\"));\n    java.toast(text + \"\\n邀请码已打印至源日志\");\n}\n\nfunction AFBook(ta) {\n    const info = result['书籍ID'];\n    if (!info) return java.toast(\"\\n未填入书籍id,请进书籍详情页,或者手动填写。\");\n    if (!source.getLoginHeaderMap()) return java.toast(\"未登录,请登录后使用\");\n    \n    try {\n        let url;\n        if (ta) {\n            url = `${ul}\/user\/bookshelf\/add?novel_id=${info}`;\n        } else {\n            let src = \"\";\n            for (let page = 1;; page++) {\n                let sr = java.ajax(`${ul}\/user\/bookshelf\/index.html?page=${page}`);\n                if (page === 2) java.log(sr);\n                if (!\/书架空空如也\/.test(sr)) {\n                    src += sr + \"\\n\";\n                } else break;\n            }\n            \n            const booklist = src.match(\/\\\/novel\\\/\\d+\\.html\/g);\n            const dfid = src.match(\/\\\/user\\\/bookshelf\\\/del\\\/id[^\"']+\/g);\n            const index = booklist.findIndex(book => book.includes(info));\n            if (index === -1) return java.toast('未找到书籍');\n            url = GetUL() + dfid[index];\n        }\n        java.log(index + \"我的\")\n        java.log(url)\n        const msg = java.get(url, {\"x-requested-with\": \"XMLHttpRequest\"});\n        java.toast(JSON.parse(msg.body()).msg);\n    } catch (error) {\n    \t   java.log(error);\n        java.toast('未知错误,已打印至日志');\n    }\n}\n\nfunction BookShelf() {\n    if (source.getLoginHeaderMap()) {\n        java.startBrowser(`${ul}\/user\/bookshelf\/`, \"书架\");\n    } else {\n        java.toast(\"未登录,请登录后再使用此功能。\");\n    }\n}",
    "respondTime": 190259,
    "ruleBookInfo": {
        "author": "@get:{a}",
        "canReName": "1",
        "coverUrl": "@get:{i}\n@js:let url = \/^https?\/.test(result) ? result : GetUL() + result;\nresult = java.connect(url).code() == 200 ? url : 'https:\/\/img.321cdn.com\/img\/01.png';\nresult;",
        "init": "<js>\nvar bookid = baseUrl.match(\/novel\\\/(\\d+)\/)[1];\n\/\/书评发送\nconst bookva = book.getVariable(\"custom\");\nif (bookva && bookva != \"\") {\n    if (source.getLoginHeader()) {\n        const commenturl = `${GetUL()}\/home\/comment\/add.html`;\n        let body = `content=${bookva}&type=novel&mid=${bookid}`;\n        \n        try {\n            const code = String(java.getVerificationCode(`data:image\/svg+xml;base64,${codesvg}`));\n            \n            if (code == \"评论\" || \/^@\\d+$\/.test(code)) {\n                if (\/^@\\d+$\/.test(code)) {\n                    body += `&pid=${code.match(\/(\\d+)\/)[1]}`;\n                }\n                \n                const headers = { \"x-requested-with\": \"XMLHttpRequest\" };\n                const response = java.post(commenturl, body, headers).body();\n                java.toast(JSON.parse(response).msg);\n            } else {\n                Packages.java.lang.Thread.sleep(40);\n                java.toast('请按图片要求填写');\n            }\n        } catch (err) {\n            java.log(err);\n        }\n    } else {\n        java.toast(\"未登录,请登录后操作\");\n    }\n    book.putCustomVariable(\"\");\n}\n\ntry {\n    sourceinfo = source.getLoginInfoMap();\n    sourceinfo['书籍ID'] = bookid;\n    if (bookid) {\n        source.putLoginInfo(JSON.stringify(sourceinfo));\n    }\n    $ = _ => String(java.getString(_));\n    img = \/^https?\/.test($('[property$=image]@content')) ? $('[property$=image]@content') : GetUL() + $('[property$=image]@content');\n    list = {\n        \"ID\": bookid,\n        \"img\": img,\n        \"name\": $('[property$=book_name]@content'),\n        \"author\": $('[property$=author]@content'),\n        \"info\": $('[property$=description]@content').slice(0, 45),\n        \"url\": \"legado:\/\/import\/addToBookshelf?src=\" + baseUrl\n    }\n    source.put(\"list\", JSON.stringify(list));\n} catch (err) {\n    java.log(\"登录UI未初始化保存数据:\" + err);\n}\nsrc;\n<\/js>\n@put:{\"n\":\"[property$=book_name]@content\",\n\"a\":\"[property$=author]@content\",\n\"t\":\"class.fix@p.2:-2@text##.*[::]\",\n\"k\":\"class.tg@a@ownText##\\n##,\",\n\"i\":\"[property$=image]@content\",\n\"l\":\"[property$=latest_chapter_name]@content\",\n\"r\":\"class.fix@p.3@text##\\\\s·\\\\s##\\n📂\",\n\"T\":\"[property$=update_time]@content\",\n\"m\":\"text.查看全部章节@href\",\n\"z\":\"class.fix@p.4@text##.*:(.*)·##$1###\",\n\"o\":\"[property$=description]@content\"}",
        "intro": "&nbsp;&nbsp;📖书籍ID:{{baseUrl.match(\/novel\\\/(\\d+)\/)[1]}}\n☑当前站源:{{GetUL()}}\n🔥@get:{r}\n🏷️标签:@get:{k}\n🕰️更新时间:@get:{T}\n📜简介:\n@get:{o}\n<js>\nconst Bookid = baseUrl.match(\/novel\\\/(\\d+)\/)[1];\nconst contul = `${GetUL()}\/index.php?s=\/Home\/comment\/lists\/id\/${Bookid}\/type\/novel\/limit\/5\/size\/12`;\nvar CommentNur = JSON.parse(source.getVariable()).co;\nvar cont = \"\";\nif (!CommentNur) {\n    var src = java.ajax(contul);\n    java.setContent(src);\n    var list = java.getElement('\/\/script[@src=\"\/public\/static\/jquery\/jquery.min.js\"]\/following-sibling::script[1]');\n    var lists = JSON.parse(String(list[0]).match(\/var\\s*trre=([\\s\\S]+),page_num\/)[1]);\n    \/\/java.log(JSON.stringify(lists,null,\"\\t\"));\n\n    cont += \"🔻评论区:\\n❗️右上角书籍变量填入评论后刷新详情页发送评论。❗️\\n\";\n    if (lists.length > 0) {\n        if (source.getLoginHeader()) {\n            let jc = source.getLoginHeaderMap();\n            let inf = decodeURIComponent(jc.get(\"Cookie\"));\n            let info = decodeURIComponent(inf).match(\/\\{[\\s\\S]+\\}\/)[0];\n            let myuid = JSON.parse(info)['uid'];\n            \/\/处理我的评论数据\n            var myComments = [];\n\n            function collectMyComments(comment, parentPath) {\n                let floor = parentPath ? `${parentPath}-${comment.floorIndex}` : comment.floorIndex;\n                if (comment.uid === Number(myuid)) {\n                    myComments.push({\n                        id: comment.id,\n                        username: comment.user.username,\n                        group: comment.user.group,\n                        floor: `【${floor}楼】`,\n                        time: timeov(comment.create_time * 1000),\n                        content: comment.content\n                    });\n                }\n                if (comment._?.forEach) {\n                    comment._.forEach((child, index) => {\n                        child.floorIndex = index + 1;\n                        collectMyComments(child, floor);\n                    });\n                }\n            }\n            lists.forEach((c, i) => {\n                c.floorIndex = i + 1;\n                collectMyComments(c, \"\");\n            });\n            \/\/java.log(JSON.stringify(myComments));\n            var mySection = \"=== 我的评论(共找到\" + myComments.length + \"条) ===\\n\\n\";\n            myComments.forEach(m => {\n                mySection += `┌【ID:${m.id}】| 👤${m.username} | ${m.group} | ${m.time}\\n└评论:${m.content}\\n${m.floor}\\n `;\n            });\n        } else {\n            mySection = \"\";\n        }\n\n        \/\/处理全部评论数据\n        function buildComments(comment, parentPath, level) {\n            let floor = parentPath ? `${parentPath}-${comment.floorIndex}` : comment.floorIndex;\n            let indent = '  '.repeat(level);\n            let str = `${indent}${parentPath ? \"│\" : \"\"}┌【ID:${comment.id}】|👤${comment.user?.username || \"\"} | ${comment.user?.group || \"已注销用户\"} | ${timeov(comment.create_time * 1000)}\\n` +\n                `${indent}${parentPath ? \"│├\" : \"\"}评论:${comment.content}\\n` +\n                `└─${indent}【${floor}楼】\\n`;\n\n            if (comment._?.forEach) {\n                comment._.forEach((child, index) => {\n                    child.floorIndex = index + 1;\n                    str += buildComments(child, floor, level + 1);\n                });\n            }\n            return str;\n        }\n\n        var allSection = \"=== 全部评论(共\" + lists.length + \"条) ===\\n\\n\";\n        lists.forEach((c, i) => {\n            c.floorIndex = i + 1;\n            allSection += buildComments(c, \"\", 0);\n        });\n\n        \/\/合并评论数据\n        cont += mySection + \"\\n\" + allSection;\n    } else {\n        cont += \"本书暂时没有评论喔\";\n    }\n}\nresult = result + \"\\n\" + cont;\nresult;\n<\/js>",
        "kind": "@get:{t}\n@get:{k}",
        "lastChapter": "@get:{l}◎@get:{T}",
        "name": "@get:{n}",
        "tocUrl": "@get:{m}\n@js: GetUL() + result;",
        "wordCount": "@get:{z}"
    },
    "ruleContent": {
        "content": "@js:\nvar url = GetUL();\n\/\/处理强制登录\nvar nn = String(cookie.getCookie(url));\nvar coks = nn.split('; ');\nvar ckk = coks.filter(item => !item.startsWith(\"lf_reader_num\")).join('; ');\ncookie.setCookie(url, ckk);\nconst chapterul = java.hexDecodeToString(src);\n\nfunction content() {\n    let html = java.ajax(chapterul);\n    let content = String(java.getString('.content_txt@html', html));\n    return content.includes('正在获取小说信息') ? (eval('bookdata = ' + html.match(\/book\\.initial\\s*=\\s*({[\\s\\S]+?};)\/)[1]),getContent(bookdata)) : content;\n}\n\nconst text = content().trim();\n\nlet retryCount = 0;\nconst maxRetries = 3;\nlet result;\nwhile (retryCount < maxRetries) {\n    if (text != \"\" && !\/正在加载小说中,请稍后\/.test(text)) {\n        result = text;\n        break;\n    } else {\n        retryCount++;\n        java.toast(`\\n未获取到文本,正在重试...\\n(当前第${retryCount} \/ ${maxRetries}次重试)`);\n        text = content();\n    }\n}\nif (retryCount === maxRetries) {\n    result = \"重试三次仍未获取到文本,请检查网络状况。\";\n}\nresult;",
        "imageStyle": "DEFAULT",
        "replaceRegex": "##\\n*{{chapter.title}}\\n*"
    },
    "ruleExplore": {
        "author": ".four@text||.t4@text||a[href^=\"\/search\"]@text",
        "bookList": ".novel-item||.rec_rullist > ul||.list-group-item",
        "bookUrl": "a[href^=\"\/novel\/\"]@href\n@js: GetUL() + result;",
        "coverUrl": "img@src\n@js: \/^https?\/.test(result) ? result : GetUL() + result;",
        "intro": ".content-txt@text",
        "kind": "{{@@.sev@text%%.six@text}}\n{{@@.t6@text}}\n{{@@.t5@text}}\n{{@@.mb-1@text##浏览:(.*)##$1浏览###}}\n{{@@.badges@text}}\n{{@@.datainfobox@p.0:-1@text##.*?:}}",
        "lastChapter": ".datainfobox@p.-2@a@text||a[href^=\"\/book\/\"]@text",
        "name": "a[href^=\"\/novel\/\"]@text##^\\d+?\\.|全文阅读$",
        "wordCount": ".five@text||.mb-1@text||.datainfobox@p.2@ownText\n@js:\nif (\/search\/.test(baseUrl)) result = result.split(\/\\s|\\u3000\/)[1].replace(\/.*?:\/, \"\");\nresult;"
    },
    "ruleSearch": {
        "author": ".mb-1 a@text",
        "bookList": ".list-group-item",
        "bookUrl": "a@href\n@js: GetUL() + result;",
        "intro": ".content-txt@text",
        "kind": "{{@@.mb-1@text##浏览:(.*)##$1浏览###}}\n{{@@a[href$=\"tag\"]@text%%small@text##\\[|\\]}}\n###",
        "name": "h5 a@text##^\\d+\\.",
        "wordCount": ".mb-1@text\n@js:result.split(\/\\s|\\u3000\/)[1].replace(\/.*?:\/, \"\");"
    },
    "ruleToc": {
        "chapterList": "class.section-list@a",
        "chapterName": "text",
        "chapterUrl": "href\n<js>\nvar url = GetUL() + result;\nresult = `data:;base64,${java.base64Encode(url)},{\"type\":\"chapter\"}`;\nresult;\n<\/js>",
        "preUpdateJs": "eval(String(source.loginUrl));\nvar sourceUL = GetUL();\nif (!String(book.bookUrl).includes(sourceUL)) {\n  book.bookUrl = String(book.bookUrl).replace(\/https?:\\\/\\\/[^\\\/]+\/,sourceUL);\n  book.tocUrl = String(book.tocUrl).replace(\/https?:\\\/\\\/[^\\\/]+\/,sourceUL);\n  java.toast(\"\\n检测到切换源站,已更新目录链接。\");\n   java.refreshTocUrl();\n}"
    },
    "searchUrl": "{{eval(String(source.loginUrl));GetUL();}}\/search.html?q={{key}}&f=_all&sort={{Sort_('search')}}&p={{page}}&serialize=",
    "weight": 0
}
广告