// ==UserScript== // @name 万能验证码自动输入(升级版) // @namespace https://www.like996.icu:1205/ // @version 4.2 // @description 在将来的时间里将会在后台默默的为你自动识别页面是否存在验证码并填入。对于一些书写不规整的验证码页面请手动配置规则。感谢老六提供框架代码,感谢哈士奇提供思路支持。 // @author crab // @match http://*/* // @match https://*/* // @connect like996.icu // @connect * // @require http://libs.baidu.com/jquery/2.0.0/jquery.min.js // @require http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js // @resource cktools https://greasyfork.org/scripts/429720-cktools/code/CKTools.js?version=1034581 // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_openInTab // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_xmlhttpRequest // @grant GM_getResourceText // @nocompat Chrome // @downloadURL none // ==/UserScript== class CaptchaWrite { IdCard() { return Set["idCard"]; } getCaptchaServerUrl(){ return "https://www.like996.icu:1205/"; } constructor() { this.Tip = this.AddTip(); if (GM_listValues().indexOf("set") == -1) { GM_setValue("set", { "idCard": "" }); var WhetherHelp = confirm("万能验证码填入\n初始化完毕!\n在将来的时间里将会在后台默默的为你\n自动识别页面是否存在验证码并填入。\n对于一些书写不规整的验证码页面请手动添加规则。\n查看添加方法请点击确定。"); if (WhetherHelp == true) { GM_openInTab(this.getCaptchaServerUrl() + "help.html", 'active') } } Set = GM_getValue("set"); // 设置自动识别初始值 var configSetKeys={"autoIdentification":"true","showHintCheck":"true","warningTone":"true","autoBlackList":"false","hotKeyToImgResult":"false"}; $.each(configSetKeys, function(key,val){ if(Set[key] == undefined ){ Set[key]=val; GM_setValue("set", Set); } }); } //手动添加规则 PickUp() { var that = this; var AddRule = {}; var IdentifyResult = ''; that.Hint('请对验证码图片点击右键!'); $("canvas,img,input[type='image']").each(function() { $(this).on("contextmenu", function() { if(that.getCapFoowwLocalStorage("crabAddRuleLock")!=null){ return; } that.setCapFoowwLocalStorage("crabAddRuleLock","lock",new Date().getTime()+100);//100毫秒内只能1次 var img = that.Aimed($(this)); console.log('PickUp_Img:' + img); if($(img).length!=1){ that.Hint('验证码选择错误,该图片实际对应多个元素。') return; } that.Hint('等待识别') IdentifyResult = that.Identify(img,function ManualRule(img,IdentifyResult){ if (img && IdentifyResult) { console.log('记录信息' + img + IdentifyResult); AddRule['img'] = img; $("img").each(function() { $(this).off("click"); $(this).off("on"); $(this).off("load"); }); that.Hint('接下来请点击验证码输入框') $("input").each(function() { $(this).click(function() { var input = that.Aimed($(this)); // console.log('PickUp_input' + input); AddRule['input'] = input; AddRule['path'] = window.location.href; AddRule['title'] = document.title; AddRule['host'] = window.location.host; AddRule['idcard'] = that.IdCard(); that.Write(IdentifyResult,input); that.Hint('完成') //移除事件 $("input").each(function() { $(this).off("click"); }); //添加信息 that.Query({ "method": "captchaHostAdd", "data": AddRule },function(data){writeResultIntervals[writeResultIntervals.length]={"img":img,"input":input}}); that.delCapFoowwLocalStorage(window.location.host); }); }); } }); }); }); } //创建提示元素 AddTip() { var TipHtml = $("
").text("Text."); TipHtml.css({ "background-color": "rgba(211,211,211,0.86)", "align-items": "center", "justify-content": "center", "position": "fixed", "color": "black", "top": "-5em", "height": "2em", "margin": "0em", "padding": "0em", "font-size": "1.2em", "width": "100%", "left": "0", "right": "0", "text-align": "center", "z-index": "9999999999999", "padding-top": "3px", display: 'none' }); $("body").prepend(TipHtml); return TipHtml; } //展示提醒 Hint(Content, Duration) { if(Set["showHintCheck"]!="true"){ return; } var that = this; that.Tip.stop(true, false).animate({ top: '-5em' }, 300, function() { if(Set["warningTone"]=="true"){ Content += that.doWarningTone(Content) } Content += "X"; that.Tip.show(); that.Tip.html(Content); }); that.Tip.animate({ top: '0em' }, 500).animate({ top: '0em' }, Duration ? Duration : 3000).animate({ top: '-5em' }, 500, function() { that.Tip.hide(); }); return; } //查询规则 Query(Json,callback) { var that = this; var QueryRule = ''; var LocalStorageData=this.getCapFoowwLocalStorage(Json.method + "_" + Json.data.host); if(Json.method=='captchaHostAdd'){ that.delCapFoowwLocalStorage("captchaHostQuery_"+Json.data.host); LocalStorageData=null; } if(LocalStorageData!=null){ console.log("存在本地缓存的验证码识别规则直接使用。") if(callback!=null){ callback(LocalStorageData); return; }else{ return LocalStorageData; } } GM_xmlhttpRequest({ url: that.getCaptchaServerUrl() + Json.method, method: 'POST', headers: {'Content-Type': 'application/json; charset=utf-8','path' : window.location.href}, data: JSON.stringify(Json.data), responseType: "json", onload: obj => { var data=obj.response; if (data.info) { that.Hint(data.info); } QueryRule = data; that.setCapFoowwLocalStorage(Json.method + "_" + Json.data.host,data,new Date().getTime()+1000*60) if(callback!=null){ callback(QueryRule); } },onerror: err => { console.log(err) } }); return QueryRule; } //开始识别 Start() { //检查配置中是否有此网站 var that = this; var Pathname = window.location.href; var Card = that.IdCard(); if(Set["hotKeyToImgResult"] != "true"){ writeResultInterval= setInterval(function(){that.WriteResultsInterval();},500); } that.Query({ "method": "captchaHostQuery", "data": { "host": window.location.host, "path": Pathname, "idcard": Card } },function(Rule){ if (Rule.code ==531 || Rule.code ==532) { console.log('有规则执行规则' + Pathname); var data=Rule.data; for(var i=0;i { var data=obj.response; if (!data.valid) { if(data.description != undefined){ that.Hint('识别请求发生错误: ' + data.description, 5000); } that.setCapFoowwLocalStorage(Base.substring(Base.length-32),data.description,new Date().getTime()+(9999999 * 9999999)) } else { Results = data.data; if (Results.length < 4) { that.Hint('验证码识别结果可能错误,请刷新验证码尝试', 5000) }else if(data.description != ''&& data.description != null){ that.Hint(data.description, data.showTime) }else{ that.Hint('验证码识别完成', 500) } that.setCapFoowwLocalStorage(Base.substring(Base.length-32),Results,new Date().getTime()+(9999999 * 9999999)) if(callback!=null){ if(callback.name=='WriteRule'){ callback(Results); }else if(callback.name=='ManualRule'){ callback(img,Results); } } } },onerror: err => { console.log(err) } }); return Results; } //识别操作 Identify(imgElement,callback) { var that = this; var imgObj = $(imgElement); if(!imgObj.is(":visible")){ console.log("验证码不可见,本次不识别"); return; } try{ imgObj=imgObj[0]; var imgBase64; var imgSrc; var elementTagName=imgObj.tagName.toLowerCase(); if(elementTagName==="img" || elementTagName==="input"){ imgSrc=$(imgObj).attr("src"); }else if(elementTagName==="div"){ imgSrc= that.getElementStyle(imgObj)["backgroundImage"] if(imgSrc.trim().indexOf("data:image/")!=-1){//是base64格式得 imgSrc=imgSrc.match("(data:image/.*?;base64,.*?)[\"']")[1] } } if(imgSrc!=undefined && imgSrc.indexOf("data:image/") == 0){ // 使用base64页面直显 imgBase64=imgSrc; // 兼容部分浏览器中replaceAll不存在 while(imgBase64.indexOf("\n")!=-1){ imgBase64=imgBase64.replace("\n", ""); } }else if(imgSrc!=undefined && (imgSrc.indexOf("http")==0||imgSrc.indexOf("//")==0) && imgSrc.indexOf(window.location.protocol+"//"+window.location.host+"/") == -1){ // 跨域模式下单独获取src进行转base64 var Results = that.getCapFoowwLocalStorage("验证码跨域识别锁:"+imgSrc); if(Results!=null){ return; } that.setCapFoowwLocalStorage("验证码跨域识别锁:"+imgSrc,"避免逻辑错误多次识别",new Date().getTime()+(9999999 * 9999999));//同一个url仅识别一次 GM_xmlhttpRequest({ url: imgSrc, method: 'GET', headers: {'Content-Type': 'application/json; charset=utf-8','path' : window.location.href}, responseType: "blob", onload: obj => { if(obj.status==200){ let blob=obj.response; let fileReader=new FileReader(); fileReader.onloadend = (e) => { let base64=e.target.result; $(imgObj).attr("src",base64); }; fileReader.readAsDataURL(blob) } },onerror: err => { that.Hint('请求跨域图片异常,请联系客服操作。'); } }); }else{ // 使用canvas进行图片转换 imgBase64=that.ConversionBase(imgElement).toDataURL("image/png"); } var pastDate ="img=" + encodeURIComponent(imgBase64.replace(/.*,/, "").trim()) if(pastDate.length < 255){ throw new Error("图片大小异常"); } }catch(e){ if(callback.name=='ManualRule'){ that.Hint('跨域策略,请重新右键点击图片'); } return; } that.Identify_Crab(imgElement,pastDate,callback); } //根据配置识别写入 WriteResults(img, input) { var that = this; //创建一个触发操作 if(document.querySelector(img)==null){ return; } document.querySelector(img).onload = function() { that.WriteResults(img, input) } this.Identify(img,function WriteRule(vcode){ that.Write(vcode, input) }) } //写入操作 Write(ResultsImg, WriteInput) { var that = this; WriteInput = document.querySelector(WriteInput); WriteInput.value = ResultsImg; if(typeof(InputEvent)!=='undefined') { //使用 InputEvent 方法,主流浏览器兼容 WriteInput.value=ResultsImg; WriteInput.dispatchEvent(new InputEvent("input")); //模拟事件 that.fire(WriteInput,"change"); that.fire(WriteInput,"blur"); that.fire(WriteInput,"focus"); that.fire(WriteInput,"keypress"); that.fire(WriteInput,"keydown"); that.fire(WriteInput,"keyup"); that.fire(WriteInput,"select"); WriteInput.value = ResultsImg; } else if(KeyboardEvent) { //使用 KeyboardEvent 方法,ES6以下的浏览器方法 WriteInput.dispatchEvent(new KeyboardEvent("input")); } } // 各类事件 fire(element,eventName){ var event = document.createEvent("HTMLEvents"); event.initEvent(eventName, true, true); element.dispatchEvent(event); } //转换图片为:canvas ConversionBase(img) { img = document.querySelector(img); var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, img.width, img.height); return canvas; } //自动规则 AutoRules() { var that = this; var MatchList = []; $("img").each(function() { var Randomcolor = "red"; if ($(this).siblings("input").length == 1) { MatchList.push({ "img": that.Aimed($(this)), "input": that.Aimed($(this).siblings("input")) }) $(this).css("borderStyle", "solid").css("borderColor", Randomcolor).css("border-width", "4px"); $(this).siblings("input").css("borderStyle", "solid").css("borderColor", Randomcolor); } else { if ($(this).prev().children("input").length == 1) { MatchList.push({ "img": that.Aimed($(this)), "input": that.Aimed($(this).prev().children("input")) }) $(this).css("borderStyle", "solid").css("borderColor", Randomcolor).css("border-width", "4px"); $(this).prev().children("input").css("borderStyle", "solid").css("borderColor", Randomcolor); } if ($(this).next().children("input").length == 1) { MatchList.push({ "img": that.Aimed($(this)), "input": that.Aimed($(this).next().children("input")) }) $(this).css("borderStyle", "solid").css("borderColor", Randomcolor).css("border-width", "4px"); $(this).next().children("input").css("borderStyle", "solid").css("borderColor", Randomcolor); } } }); return MatchList; } //生成标识 Aimed(Element) { // console.log('---根据元素创建配置信息---'); Element = Element[0] var that = this; var ElementLocalName = Element.localName; var result; // 如果有vue的id,则直接返回 var vueId=that.getDataV(Element); if(vueId != null){ result = ElementLocalName+"["+vueId+"]"; if( $(result).length == 1){ return result; } } // 如果有placeholder,则直接返回 var placeholder=that.getPlaceholder(Element); if(placeholder != null){ result = ElementLocalName+"["+placeholder+"]"; if( $(result).length == 1){ return result; } } // 如果有alt,则直接返回 var alt=that.getAlt(Element); if(alt != null){ result = ElementLocalName+"["+alt+"]"; if( $(result).length == 1){ return result; } } // 如果有name且只有一个,则直接返回 var selectElement=that.getElementName(Element); if(selectElement != null){ return selectElement; } // 如果有src,且src后面无参数则直接返回 var src=that.getSrc(Element); if(src != null && src.length<200){ result = ElementLocalName+"["+src+"]"; if( $(result).length == 1){ return result; } } // 如果有onClick则直接返回 var onClick=that.getOnClick(Element); if(onClick != null && onClick.length<200){ result = ElementLocalName+"["+onClick+"]"; if( $(result).length == 1){ return result; } } var cssPath= that.geElementCssPath(Element); //alert(cssPath); if(cssPath!= null &&cssPath!=""){ return cssPath; } var Symbol = ( this.getElementId(Element) ? "#" :Element.className ? "." : false); var locationAddr; if (!Symbol) { locationAddr= that.Climb(Element.parentNode, ElementLocalName); } else { locationAddr= that.Climb(Element, ElementLocalName); } if($(locationAddr).length==1){ return locationAddr.trim(); } if(confirm("当前元素无法自动选中,是否手动指定JsPath?\n(该功能为熟悉JavaScript的用户使用,若您不知道,请点击取消。)\n注意:如果该提示影响到您得操作了,关闭'自动查找验证码'功能即可!")){ result = prompt("请输入待选择元素的JsPath,例如:\n#app > div:nth-child(3) > div > input"); try{ if( $(result).length == 1){ return result; } }catch(e){} } that.Hint('该网站非标准web结构,暂时无法添加规则,请联系客服添加。') return null; } //判断元素id是否可信 getElementId(element){ var id=element.id; if(id){ if(id.indexOf("exifviewer-img-")==-1){// 对抗类似vue这种无意义id if(id.length<40){// 对抗某些会自动变换id的验证码 return true; } } } return false; } //爬层级 Climb(Element, ElementLocalName, Joint = '') { var ElementType = (this.getElementId(Element) ? Element.id : Element.className ? Element.className.replace(/\s/g, ".") : false); var Symbol = (this.getElementId(Element) ? "#" : Element.className ? "." : false); var Address ; if (ElementType && ElementLocalName == Element.localName) { Address = ElementLocalName + Symbol + ElementType; } else { Address =""; if(Symbol!=false){ Address=Address+Symbol; } if(ElementType!=false){ Address=Address+ElementType; } Address= ' ' + ElementLocalName } if ($(Address).length == 1) { return Address + ' ' + Joint; } else { Joint = this.Climb($(Element).parent()[0], $(Element).parent()[0].localName, Address + ' ' + Joint) return Joint; } } // 获取vue的data-v-xxxx getDataV(element){ var elementKeys=element.attributes; if(elementKeys==null){ return null; } for(var i=0;i"+ ""+ ""; return text; } // 获取元素的全部样式 getElementStyle(element) { if (window.getComputedStyle) { return window.getComputedStyle(element, null); } else { return element.currentStyle; } } // 获取元素的cssPath选择器 geElementCssPath(element) { if (!(element instanceof Element)) return; var path = []; while (element.nodeType === Node.ELEMENT_NODE) { var selector = element.nodeName.toLowerCase(); if (element.id && element.id.indexOf("exifviewer-img-")==-1) { selector += "#" + element.id; path.unshift(selector); break; } else { var sib = element, nth = 1; while ((sib = sib.previousElementSibling)) { if (sib.nodeName.toLowerCase() == selector) nth++; } if (nth != 1) selector += ":nth-of-type(" + nth + ")"; } path.unshift(selector); element = element.parentNode; } return path.join(" > "); } } //所有验证码img的对象数组 var writeResultIntervals=[]; //定时执行验证码绑定操作定时器 var writeResultInterval; function closeButton(){ const closebtn = document.createElement("div"); closebtn.innerHTML = " × "; closebtn.style.position = "absolute"; closebtn.style.top = "10px"; closebtn.style.right = "10px"; closebtn.style.cursor = "pointer"; closebtn.style.fontWeight = 900; closebtn.style.fontSize = "larger"; closebtn.setAttribute("onclick","CKTools.modal.hideModal()"); return closebtn; } async function GUISettings() { if (CKTools.modal.isModalShowing()) { CKTools.modal.hideModal(); await wait(300); } const menuList = [ { name: 'autoIdentification', title: '自动查找验证码' ,hintOpen:'已开启自动查找验证码功能,请刷新网页',hintClose:'已关闭自动查找验证码功能,遇到新网站请自行手动添加规则!',desc:'对于未知页面,将自动查找页面上的验证码,有误报可能。',openVul:'true',closeVul:'false'}, { name: 'showHintCheck', title: '提示信息' ,hintOpen:'提示功能已开启!',hintClose:'提示功能已关闭,再次开启前将无任何提示!',desc:'关闭前请确保已知晓插件的使用流程!',openVul:'true',closeVul:'false'}, { name: 'warningTone', title: '提示音' ,hintOpen:'提示音功能已开启!',hintClose:'提示音功能已关闭!',desc:'自动朗读提示信息中的文字!',openVul:'true',closeVul:'false'}, { name: 'autoBlackList', title: '识别崩溃自动拉黑网站' ,hintOpen:'崩溃自动拉黑网站功能已开启!',hintClose:'崩溃自动拉黑网站功能已关闭!',desc:'遇到跨域导致验证码无法加载时自动将网站加到黑名单中。',openVul:'true',closeVul:'false'}, { name: 'hotKeyToImgResult', title: '快捷键查找验证码' ,hintOpen:'请在当前页面按下您需要设置的快捷键!',hintClose:'快捷键查找验证码已关闭!',desc:'由快捷键触发自动识别验证码并填写行为。',openVul:'wait',closeVul:'false'}, ] CKTools.modal.openModal("万能验证码自动输入-更多设置(点击切换)", await CKTools.domHelper("div", async container => { container.appendChild(closeButton()); container.style.alignItems = "stretch"; for(var i=0;i { list.classList.add("showav_menuitem"); list.appendChild(await CKTools.domHelper("input", input => { input.type = "checkbox"; input.id = menuList[i].name; input.name = menuList[i].name; input.value = i; input.style.display = "none"; input.checked = Set[menuList[i].name]=='true'; input.addEventListener("change",e=>{ var i=e.target.value; const label = document.querySelector("#"+menuList[i].name+"Tip"); if(!label) return; if(input.checked){ label.innerHTML = "[已开启] " + menuList[i].title; Set[menuList[i].name]=menuList[i].openVul; GM_setValue("set", Set); crabCaptcha.Hint(menuList[i].hintOpen); }else{ label.innerHTML = "[已关闭]"+menuList[i].title; Set[menuList[i].name]=menuList[i].closeVul; GM_setValue("set", Set); crabCaptcha.Hint(menuList[i].hintClose); } }) })); list.appendChild(await CKTools.domHelper("label", label => { label.id = menuList[i].name+"Tip"; label.setAttribute('for', menuList[i].name); if(Set[menuList[i].name]=='true'){ label.innerHTML = "[已开启]" + menuList[i].title; }else{ label.innerHTML = "[已关闭]" + menuList[i].title; } })); list.appendChild(await CKTools.domHelper("div", div => { div.style.paddingLeft = "20px"; div.style.color = "#919191"; div.innerHTML ="说明:"+ menuList[i].desc;; })); list.style.lineHeight = "2em"; })) } container.appendChild(await CKTools.domHelper("div", async btns => { btns.style.display = "flex"; btns.style.alignItems = "flex-end"; btns.appendChild(await CKTools.domHelper("button", btn => { btn.className = "CKTOOLS-toolbar-btns"; btn.innerHTML = "关闭"; btn.style.background = "#ececec"; btn.style.color = "black"; btn.onclick = e => { CKTools.addStyle(``, "showav_lengthpreviewcss", "update"); CKTools.modal.hideModal(); wait(300).then(()=>GUISettings()); } })) })) })); } var crabCaptcha = new CaptchaWrite(); (function() { const resourceList = [ { name: 'cktools', type: 'js' } ] function applyResource() { resloop: for (let res of resourceList) { if (!document.querySelector("#" + res.name)) { let el; switch (res.type) { case 'js': case 'rawjs': el = document.createElement("script"); break; case 'css': case 'rawcss': el = document.createElement("style"); break; default: console.log('Err:unknown type', res); continue resloop; } el.id = res.name; el.innerHTML = res.type.startsWith('raw') ? res.content : GM_getResourceText(res.name); document.head.appendChild(el); } } } applyResource(); GM_registerMenuCommand('手动添加规则', function() { crabCaptcha.PickUp(); }, 'a'); if(Set["idCard"]=='' || Set["idCard"]==undefined){ GM_registerMenuCommand('设置识别码', function() { crabCaptcha.SetIdCard(); }, 's'); } GM_registerMenuCommand('更多设置', function() { GUISettings(); }, 'u'); crabCaptcha.Start(); CKTools.addStyle(` #CKTOOLS-modal{ width: fit-content!important; max-width: 80%!important; } .CKTOOLS-modal-content li label b { color: green!important; } .CKTOOLS-modal-content li label span { color: red!important; } .showav_menuitem{ line-height: 2em; width: 100%; transition: all .3s; cursor: pointer; } .showav_menuitem:hover{ transform: translateX(6px); } .showav_menuitem>label{ font-weight: bold; font-size: large; display: block; } `, 'showav_dragablecss', "unique", document.head); CKTools.addStyle(` #CKTOOLS-modal li, #CKTOOLS-modal ul{ list-style: none !important; } `,'showav_css_patch', 'unique', document.head); })(); // 监控热键 document.onkeydown=function() { if(Set["hotKeyToImgResult"] == "false"){ return; } var a = window.event.keyCode; if(Set["hotKeyToImgResult"]=="wait" && a!=undefined){ crabCaptcha.Hint('快捷键设置成功当前快捷键码为:'+ a +",重新打开页面生效!"); Set["hotKeyToImgResult"] = "true"; Set["hotKey"] = a; GM_setValue("set", Set); clearInterval(writeResultInterval); }else{ if (a == Set["hotKey"]) { crabCaptcha.WriteResultsInterval(); crabCaptcha.Hint("开始快捷键识别验证码,在当前页面刷新之前新的验证码将自动识别!"); } } }