Greasy Fork

Greasy Fork is available in English.

起点小说解锁|VIP章节免费阅读|极速章节识别

可解锁起点小说VIP付费章节。基本还原付费效果,无需设置即可阅读。

当前为 2023-02-25 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         起点小说解锁|VIP章节免费阅读|极速章节识别
// @version      1.3.4
// @description  可解锁起点小说VIP付费章节。基本还原付费效果,无需设置即可阅读。
// @author       JiGuang
// @namespace    www.xyde.net.cn
// @homepageURL  http://www.xyde.net.cn
// @match        https://vipreader.qidian.com/chapter/*
// @match        *://book.zongheng.com/chapter/*/*.html
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
// @require https://cdn.staticfile.org/jquery/2.0.3/jquery.min.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @grant GM_registerMenuCommand
// @connect
// @license MIT
// ==/UserScript==
 
(function() {
  'use strict';
    //全局配置
     //获取cookie值
    var index = getCookie("choice");
    var times = getCookie("times");
    if(index==null){index=0}
    if(times==null){times=0}
    var config = {
        //配置版本号
        version:1,
        //支持的书源地址:
        //步骤1
        webSites :
        ["https://souxs.leeyegy.com/search.aspx?key=",
         "http://www.dushuge.com/gdsuisfguigsdugiusiuesu.php?ie=gbk&q=",
         "https://www.disixs.com/search.php?keyword=",
         "https://so.biqusoso.com/s2.php?ie=utf-8&siteid=qu-la.com&q=",
         "http://www.b5200.net/modules/article/search.php?searchkey=",
         "http://www.siluke.cc/search.html?name="
        ],
        //跳转网址:用于修正脚本读取章节地址自动把起点前缀拼接起来
        //步骤1
        webGo: ['https://quapp.shenbabao.com/book/','http://www.dushuge.com','https://www.disixs.com','https://www.qu-la.com','http://www.b5200.net/','http://www.siluke.cc'],
        //网页内容:F12查看页面元素 找到章节文字所在的标签id
        webContent:["",'#content','#content','#txt','#content','#content'],
        //书源描述
        webDesc:["参八宝","读书阁","58小说网","官术网","书趣阁","思路客"],
        //正在使用的书源
        webSiteIndex : index,
        //搜索前缀:
        //步骤2:查看书源网站搜索关键字后跳转地址 并替换
        webSearch : ["&page=1&siteid=app2",'','','','',''],
        //搜索方法 : 目前没有特别大的作用
        webMethod :["GET","GET","GET","POST","POST","GET"],
        //使用序列: 不同书源的获取章节目录的标签选择不同
        //步骤5:0 代表第一个字符串
        webReturn:[0,2,0,1,0,0],
        //书源类型:0代表网页书源,1代表api请求书源
        webType:[1,0,0,0,0,0],
        //具体章节网址替换
        webHref:[0,0,0,0,1,0],
        //book:不同书源的获取作品名的标签选择不同
        //步骤3:去书源网站搜索页面查找标签并替换
        webBook:["",
                 "h4.bookname > a",
                 "a.result-game-item-title-link",
                 "a",
                 ".odd > a",
                 ".s2 > a"],
          //author
        //步骤3:去书源网站搜索页面查找标签并替换
        webAuthor:["",
                   "div.author",
                   "div.result-game-item-info > p:nth-child(1) > span:nth-child(2)",
                   "span.s4",
                   ".odd",
                   ".s4 > a"]
    }
    //注册的菜单和对应执行的函数
    var menus = [
    {
        name:'打开设置',
        event:openSetting
    },
    ]
 
    //增加cookie缓存
     function setCookie(cName,value,datetime){
    var oDate = new Date();
    if(datetime==0){datetime=1* 24 * 60 * 60 * 1000}
    oDate.setTime(oDate.getTime() + datetime);//设置过期时间
    var cookieString =cName + value + ";expires='" + oDate.toGMTString() + ";path=/";
    document.cookie = cookieString;//存cookie
    }
 
    //获取指定名称的cookie的值
    function getCookie(cName){
        var arrStr = document.cookie.split("; ");
        for (var i = 0; i < arrStr.length; i++) {
            var temp = arrStr[i].split("=");
            if (temp[0] == cName){
                return decodeURI(temp[1]);
            }
        }
    }
 
    //增加菜单
    function addMenu(){
        for(var menu of menus){
        GM_registerMenuCommand(menu.name, menu.event)
    }
    }
 
    //添加新书源
    function openSetting(){
        try{
            document.querySelector("#j_navSettingBtn > a").click()
        }catch(e){
            notify('打开设置失败','warning')
        }
    }
 
 
    //把更换书源增加到设置菜单
    function hookSetting(){
        let bookhtml = ``
        for(var di in config.webDesc){
          bookhtml += `<option value="${di}">${config.webDesc[di]}</option>`
        }
        if(!document.querySelector(".setting-list-wrap")){
            setTimeout(hookSetting,1000)
            return
        }
        let e = document.createElement("div")
        e.innerHTML = `<li class="remind" style="margin-top:10px;">
<i>书源切换</i>
<select id="select" style="position:relative;top:5px">
<option value="#">请选择要切换的书源</option>
${bookhtml}
</select>
</li>`
        document.querySelector(".setting-list-wrap").firstElementChild.appendChild(e)
        document.querySelector("#select").onchange = function(){
            var index=document.querySelector("#select").value
            setCookie("choice=",index,0)
            location.reload()
        }
        //打开评论
         document.querySelector("#j-sectionCommentBtn").onclick =function(){
             var state = document.querySelector("#j-sectionCommentSwitch").innerHTML
             if(state=="关闭"){
                 // $("body").addClass("section-comment-open")
                $("html").addClass=("j-sectionCommentLimit")
                $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
             }
             else{
               //  $("body").removeClass("section-comment-open")
                $("#j-readPage").removeClass("j-sectionCommentLimit")
                $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
                // $("#paragraph-review-app").css("display","none")
             }
         }
    }
    //自动加载本章说
   async function comment(){
      $("#j-readPage").removeClass("j-sectionCommentLimit")
      $("#j_chapterBox > div > div").removeClass("j-sectionCommentLimit")
    }
 
    //提示用户
    function notify(title = '操作成功',type = 'success',show = true){
        console.log(title)
        const Toast = Swal.mixin({
            toast: true,
            position: 'top-end',
            showConfirmButton: false,
            timer: 2000,
            timerProgressBar: true,
            didOpen: (toast) => {
                toast.addEventListener('mouseenter', Swal.stopTimer)
                toast.addEventListener('mouseleave', Swal.resumeTimer)
            }
        })
        if(show)
        Toast.fire({
            icon: type,
            title: title
        })
        return Toast
    }
 
    //获取章节名
    function QDgetBookChapter(){
      if(document.querySelector("div > div.text-head > h3 > span.content-wrap")){
        let res = '' + document.querySelector("div > div.text-head > h3 > span.content-wrap").innerText
        res = res.replace(' ','')
        return res
      }
      return undefined
    }
 
    //获取书本名
    function QDgetBookName(){
      return document.querySelector("#bookImg").innerText
    }
 
    //本章是否已被购买
    function QDgetChapterOrder() {
        // @ts-ignore
        return document.querySelector("a.admire.lang.j_admireBtn")
    }
 
    //设置页面阅读内容
 async  function QDsetContent(content){
   // console.log(content)
      const regs =/<br>[\s]{0,1}<br>/g
    //  console.log(regs.test(content))
      content=content.replace(regs, "<br><br>  ")
       let int = 1;
       while (true) {
       let key = `<span class="review-count" data-segid="${int++}" style=" cursor: pointer; "></span>`;
       content = content.replace("<br><br>", key);
       content = content.replace("</p><p>", key);
       if(content.indexOf("<br><br>") == "-1" && content.indexOf("</p><p>") == "-1") break;
       }
      var fir ='<p data-type="2">'
      content =fir +content
      var reg = RegExp("  ", "g");
      content = content.replace(reg, '</p><p data-type="2">');
      document.querySelector("div > div.read-content.j_readContent ").innerHTML = content
      let readQrcodeMobile = document.querySelector("#readQrcodeMobile")
      let cid = readQrcodeMobile.dataset.cid
     let bid = readQrcodeMobile.dataset.bid
     const res = await parseDocFromAjax("GET",`https://vipreader.qidian.com/ajax/chapterReview/reviewSummary?bookId=${bid}&&chapterId=${cid}`,true)
     console.log(res)
       res.list.map(item => {
            const span = document.querySelector(`span[data-segid="${item.segmentId}"]`)
            span ? span.innerHTML = item.reviewNum+"<i><cite></cite></i>" : ""
        })
      document.getElementsByClassName('read-content')[0].setAttribute('style',`line-height: 1.5;letter-spacing: 1px`)
    const removeSpan = document.querySelectorAll("span[data-segid")
    removeSpan.forEach(item => {
    if(item.innerText == '' || item.innerText == 0) item.remove()
    })
    console.log("移除成功")
}
 
    //将请求的url的html内容转化成document对象
    async function parseDocFromAjax(method,url,flag){
      return new Promise((resolve,reject) => {
          GM_xmlhttpRequest({
              method,
              url:url,
              onload:(res) => {
                  if(config.webType[config.webSiteIndex] == 1 || flag){
                   let str = res.response
               // console.log(str)
                    str=str.replace(/\\r\\n  \\r\\n  /g, "<br><br>  &nbsp;&nbsp;&nbsp;&nbsp;")
                    str=str.replace(/\\r\\n  /g, "<br><br>  &nbsp;&nbsp;&nbsp;&nbsp;")
                   let arr = eval('(' + str + ')')
                   const {data}=arr
                  console.log(data)
                   return resolve(data)
                  }
                  let htmldoc = document.createElement('html')
                  let htmlstr = res.responseText
                  htmlstr=htmlstr.replace(/http /g, "https")
                  htmlstr=htmlstr.replace(/img src/g, "a url")
                  htmlstr=htmlstr.replace(/onerror/g, "class")
              //  console.log(htmlstr)
                  htmldoc.innerHTML = htmlstr
                  console.log(url)
                  resolve(htmldoc)
              },
              onerror:(err) => {
                  reject(err)
              }
          })
      })
    }
 
 
 
    //搜索小说并返回结果
    async function searchBook(keywords){
      const r = await parseDocFromAjax(config.webMethod[config.webSiteIndex],config.webSites[config.webSiteIndex]+keywords +config.webSearch[config.webSiteIndex] )
      let resList = []
       if(config.webType[config.webSiteIndex] == 1){
           r.map(item =>{
                resList.push({id:item.Id,bookName:item.Name,author:item.Author,url:config.webGo[config.webSiteIndex] +item.Id+"/"})
               //console.log(item)
           })
           //console.log(resList[0])
           return resList
       }
      var bookList = r.querySelectorAll(config.webBook[config.webSiteIndex])
      const authorList = r.querySelectorAll(config.webAuthor[config.webSiteIndex])
      for(let i in bookList){
            if(bookList[i].title){
                resList.push({bookName:bookList[i].title,author:authorList[i].innerText,url:config.webGo[config.webSiteIndex] + bookList[i].pathname})
            }
            resList.push({bookName:bookList[i].innerText,author:authorList[i].innerText,url:config.webGo[config.webSiteIndex] + bookList[i].pathname})
      }
       // console.log(resList)
      return resList
    }
 
    //获取小说目录
    async function getChapterList(book){
      let resList = []
      let bookUrl = book.url.replace('https://vipreader.qidian.com/',config.webGo[config.webSiteIndex])
      const r = await parseDocFromAjax('GET',bookUrl)
        if(config.webType[config.webSiteIndex] == 1){
           // console.log(r)
             r.list.map(item => {
                 item.list.map(i => {
               resList.push({title:i.name,url:config.webGo[config.webSiteIndex]+book.id+"/"+i.id+".html"})
            })
        })
           // console.log(resList);
            return resList
        }
      let s=["#list > dl > dd > a","ul.cf > li > a","div.listmain > dl > dd > a"]
      //步骤4:如书源目录标签不相同 此处添加后再在webReturn修改对应数字
      const cateList = r.querySelectorAll(s[config.webReturn[config.webSiteIndex]])
      console.log("cateList:",cateList)
      for(let i of cateList){
       // console.log( i)
        let url = i.getAttribute("href")
        if(config.webHref[config.webSiteIndex] == 1){
           // console.log("Ok")
           //  bookUrl = bookUrl.substring(0, bookUrl.lastIndexOf("/")+1)
            config.webGo[config.webSiteIndex] = ''
           }
        url =config.webGo[config.webSiteIndex] +url
        resList.push({title:i.innerText,url:url})
      }
      return resList
    }
 
    //获取章节内容
    async function getContent(pageUrl){
      const res = await parseDocFromAjax('GET',pageUrl)
      if(config.webType[config.webSiteIndex] == 1){
          let title = res.cname.replace(" ",'' )
          if(res.content.indexOf(title) == -1) return res.content
          title = title +'<br><br>  '
         res.content= res.content.replace(title,'')
        //console.log('getContent:',res.content)
          return res.content
      }
      return res.querySelector(config.webContent[config.webSiteIndex]).innerHTML
    }
 
 
    //解析书源函数
    async function parseMain(){
 
      //搜索小说名字
      var r = await searchBook(QDgetBookName())
      var a = g_data.bookInfo.authorName
      let ii = 0
      //优先匹配名字相同的
      for(let suoyin in r){
        if(r[suoyin].bookName == QDgetBookName()||r[suoyin].author==a){
          ii = suoyin
            break;
        }
      }
      if(r[ii] == undefined){
          console.log("搜索作者")
        r = await searchBook(a)
        for(let suoyin in r){
          if(r[suoyin].bookName == QDgetBookName()){
                 ii = suoyin
                 break;
              }
          }
      }
      //获取第一项结果章节目录
      if(r[ii] == undefined){
        console.log('该小说暂无资源')
      }
     //  console.log(r[ii])
      const clist = await getChapterList(r[ii])
      if(QDgetBookChapter() == undefined || clist.length == 0){
        console.log('抓取目录失败')
      }
      console.log('抓取目录成功')
    // console.log(clist)
      //获取章节名
      for(let i in clist){
        let tit = '' + clist[i].title
        let str = tit
        tit = tit.replace(' ','')
        //console.log('匹配',tit,QDgetBookChapter())
          var patt1 =/[a-zA-Z\u4e00-\u9fa5]+/g
          var patt2 =/[0-9]+/g
          str = QDgetBookChapter()
          var flag=false
          //排除纯数字章节的影响
          if(tit.match(patt1)==null){
              tit = tit.match(patt2)
              str = str.match(patt2)
          }
        else if(str.match(patt1)==null){
              str = str.match(patt2)
              tit = tit.match(patt2)==null?tit.match(patt1):tit.match(patt2)
          }
          else{
              str = str.match(patt1)
              tit = tit.match(patt1)
              //有些作者喜欢加第几卷第几章 但是书源网站没有卷名
              var str2 =str.join("").split(/卷|章/)
              var tit2 =tit.join("").split(/卷|章/)
              //模糊读取,若无法精准匹配 尝试模糊名匹配 并设置缓存默认以此方法匹配,默认是2分钟
              console.log(times)
              if(times>=4&times<11){
                  //自带数字章节名 首个字符串与书源匹配
                  if(str[0]==tit[0]){
                    flag=true
                    setCookie("times=",times,1*1000*60*2)//这里修改2可以改缓存时间
                  }
              }
             else if(times>=11&times<17){
                 //末尾名匹配
                   if(str2[str2.length-1]==tit2[tit2.length-1]){
                       flag=true
                       setCookie("times=",times,1*1000*60*2)
                   }
                 }
             else if(times>=17){
                 //中间名匹配
                if(str2[str2.length-2]==tit2[tit2.length-2]){
                    flag=true
                    setCookie("times=",times,1*1000*60*2)
                  }
              }
          }
       //  console.log(str[0],tit[0])
        if(str.join("")==tit.join("")||flag==true){
          console.log('检查到结果')
          const content = await getContent(clist[i].url)
          QDsetContent(content)
          console.log('写入成功')
          notify('小说读取成功')
          return
        }
      }
        times++
         setCookie("times=",times,1*1000*60*2)
         console.log('目录匹配失败')
      notify('未查询到该小说内容','warning')
      throw new Error('该书源解析失败')
    }
 
    //递归更换书源
    async function mergeOne(index){
            try{
                if(index){
                    config.webSiteIndex = index
                    console.log(index)
                }
                notify(`正在切换到书源${config.webDesc[config.webSiteIndex]}...`,'info')
                await parseMain()
            }catch(e){
                console.log(e)
                config.webSiteIndex = (config.webSiteIndex + 1) % 6
                mergeOne()
            }
        }
    //MAIN-BEFORE 主程序预备函数
    if(QDgetChapterOrder()!=null){
       notify(`已订阅章节`)
    }else{
     addMenu()
    //MAIN 主程序
    notify(`您正在阅读${QDgetBookName()}的${QDgetBookChapter()}`)
    mergeOne()
    comment()
    hookSetting()
    }

        // Your code here...
})();