|
3 | 3 | // @description Adds the magic of AI to Amazon shopping
|
4 | 4 | // @author KudoAI
|
5 | 5 | // @namespace https://kudoai.com
|
6 |
| -// @version 2025.1.14.5 |
| 6 | +// @version 2025.1.14.6 |
7 | 7 | // @license MIT
|
8 | 8 | // @icon https://amazongpt.kudoai.com/assets/images/icons/amazongpt/black-gold-teal/icon48.png?v=0fddfc7
|
9 | 9 | // @icon64 https://amazongpt.kudoai.com/assets/images/icons/amazongpt/black-gold-teal/icon64.png?v=0fddfc7
|
|
56 | 56 | // @connect chatgpt.com
|
57 | 57 | // @connect update.greasyfork.org
|
58 | 58 | // @connect fanyi.sogou.com
|
| 59 | +// @connect toyaml.com |
59 | 60 | // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.5.0/dist/chatgpt.min.js#sha256-+C0x4BOFQc38aZB3pvUC2THu+ZSvuCxRphGdtRLjCDg=
|
60 | 61 | // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js#sha256-dppVXeVTurw1ozOPNE3XqhYmDJPOosfbKQcHyQSE58w=
|
61 | 62 | // @require https://cdn.jsdelivr.net/npm/generate-ip@2.4.4/dist/generate-ip.min.js#sha256-aQQKAQcMgCu8IpJp9HKs387x0uYxngO+Fb4pc5nSF4I=
|
|
429 | 430 | },
|
430 | 431 | expectedOrigin: { url: 'https://chatgpt.com', headers: { 'Priority': 'u=4' }},
|
431 | 432 | method: 'POST', streamable: true
|
| 433 | + }, |
| 434 | + 'ToYaml.com': { |
| 435 | + endpoint: 'https://toyaml.com/streams', |
| 436 | + expectedOrigin: { url: 'https://toyaml.com/chat.html', headers: { 'x-requested-with': 'XMLHttpRequest' }}, |
| 437 | + method: 'GET', streamable: true, watermark: '【本答案来自 toyaml.com】' |
432 | 438 | }
|
433 | 439 | }
|
434 | 440 |
|
|
2502 | 2508 | const ip = ipv4.generate({ verbose: false })
|
2503 | 2509 | const headers = {
|
2504 | 2510 | 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br, zstd',
|
2505 |
| - 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'DNT': '1', |
2506 |
| - 'Host': new URL(apis[api].endpoints?.completions || apis[api].endpoint).hostname, |
2507 |
| - 'Origin': apis[api].expectedOrigin.url, 'Sec-Fetch-Site': 'same-origin', |
2508 |
| - 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'X-Forwarded-For': ip, 'X-Real-IP': ip |
| 2511 | + 'Connection': 'keep-alive', 'DNT': '1', |
| 2512 | + 'Origin': apis[api].expectedOrigin.url, 'X-Forwarded-For': ip, 'X-Real-IP': ip |
2509 | 2513 | }
|
2510 | 2514 | headers.Referer = headers.Origin + '/'
|
2511 |
| - if (api == 'OpenAI') headers.Authorization = 'Bearer ' + config.openAIkey |
2512 |
| - Object.assign(headers, apis[api].expectedOrigin.headers) |
| 2515 | + if (apis[api].method == 'POST') Object.assign(headers, { |
| 2516 | + 'Content-Type': 'application/json', |
| 2517 | + 'Host': new URL(apis[api].endpoints?.completions || apis[api].endpoint).hostname, |
| 2518 | + 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors' |
| 2519 | + }) |
| 2520 | + else if (apis[api].method == 'GET') headers['x-requested-with'] = 'XMLHttpRequest' |
| 2521 | + Object.assign(headers, apis[api].expectedOrigin.headers) // API-specific ones |
| 2522 | + if (api == 'OpenAI') headers.Authorization = `Bearer ${config.openAIkey}` |
2513 | 2523 | return headers
|
2514 | 2524 | },
|
2515 | 2525 |
|
|
2632 | 2642 | }
|
2633 | 2643 |
|
2634 | 2644 | // Get/show answer from AI
|
2635 |
| - xhr({ |
2636 |
| - method: apis[get.reply.api].method, |
2637 |
| - url: apis[get.reply.api].endpoints?.completions || apis[get.reply.api].endpoint, |
| 2645 | + const reqMethod = apis[get.reply.api].method |
| 2646 | + const xhrConfig = { |
| 2647 | + headers: api.createHeaders(get.reply.api), method: reqMethod, |
2638 | 2648 | responseType: config.streamingDisabled || !config.proxyAPIenabled ? 'text' : 'stream',
|
2639 |
| - headers: api.createHeaders(get.reply.api), data: await api.createPayload(get.reply.api, msgChain), |
2640 |
| - onload: resp => dataProcess.text(get.reply, resp), |
2641 |
| - onloadstart: resp => dataProcess.stream(get.reply, resp), |
2642 | 2649 | onerror: err => { log.error(err)
|
2643 | 2650 | if (!config.proxyAPIenabled)
|
2644 | 2651 | appAlert(!config.openAIkey ? 'login' : ['openAInotWorking', 'suggestProxy'])
|
2645 | 2652 | else api.tryNew(get.reply)
|
2646 |
| - } |
2647 |
| - }) |
| 2653 | + }, |
| 2654 | + onload: resp => dataProcess.text(get.reply, resp), |
| 2655 | + onloadstart: resp => dataProcess.stream(get.reply, resp), |
| 2656 | + url: ( apis[get.reply.api].endpoints?.completions || apis[get.reply.api].endpoint ) |
| 2657 | + + ( reqMethod == 'GET' ? `?q=${encodeURIComponent(msgChain[msgChain.length -1].content)}` : '' ) |
| 2658 | + } |
| 2659 | + if (reqMethod == 'POST') xhrConfig.data = await api.createPayload(get.reply.api, msgChain) |
| 2660 | + xhr(xhrConfig) |
2648 | 2661 | }
|
2649 | 2662 | }
|
2650 | 2663 |
|
|
2666 | 2679 | reader.read().then(processStreamText).catch(err => log.error('Error processing stream', err.message))
|
2667 | 2680 |
|
2668 | 2681 | function processStreamText({ done, value }) {
|
2669 |
| - if (done) { caller.sender = null |
2670 |
| - if (appDiv.querySelector('.loading')) // no text shown |
2671 |
| - api.tryNew(caller) |
2672 |
| - else { // text was shown |
2673 |
| - caller.status = 'done' ; caller.attemptCnt = null |
2674 |
| - show.replyCornerBtns() ; api.clearTimedOut(caller.triedAPIs) |
2675 |
| - } return |
2676 |
| - } |
| 2682 | + if (done) { handleProcessCompletion() ; return } |
2677 | 2683 | let chunk = new TextDecoder('utf8').decode(new Uint8Array(value))
|
| 2684 | + if (chunk.includes(apis[caller.api].watermark)) { handleProcessCompletion() ; return } |
2678 | 2685 | if (caller.api == 'MixerBox AI') { // pre-process chunks
|
2679 | 2686 | const extractedChunks = Array.from(chunk.matchAll(/data:(.*)/g), match => match[1]
|
2680 | 2687 | .replace(/\[SPACE\]/g, ' ').replace(/\[NEWLINE\]/g, '\n'))
|
|
2709 | 2716 | processStreamText({ done, value })
|
2710 | 2717 | }).catch(err => log.error('Error reading stream', err.message))
|
2711 | 2718 | }
|
| 2719 | + |
| 2720 | + function handleProcessCompletion() { |
| 2721 | + caller.sender = null |
| 2722 | + if (appDiv.querySelector('.loading')) // no text shown |
| 2723 | + api.tryNew(caller) |
| 2724 | + else { // text was shown |
| 2725 | + caller.status = 'done' ; caller.attemptCnt = null |
| 2726 | + show.replyCornerBtns() ; api.clearTimedOut(caller.triedAPIs) |
| 2727 | + } return |
| 2728 | + } |
2712 | 2729 | },
|
2713 | 2730 |
|
2714 | 2731 | text(caller, resp) {
|
|
2739 | 2756 | } catch (err) { handleProcessError(err) }
|
2740 | 2757 | }
|
2741 | 2758 | } else if (resp.responseText) {
|
2742 |
| - if (/AIchatOS|FREEGPT/.test(caller.api)) { |
| 2759 | + if (/AIchatOS|ToYaml.com|FREEGPT/.test(caller.api)) { |
2743 | 2760 | try { // to show response
|
2744 | 2761 | const text = resp.responseText, chunkSize = 1024
|
2745 | 2762 | let currentIdx = 0
|
|
2776 | 2793 | api.tryNew(caller)
|
2777 | 2794 | } else {
|
2778 | 2795 | caller.status = 'done' ; api.clearTimedOut(caller.triedAPIs) ; caller.attemptCnt = null
|
| 2796 | + respText = respText.replace(apis[caller.api].watermark, '').trim() |
2779 | 2797 | show.reply(respText) ; show.replyCornerBtns()
|
2780 | 2798 | }}}
|
2781 | 2799 |
|
|
0 commit comments