// ==UserScript==
// @name 一步搜题,自助查题|无侵入,更安全|超星学习通|智慧树|成人本科|继续教育|复制、截图搜题|超全题库,专业开发,值得信赖
// @version 0.2.2
// @description 一步搜题、自主查题,覆盖范围广,答案全,速度快。安全至上,不侵入主网站网络,不搜集用户信息,不主动提交,安全靠谱有保障,无后顾之忧当。涵盖中学、大学,超星学习通,智慧树,成人本科,继续教育等题库,支持复制搜题,截图搜题,方便快捷,一步到位,欢迎使用
// @author You
// @match *://*/*
// @match *://超星.com/*
// @match *://搜题.com/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js
// @require https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.16/vue.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.min.js
// @resource snow https://cdnjs.cloudflare.com/ajax/libs/quill/1.3.7/quill.snow.min.css
// @grant GM_info
// @grant GM_xmlhttpRequest
// @grant GM_getResourceText
// @grant GM_addStyle
// @connect appwk.baidu.com
// @connect 39.105.186.109
// @grant GM.getValue
// @grant GM.setValue
// @grant GM_setValue
// @grant GM_getValue
// @antifeature ads 脚本搜索题目时,可能附带广告
// @antifeature payment 脚本题目搜索,需对接第三方接口付费
// @namespace https://gf.qytechs.cn/zh-CN/users/1251635-ebsoo
// ==/UserScript==
$(document).ready(function() {
if (window.self !== window.top) {
return;
}
// 全局变量
var show = false;
var validateKey = '';
// Tab 组件
Vue.component('eb-tab', {
data: function () {
return {
tabList: [
{ name: '搜题' },
{ name: '配置' }
],
currentIndex: 0
}
},
template: `
<div class="eb-tab-container">
<div v-for="(tab, idx) in tabList" class="eb-tab" v-bind:class="{ active: currentIndex == idx }" @click="switchTab(idx)">{{tab.name}}</div>
</div>
`,
methods: {
switchTab(idx) {
this.currentIndex = idx;
this.$emit('switch-tab', idx);
}
}
});
// Header
Vue.component('eb-header', {
template: `
<div id="ebMyHeader" class="eb-header">
<div>ebsoo|一步搜题</div>
<div class="eb-close-icon" @click="closeEv">X</div>
</div>
`,
mounted() {
this.makeDraggable(this.state, document.getElementById(this.draggleId), document.getElementById(this.targetId));
},
data: function() {
return {
draggleId: 'ebMyHeader',
targetId: 'ebsooWrap0104',
state: {
dragging: false,
initX: 0,
initY: 0,
transX: 0,
transY: 0,
moveX: 0,
moveY: 0,
}
}
},
methods: {
closeEv() {
ebToggleOpenClose();
},
makeDraggable(state, el, target) {
function globalMousemove(event) {
const { clientX, clientY } = event;
const { initX, initY, transX, transY } = state;
const moveX = clientX - initX;
const moveY = clientY - initY;
target.style.transform = `translate(${transX + moveX}px, ${transY + moveY}px)`;
state.moveX = moveX;
state.moveY = moveY;
state.dragging = true;
}
function globalMouseup(_event) {
const { transX, transY, moveX, moveY } = state;
state.transX = transX + moveX;
state.transY = transY + moveY;
state.moveX = 0;
state.moveYY = 0;
document.removeEventListener('mousemove', globalMousemove);
document.removeEventListener('mouseup', globalMouseup);
state.dragging = null;
}
function mousedown(event) {
const {clientX, clientY} = event;
state.initX = clientX;
state.initY = clientY;
document.addEventListener('mousemove', globalMousemove);
document.addEventListener('mouseup', globalMouseup);
}
el.addEventListener('mousedown', mousedown);
}
}
});
// 搜索
Vue.component('eb-content-search', {
template: `
<div class="eb-content-search-container">
<div class="eb-editor-outer">
<div id="eb-editor-container"></div>
<div class="eb-my-footer">
<div class="eb-btn primary" @click="submit">搜索</div>
<div class="eb-btn bordered" @click="clear">清空</div>
<div v-if="remainCount > 0" class="eb-remain-count">剩余{{remainCount}}次</div>
</div>
</div>
<div id="adkfuasdgku" class="eb-answers">
<div class="fk-item" v-for="(item, index) in dataList">
<div class="eb-question" v-html="item.question">{{index}}</div>
<div class="eb-options-wrap" v-if="item.options.length">
<div class="eb-label">选项:</div>
<div v-for="(option, idx) in item.options">{{idx + 1}}、{{option}}</div>
</div>
<div class="eb-answer-wrap" v-if="item.answer.length">
<div class="eb-label">答案:</div>
<div class="fk-answer" v-for="(answer, idx) in item.answer" v-html="answer">
</div>
</div>
<div class="eb-answer-wrap" v-if="item.analysis">
<div class="eb-label">解析:</div>
<div class="fk-analysis" v-html="item.analysis">
</div>
</div>
</div>
</div>
<eb-loading v-show="showLoading"></eb-loading>
</div>
`,
data: function() {
return {
showLoading: false,
remainCount: 0,
dataList: []
}
},
methods: {
submit() {
let ops = quill.getContents().ops;
let imgNode = ops.find(item => item.insert && item.insert.image);
let txtNode = ops.find(item => item.insert && typeof item.insert == 'string');
let question = '';
if (imgNode) {
let base64 = imgNode.insert.image;
this.imgHandle(base64).then(res => {
this.imgOcr(res).then(val => {
this.fetchAnswer(val);
})
})
} else if (txtNode) {
question = txtNode.insert.replace(/\n|\t|\s/g, '');
this.fetchAnswer(question);
}
},
clear() {
quill.deleteText(0, 99999);
},
fetchAnswer(question) {
if (!question) {
alert('请输入文字,或者复制截图进来');
return;
}
if (question.length < 5) {
alert(`至少输入5个字,当前值: ${question}`);
return;
}
this.showLoading = true;
let prodHost = 'http://39.105.186.109:4000/eb/search/qs?key=' + (validateKey ? String(validateKey).trim() : '');
ebProxyHttp('POST', prodHost, {
headers: {
"Content-Type": "application/json"
},
data: JSON.stringify({
keyWord: question,
href: location.href || ''
})
}).then(response => {
let responseData = JSON.parse(response.responseText);
let code = responseData.code;
let msg = responseData.msg;
if (code == 1) {
let dataList = responseData.data.data;
this.remainCount = responseData.data.remainCount;
this.dataList = dataList;
this.showLoading = false;
$('#adkfuasdgku').scrollTop(0);
if (!dataList.length) {
alert('未搜到到相关结果');
}
} else {
this.showLoading = false;
alert(msg)
}
}).catch(_ => {
this.showLoading = false;
alert('出错了,请反馈');
})
},
imgHandle(base64) {
return new Promise((resolve, reject) => {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const image = new Image();
image.setAttribute("crossOrigin", "Anonymous");
image.src = base64;
image.onload = function() {
canvas.width = image.width;
canvas.height = image.height;
context.fillStyle = "#fff";
context.fillRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
canvas.toBlob(blob => {
resolve(blob);
});
};
});
},
imgOcr(blob) {
return new Promise((resolve, reject) => {
var fd = new FormData();
fd.append("image", blob, "1.png");
GM_xmlhttpRequest({
url: "https://appwk.baidu.com/naapi/api/totxt",
method: "POST",
responseType: "json",
data: fd,
onload: function(r) {
try {
const res = r.response.words_result.map(item => {
return item.words;
}).join("");
resolve(res);
} catch (err) {
resolve("");
}
}
});
});
}
}
});
// 配置
Vue.component('eb-config-set', {
template: `
<div class="eb-config-set">
<div class="eb-config-label">请输入校验码,输入后可直接去搜题</div>
<div>
<input id="ebConfigKeyInput" class="eb-config-key-input" maxlength="16" placeholder="请输入校验码" v-model="yourKey" @input="handleEbKeyInput" />
</div>
<div class="eb-way-to-get">
<div class="eb-way-item">
<div class="eb-way-label">方式一:通过爱发电获取校验码</div>
<div class="eb-get-key" @click="getCode">获取校验码</div>
</div>
<div class="eb-config-other" v-html="otherHtml"></div>
</div>
</div>
`,
mounted() {
ebGetValue('ebKey').then(res => {
let key = JSON.parse(res).key
this.yourKey = key;
validateKey = this.yourKey;
})
ebProxyHttp('GET', 'http://39.105.186.109:4000/eb/otherInfo').then(res => {
let responseData = JSON.parse(res.responseText);
if (responseData.code == 1) {
this.otherHtml = responseData.data.text
}
}).catch(_err => {})
},
props: {
remainCount: {
type: Number,
default: 0
}
},
data: function() {
return {
yourKey: '',
otherHtml: ''
}
},
methods: {
handleEbKeyInput(event) {
setTimeout(() => {
validateKey = this.yourKey;
ebSetValue('ebKey', {
key: validateKey
})
}, 100);
},
getCode() {
ebProxyHttp('GET', 'http://39.105.186.109:4000/eb/baseInfo').then(res => {
let responseData = JSON.parse(res.responseText);
if (responseData.code == 1) {
window.open(responseData.data.url, '_blank')
} else {
alert('获取校验码出错')
}
}).catch(_err => {});
}
}
});
Vue.component('eb-loading', {
template: `
<div class="eb-loading-wrap">
<svg t="1710311969650" class="icon eb-loading-icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6524" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M511.882596 287.998081h-0.361244a31.998984 31.998984 0 0 1-31.659415-31.977309v-0.361244c0-0.104761 0.115598-11.722364 0.115598-63.658399V96.000564a31.998984 31.998984 0 1 1 64.001581 0V192.001129c0 52.586273-0.111986 63.88237-0.119211 64.337537a32.002596 32.002596 0 0 1-31.977309 31.659415zM511.998194 959.99842a31.998984 31.998984 0 0 1-31.998984-31.998984v-96.379871c0-51.610915-0.111986-63.174332-0.115598-63.286318s0-0.242033 0-0.361243a31.998984 31.998984 0 0 1 63.997968-0.314283c0 0.455167 0.11921 11.711527 0.11921 64.034093v96.307622a31.998984 31.998984 0 0 1-32.002596 31.998984zM330.899406 363.021212a31.897836 31.897836 0 0 1-22.866739-9.612699c-0.075861-0.075861-8.207461-8.370021-44.931515-45.094076L195.198137 240.429485a31.998984 31.998984 0 0 1 45.256635-45.253022L308.336112 263.057803c37.182834 37.182834 45.090463 45.253022 45.41197 45.578141A31.998984 31.998984 0 0 1 330.899406 363.021212zM806.137421 838.11473a31.901448 31.901448 0 0 1-22.628318-9.374279L715.624151 760.859111c-36.724054-36.724054-45.018214-44.859267-45.097687-44.93874a31.998984 31.998984 0 0 1 44.77618-45.729864c0.32512 0.317895 8.395308 8.229136 45.578142 45.411969l67.88134 67.88134a31.998984 31.998984 0 0 1-22.624705 54.630914zM224.000113 838.11473a31.901448 31.901448 0 0 0 22.628317-9.374279l67.88134-67.88134c36.724054-36.724054 45.021826-44.859267 45.097688-44.93874a31.998984 31.998984 0 0 0-44.776181-45.729864c-0.32512 0.317895-8.395308 8.229136-45.578142 45.411969l-67.88134 67.884953a31.998984 31.998984 0 0 0 22.628318 54.627301zM255.948523 544.058589h-0.361244c-0.104761 0-11.722364-0.115598-63.658399-0.115598H95.942765a31.998984 31.998984 0 1 1 0-64.00158h95.996952c52.586273 0 63.88237 0.111986 64.337538 0.11921a31.998984 31.998984 0 0 1 31.659414 31.97731v0.361244a32.002596 32.002596 0 0 1-31.988146 31.659414zM767.939492 544.058589a32.002596 32.002596 0 0 1-31.995372-31.666639v-0.361244a31.998984 31.998984 0 0 1 31.659415-31.970085c0.455167 0 11.754876-0.11921 64.34115-0.11921h96.000564a31.998984 31.998984 0 0 1 0 64.00158H831.944685c-51.936034 0-63.553638 0.111986-63.665624 0.115598h-0.335957zM692.999446 363.0176a31.998984 31.998984 0 0 1-22.863126-54.381656c0.317895-0.32512 8.229136-8.395308 45.41197-45.578141l67.88134-67.884953A31.998984 31.998984 0 1 1 828.693489 240.429485l-67.892177 67.88134c-31.020013 31.023625-41.644196 41.759794-44.241539 44.393262l-0.697201 0.722488a31.908673 31.908673 0 0 1-22.863126 9.591025z" fill="" p-id="6525"></path></svg>
</div>
`,
data: function() {
},
methods: {
}
});
let httpClient = GM.xmlHttpRequest ? GM.xmlHttpRequest : (GM_xmlhttpRequest || null);
const styleText = GM_getResourceText("snow");
GM_addStyle(styleText);
GM_addStyle(`
#ebsooWrap0104 {
padding-top: 32px;display: flex;flex-direction: column; position: fixed;top: 100px;right: 100px;width: 400px;height: 600px;background-color: #fff;z-index:9999;border: solid #198754 1px;overflow: hidden; transform: translate(-8000px, 0);
}
#ebsooWrap0104 div {
margin: unset;
}
#ebsooWrap0104 #ebMyHeader {
position: absolute;left: 0;top: 0;right: 0;height: 32px;padding: 0 4px 0 12px;text-align: left;background-color: #198754; line-height: 32px;cursor: move;display: flex;justify-content: space-between; align-items: center; font-weight: bold; color: #fff; border-radius: 0px;user-select: none;
}
#ebsooWrap0104 #ebMyHeader .eb-close-icon {
cursor: pointer;font-size: 12px;border: solid #fff 2px;border-radius: 50%;height: 24px;width: 24px;line-height: 22px;text-align: center;
}
#ebsooWrap0104 .router-content {
height: calc(100% - 48px);
}
#ebsooWrap0104 .eb-content-search-container {
height: 100%;display: flex;flex-direction: column;
}
#ebsooWrap0104 .eb-editor-outer {
flex-shrink: 0;padding: 6px;box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5); padding-bottom: 10px;
}
#ebsooWrap0104 .ql-container {
height: 90px;
}
#ebsooWrap0104 .ql-toolbar {
text-align: left;
}
#ebsooWrap0104 .eb-my-footer {
position: relative; display: flex; align-items: center; height: 42px;line-height: 42px;border: 1px solid #ccc;border-top: none;box-sizing: border-box;text-align: center;
}
#ebsooWrap0104 .eb-my-footer .eb-btn {
margin:0 4px; width: 64px; height: 28px; line-height: 27px;text-align: center; border-radius: 6px; cursor: pointer; font-size: 14px; letter-spacing: 2px;
}
#ebsooWrap0104 .eb-my-footer .eb-btn.primary {
background-color: #198754; color: #fff;
}
#ebsooWrap0104 .eb-my-footer .eb-btn.bordered {
border: 1px solid #212529; color: #212529;
}
#ebsooWrap0104 .eb-my-footer .eb-remain-count {
position: absolute; right: 10px; font-weight: bold;
}
#ebsooWrap0104 .eb-answers {
flex: 1;text-align: left;overflow-y: scroll; padding: 6px;
}
#ebsooWrap0104 .eb-answers .fk-item {
padding: 10px 12px; border: dashed green 1px; margin-bottom: 6px; border-radius: 8px; box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5);
}
#ebsooWrap0104 .eb-answers .fk-item .eb-question,
#ebsooWrap0104 .eb-options-wrap,
#ebsooWrap0104 .eb-answer-wrap {
font-size: 13px; line-height: 17px;
}
#ebsooWrap0104 .fk-answer {
padding: 4px; border: dashed red 2px;font-weight: bold; border-bottom: none;
}
#ebsooWrap0104 .eb-answer-wrap .fk-answer:last-child {
border-bottom: dashed red 2px;
}
#ebsooWrap0104 .fk-analysis {
padding: 4px;
}
#ebsooWrap0104 .eb-answers em {
color: blue;font-style: normal;margin: 0 1px;
}
#ebsooWrap0104 .eb-label {
font-size: 14px;margin: 8px 0;width: fit-content;border-bottom: dashed #ccc 1px;
}
#ebsooWrap0104 .eb-panel-container {
position: absolute;left: 0;top: -100%;width: 100%;height: 100%;z-index: 10000;transition: top .3s;background-color: rgba(0,0,0,0.35);
}
#ebsooWrap0104 .eb-panel-container.show {
top: 32px;
}
#ebsooWrap0104 .eb-panel-wrap {
width: 400px;height: 400px;margin: 80px auto auto; background-color: #fff; border-radius: 10px;
}
#ebsooWrap0104 .eb-tab-container {
padding-left: 12px;margin: 6px 0;display: flex;height: 36px;border-bottom: solid #ccc 1px;
}
#ebsooWrap0104 .eb-tab-container .eb-tab {
box-sizing: border-box;position: relative;top: 1px;width: 72px;height: 36px;line-height: 36px;text-align: center;cursor: pointer;border-top: solid #fff 1px;
}
#ebsooWrap0104 .eb-tab-container .eb-tab.active {
border: solid #198754 1px;border-bottom: solid #fff 1px;border-top-left-radius: 6px;border-top-right-radius: 6px;background-color: #fff;
}
#ebsooWrap0104 .eb-config-set {
padding: 0px 16px; text-align: left;
}
#ebsooWrap0104 .eb-config-set .eb-config-label {
color: #212529; margin-bottom: 7px;margin-top: 10px;
}
#ebsooWrap0104 .eb-config-set .eb-config-key-input {
border: solid #ccc 1px;width: 240px; height: 30px; line-height: 30px; border-radius: 4px;padding: 0 10px; text-align: left; font-size: 15px; font-weight: bold;
}
#ebsooWrap0104 .eb-config-set .eb-config-key-btn {
margin:0 4px; width: 64px; height: 30px; line-height: 28px;text-align: center; border-radius: 6px; cursor: pointer; font-size: 14px; letter-spacing: 2px; color: #212529; border: 1px solid #212529;
}
#ebsooWrap0104 .eb-config-set .eb-way-to-get {
margin-top: 16px;
}
#ebsooWrap0104 .eb-config-set .eb-way-to-get .eb-way-item {
margin-bottom: 12px;
}
#ebsooWrap0104 .eb-config-set .eb-way-to-get .eb-way-label {
margin-bottom: 12px;
}
#ebsooWrap0104 .eb-config-set .eb-get-key {
height: 36px;line-height: 36px;text-align: center;width: 130px;background: #198754;color: #fff;border-radius: 12px;font-size: 14px;letter-spacing: 2px; cursor: pointer;animation: ebBtnAni 2s infinite;
}
@keyframes ebBtnAni {
0% {
scale: 1
}
50% {
scale: 0.9
}
100% {
scale: 1
}
}
#ebsooWrap0104 .eb-config-set .eb-config-key-btn.edit {
background-color: #198754; color: #fff; border: 1px solid #198754;
}
#ebsooWrap0104 .ql-toolbar.ql-snow {
padding: 0;
}
#ebsooWrap0104 .eb-loading-wrap {
position: absolute;left: 0;top: 0;right: 0;bottom: 0;background-color: rgba(0,0,0,0.05);text-align: center;
}
.eb-loading-wrap .eb-loading-icon {
margin-top: 50%; display: inline; width: 50px;
}
.eb-trigger-btn {
position: fixed; right: 0;bottom:50%; width: 40px; height: 56px; line-height: 56px; background-color: #198754; color: #fff; cursor: pointer;border-top-left-radius:8px; border-bottom-left-radius: 10px; text-align: center; font-size: 20px; user-select: none;z-index: 99999;
}
`);
var $wrap = $(`
<div id="ebsooWrap0104">
<eb-header></eb-header>
<eb-tab @switch-tab="handleSwitchTab"></eb-tab>
<div class="router-content">
<eb-content-search ref="ebContentSearchRef" v-show="currentTabIdx == 0" @patch-remain-count="handlePatchRemainCount"></eb-content-search>
<eb-config-set :remianCount="remainCount" v-show="currentTabIdx == 1"></eb-config-set>
</div>
</div>
<div id="ebTriggerBtn" class="eb-trigger-btn">开</div>`);
$('body').prepend($wrap);
var ebVmdf = new Vue({
el: '#ebsooWrap0104',
data: {
currentTabIdx: 0,
remainCount: 0
},
methods: {
handleSwitchTab(idx) {
this.currentTabIdx = idx;
},
handlePatchRemainCount(remainCount) {
this.remainCount = Number(remainCount);
}
}
});
var quill = new Quill('#eb-editor-container', {
modules: {
toolbar: [
['image']
],
keyboard: {
bindings: {
enter: {
key: 13,
handler: function() {
ebEnterThrottle();
}
}
}
},
},
placeholder: '输入文字,复制截图,或者上传图片进来',
theme: 'snow'
});
function ebProxyHttp (method, url, config) {
let options = {
timeout: 30000,
}
if (config) {
Object.assign(options, config);
}
return new Promise((resolve, reject) => {
httpClient(Object.assign({
method: method,
url: url.indexOf('http') > -1 ? url : baseUrl + url,
onload: function(response) {
resolve(response);
},
onerror: function (err) {
reject(err);
},
ontimeout: function () {
reject();
}
}, options));
})
};
function ebSetValue (key, val) {
if (GM_setValue) {
return new Promise(resolve => {
resolve(GM_setValue(key, JSON.stringify(val)));
});
} else if (GM.getValue) {
return GM.setValue(key, JSON.stringify(val));
}
}
function ebGetValue (key) {
if (GM_getValue) {
return new Promise(resolve => {
resolve(GM_getValue(key, "{}"))
})
} else if (GM.getValue) {
return GM.getValue(key);
}
}
$('#ebTriggerBtn').on('click', _ => {
ebToggleOpenClose();
})
function ebToggleOpenClose() {
if (show) {
show = false;
$('#ebsooWrap0104').css('transform', 'translate(-8000px, 0)');
$('#ebTriggerBtn').css('background-color', '#198754');
$('#ebTriggerBtn').html('开');
} else {
show = true;
$('#ebsooWrap0104').css('transform', 'translate(0, 0)');
$('#ebTriggerBtn').css('background-color', '#ccc');
$('#ebTriggerBtn').html('关');
}
}
function ebThrottle(func, delay) {
var lastTime;
return function() {
var now = Date.now();
if (!lastTime || now - lastTime >= delay) {
func.apply(this, arguments);
lastTime = now;
}
}
}
var ebEnterThrottle = ebThrottle(function() {
ebVmdf.$refs.ebContentSearchRef.submit();
}, 1000);
})