fenbiCopy

通过ctrl+q复制粉笔练习题,导出错题为json格式

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         fenbiCopy
// @namespace    http://tampermonkey.net/
// @version      0.6.1
// @description  通过ctrl+q复制粉笔练习题,导出错题为json格式
// @author       You
// @match        *://*.fenbi.com/*
// @match        *://*.mbadashi.com/*
// @icon         https://www.google.com/s2/favicons?domain=fenbi.com
// @grant        none
// @license MIT
// ==/UserScript==


(function() {
    'use strict';
var $ = selector => {
    return document.querySelector(selector)
}

var $$ = selector => {
    return document.querySelectorAll(selector)
}

/**
 * Export JSON
 */
function exportJSON(data = {}, filename) {
    let link = document.createElement('a')
    if (!filename) {
        filename = `${Date.now()}.json`
    }
    if (!/\.json$/.test(filename)) {
        filename += '.json'
    }
    link.download = filename
    link.href =
        'data:application/json;charset=utf-8,' +
        encodeURIComponent(JSON.stringify(data))
    link.click()
    link = null
}

const headers = {
    Accept: 'application/json',
    'Content-Type': 'application/json',
}

function get(url) {
    return fetch(url, {
        method: 'GET',
        credentials: 'include',
        headers: headers,
    })
        .then(response => {
            return handleResponse(url, response)
        })
        .catch(error => {
            console.error(`GET Request fail. url:${url}. message:${error}`)
            return Promise.reject({
                error: {
                    message: 'GET Request failed.',
                },
            })
        })
}

function post(url, data) {
    return fetch(url, {
        method: 'POST',
        credentials: 'include',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: data,
    })
        .then(response => {
            return handleResponse(url, response)
        })
        .catch(err => {
            console.error(`Request failed. Url = ${url} . Message = ${err}`)
            return { error: { message: 'Request failed.' } }
        })
}


function handleResponse(url, response) {
    let res = response
    if (res.status === 200) {
        return res.json()
    } else {
        console.error(`Request fail. url:${url}`)
        Promise.reject({
            error: {
                message: 'Request failed due to server error',
            },
        })
    }
}

/**
 * 查询题目列表的id
 * @param {*} categoryId
 * 言语:22017
 * 数量:22018
 * 判断:22019
 * 资料:22020
 * 常识:22021
 * @returns Promise
 */
const getQuestionIdByCategoryId = categoryId => {
    const url = `https://tiku.fenbi.com/api/xingce/errors?categoryId=${categoryId}&offset=0&limit=10000&order=asc&timeRange=0&app=web&kav=12&version=3.0.0.0`

    return get(url)
}

const getCategoryTree = () => {
    const url = `https://tiku.fenbi.com/api/xingce/errors/keypoint-tree?timeRange=0&app=web&kav=12&version=3.0.0.0`

    return get(url)
}

// 通过题目id查询题目内容以及解析
const getSolutionById = ids => {
    const url = `https://tiku.fenbi.com/api/xingce/solutions?ids=${ids}&app=web&kav=12&version=3.0.0.0`
    // get(url).then(res => {
    //     // content: 内容
    //     // correctAnswer: 正确答案
    //     // accessories 选项
    //     // solution: 解析
    //     // source:来源
    //     // keyPoint: { id, name } 题目分类
    //     // questionMeta: { mostWrongAnswer }
    // })
    return get(url)
}

// 通过题目id查询题目内容以及解析
const getSolutionByIds = async questionIds => {
    const sliceQuestionIds = []
    for (let i = 0; i < questionIds.length; i += 500) {
        sliceQuestionIds.push(questionIds.slice(i, i+500))
    }
    let solution = []
    for (let i = 0; i < sliceQuestionIds.length; i++) {
        const s = await get(`https://tiku.fenbi.com/api/xingce/solutions?ids=${sliceQuestionIds[i].toString()}&app=web&kav=12&version=3.0.0.0`)
        console.log('插入一次', sliceQuestionIds[i], s)
        solution = solution.concat(s)
    }
    return solution
}

const exportWrongQuestion = async () => {
    // 言语:22017
    // 数量:22018
    // 判断:22019
    // 资料:22020
    // 常识:22021
    // const categoryList = [22017, 22018, 22019, 22020, 22021]
    const data = {}
    const questionIds = []
    const keyPointTree = await getCategoryTree()
    keyPointTree &&
        keyPointTree.forEach(item => {
            questionIds.push(...item.questionIds)
        })
    // 试着
    console.log('questionIds', questionIds)
    const sliceQuestionIds = []
    for (let i = 0; i < questionIds.length; i += 500) {
        sliceQuestionIds.push(questionIds.slice(i, i+500))
    }
    let solution = []
    for (let i = 0; i < sliceQuestionIds.length; i++) {
        const s = await getSolutionById(sliceQuestionIds[i].toString())
        console.log('插入一次', sliceQuestionIds[i], s)
        solution = solution.concat(s)
    }
    data.question = solution
    data.category = keyPointTree
    exportJSON(data, 'fenbiData')
}

const registerExportBtn = () => {
    var html = `<button class="export-question">导出错题</button>`
    var wrap = document.querySelector('#keypoint-list .sort-filter')
    wrap && wrap.insertAdjacentHTML('beforeend', html)
    wrap && wrap.addEventListener('click', e => {
        var el = e.target
        if (el && el.classList.contains('export-question')) {
            console.log('导出错题')
            exportWrongQuestion()
        }
    })
}

var sleep = (timeout = 1000) => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve()
        }, timeout)
    })
}

// 导出阅读题
var exportRead = async () => {
    const set = new Set()
    for (let i = 0; i < 15; i++) {
        const data = await post(
            'https://tiku.fenbi.com/api/xingce/exercises?app=web&kav=12&version=3.0.0.0',
            'type=3&keypointId=22339&limit=100&exerciseTimeMode=2'
        )
        await sleep(3000)
        data.sheet.questionIds.forEach(item => set.add(item))
        console.log(`第${i + 1}次加载100题`)
    }
    const result = await getSolutionByIds([...set])
    exportJSON(result, 'fenbiData')
}

window.exportRead = exportRead

var insertInputEl = () => {
    const el = document.createElement('input')
    const btn = document.createElement('button')
    el.className = 'input-answer'
    btn.className = 'btn-answer'
    btn.innerHTML = '点击提交'
    $('.exam-detail') && $('.exam-detail').appendChild(el)
    $('.exam-detail') && $('.exam-detail').appendChild(btn)
    $('.btn-answer') && $('.btn-answer').addEventListener('click', () => {
        selectAnswer()
    })
    console.log('插入成功吗')
}

var copyText = text => {
    var textarea = document.createElement('textarea')
    document.body.appendChild(textarea)
    // 隐藏此输入框
    textarea.style.position = 'fixed'
    textarea.style.clip = 'rect(0 0 0 0)'
    textarea.style.top = '10px'
    // 赋值
    textarea.value = text
    // 选中
    textarea.select()
    // 复制
    document.execCommand('copy', true)
    // 移除输入框
    document.body.removeChild(textarea)
}

var fbRegisterEventTotal = (pos) => {
    const html = $('.exam-detail').children
    const items = $$('.chapter-control-item')
    if (!items) return ;

    const item = items[pos]
    console.log('item', item)
    const question = item.querySelectorAll('span')
    const start = parseInt(question[0].textContent) - 1
    const end = parseInt(question[question.length-1].textContent)
    const html2 = Array.from(html).filter((item, index) => index >= start && index < end)
    let str = ''
    html2.forEach(item => str += item.innerHTML)
    const result = `<section _ngcontent-fenbi-web-exams-c64="" class="exam-detail bg-color-gray-bold">${str}</section>`
    return result.replaceAll('\x3C!---->', '')
}

var fbRegisterEvent = () => {
    // 按ctrl + Q, 复制到剪切板。
    window.addEventListener('keydown', e => {
        if (e.ctrlKey && e.keyCode === 81) {
            console.log('已粘贴到剪切板')
            var html = ''
            if (window.location.href.includes('www.mbadashi.com')) {
               html = $('.multiple-choice-content').innerHTML.replace(/<pre/g, "<div").replace(/<\/pre>/g, "</div>")
            } else {
                html = $('.exam-detail').innerHTML
            }
            copyText(html)
        } else if (e.altKey && e.keyCode >= 49 && e.keyCode <= 53) {
            html = fbRegisterEventTotal(e.keyCode-49)
            copyText(html)
        }
    })
}

var selectAnswer = () => {
    const questionList = $$('.exam-detail .options')

    const answer = $('.input-answer').value.replace(/ /g, '')
    for (let i = 0; i < questionList.length; i++) {
        // 选项
        const options = questionList[i].querySelectorAll('.theme-ques-option')
        const s = parseInt(answer[i])
        options[s-1].click()
    }
}

var fbMain = () => {
    fbRegisterEvent()
    setTimeout(() => {
        registerExportBtn()
        insertInputEl()
    }, 3000)
}

fbMain()

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