|
148 | 148 | // @description:zu Yengeza izimpendulo ze-AI ku-Brave Search (inikwa amandla yi-GPT-4o!)
|
149 | 149 | // @author KudoAI
|
150 | 150 | // @namespace https://kudoai.com
|
151 |
| -// @version 2025.1.14.4 |
| 151 | +// @version 2025.1.14.5 |
152 | 152 | // @license MIT
|
153 | 153 | // @icon https://assets.bravegpt.com/images/icons/bravegpt/icon48.png?v=df624b0
|
154 | 154 | // @icon64 https://assets.bravegpt.com/images/icons/bravegpt/icon64.png?v=df624b0
|
|
179 | 179 | // @connect chatgpt.com
|
180 | 180 | // @connect update.greasyfork.org
|
181 | 181 | // @connect fanyi.sogou.com
|
| 182 | +// @connect toyaml.com |
182 | 183 | // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.5.0/dist/chatgpt.min.js#sha256-+C0x4BOFQc38aZB3pvUC2THu+ZSvuCxRphGdtRLjCDg=
|
183 | 184 | // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js#sha256-dppVXeVTurw1ozOPNE3XqhYmDJPOosfbKQcHyQSE58w=
|
184 | 185 | // @require https://cdn.jsdelivr.net/npm/generate-ip@2.4.4/dist/generate-ip.min.js#sha256-aQQKAQcMgCu8IpJp9HKs387x0uYxngO+Fb4pc5nSF4I=
|
|
603 | 604 | },
|
604 | 605 | expectedOrigin: { url: 'https://chatgpt.com', headers: { 'Priority': 'u=4' }},
|
605 | 606 | method: 'POST', streamable: true
|
| 607 | + }, |
| 608 | + 'ToYaml.com': { |
| 609 | + endpoint: 'https://toyaml.com/streams', |
| 610 | + expectedOrigin: { url: 'https://toyaml.com/chat.html', headers: { 'x-requested-with': 'XMLHttpRequest' }}, |
| 611 | + method: 'GET', streamable: true, watermark: '【本答案来自 toyaml.com】' |
606 | 612 | }
|
607 | 613 | }
|
608 | 614 |
|
|
3153 | 3159 | const ip = ipv4.generate({ verbose: false })
|
3154 | 3160 | const headers = {
|
3155 | 3161 | 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br, zstd',
|
3156 |
| - 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'DNT': '1', |
3157 |
| - 'Host': new URL(apis[api].endpoints?.completions || apis[api].endpoint).hostname, |
3158 |
| - 'Origin': apis[api].expectedOrigin.url, 'Sec-Fetch-Site': 'same-origin', |
3159 |
| - 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'X-Forwarded-For': ip, 'X-Real-IP': ip |
| 3162 | + 'Connection': 'keep-alive', 'DNT': '1', |
| 3163 | + 'Origin': apis[api].expectedOrigin.url, 'X-Forwarded-For': ip, 'X-Real-IP': ip |
3160 | 3164 | }
|
3161 | 3165 | headers.Referer = headers.Origin + '/'
|
3162 |
| - if (api == 'OpenAI') headers.Authorization = 'Bearer ' + config.openAIkey |
3163 |
| - Object.assign(headers, apis[api].expectedOrigin.headers) |
| 3166 | + if (apis[api].method == 'POST') Object.assign(headers, { |
| 3167 | + 'Content-Type': 'application/json', |
| 3168 | + 'Host': new URL(apis[api].endpoints?.completions || apis[api].endpoint).hostname, |
| 3169 | + 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors' |
| 3170 | + }) |
| 3171 | + else if (apis[api].method == 'GET') headers['x-requested-with'] = 'XMLHttpRequest' |
| 3172 | + Object.assign(headers, apis[api].expectedOrigin.headers) // API-specific ones |
| 3173 | + if (api == 'OpenAI') headers.Authorization = `Bearer ${config.openAIkey}` |
3164 | 3174 | return headers
|
3165 | 3175 | },
|
3166 | 3176 |
|
|
3284 | 3294 | }
|
3285 | 3295 |
|
3286 | 3296 | // Get/show answer from AI
|
3287 |
| - xhr({ |
3288 |
| - method: apis[get.reply.api].method, |
3289 |
| - url: apis[get.reply.api].endpoints?.completions || apis[get.reply.api].endpoint, |
| 3297 | + const reqMethod = apis[get.reply.api].method |
| 3298 | + const xhrConfig = { |
| 3299 | + headers: api.createHeaders(get.reply.api), method: reqMethod, |
3290 | 3300 | responseType: config.streamingDisabled || !config.proxyAPIenabled ? 'text' : 'stream',
|
3291 |
| - headers: api.createHeaders(get.reply.api), data: await api.createPayload(get.reply.api, msgChain), |
3292 |
| - onload: resp => dataProcess.text(get.reply, resp), |
3293 |
| - onloadstart: resp => dataProcess.stream(get.reply, resp), |
3294 | 3301 | onerror: err => { log.error(err)
|
3295 | 3302 | if (!config.proxyAPIenabled)
|
3296 | 3303 | appAlert(!config.openAIkey ? 'login' : ['openAInotWorking', 'suggestProxy'])
|
3297 | 3304 | else api.tryNew(get.reply)
|
3298 |
| - } |
3299 |
| - }) |
| 3305 | + }, |
| 3306 | + onload: resp => dataProcess.text(get.reply, resp), |
| 3307 | + onloadstart: resp => dataProcess.stream(get.reply, resp), |
| 3308 | + url: ( apis[get.reply.api].endpoints?.completions || apis[get.reply.api].endpoint ) |
| 3309 | + + ( reqMethod == 'GET' ? `?q=${encodeURIComponent(msgChain[msgChain.length -1].content)}` : '' ) |
| 3310 | + } |
| 3311 | + if (reqMethod == 'POST') xhrConfig.data = await api.createPayload(get.reply.api, msgChain) |
| 3312 | + xhr(xhrConfig) |
3300 | 3313 |
|
3301 | 3314 | // Get/show related queries if enabled on 1st get.reply()
|
3302 | 3315 | if (!config.rqDisabled && get.reply.attemptCnt == 1)
|
|
3355 | 3368 |
|
3356 | 3369 | // Get queries
|
3357 | 3370 | const payload = await api.createPayload(get.related.api, [{ role: 'user', content: rqPrompt }])
|
3358 |
| - return new Promise(resolve => xhr({ |
3359 |
| - method: apis[get.related.api].method, |
3360 |
| - url: apis[get.related.api].endpoints?.completions || apis[get.related.api].endpoint, |
3361 |
| - responseType: 'text', headers: api.createHeaders(get.related.api), |
3362 |
| - data: payload, |
3363 |
| - onload: resp => dataProcess.text(get.related, resp).then(resolve), |
3364 |
| - onerror: err => { log.error(err) ; api.tryNew(get.related) } |
3365 |
| - })) |
| 3371 | + return new Promise(resolve => { |
| 3372 | + const reqMethod = apis[get.related.api].method |
| 3373 | + const xhrConfig = { |
| 3374 | + headers: api.createHeaders(get.related.api), method: reqMethod, responseType: 'text', |
| 3375 | + onerror: err => { log.error(err) ; api.tryNew(get.related) }, |
| 3376 | + onload: resp => dataProcess.text(get.related, resp).then(resolve), |
| 3377 | + url: ( apis[get.related.api].endpoints?.completions || apis[get.related.api].endpoint ) |
| 3378 | + + ( reqMethod == 'GET' ? `?q=${rqPrompt}` : '' ) |
| 3379 | + } |
| 3380 | + if (reqMethod == 'POST') xhrConfig.data = payload |
| 3381 | + xhr(xhrConfig) |
| 3382 | + }) |
3366 | 3383 | }
|
3367 | 3384 | }
|
3368 | 3385 |
|
|
3384 | 3401 | reader.read().then(processStreamText).catch(err => log.error('Error processing stream', err.message))
|
3385 | 3402 |
|
3386 | 3403 | function processStreamText({ done, value }) {
|
3387 |
| - if (done) { caller.sender = null |
3388 |
| - if (appDiv.querySelector('.loading')) // no text shown |
3389 |
| - api.tryNew(caller) |
3390 |
| - else { // text was shown |
3391 |
| - caller.status = 'done' ; caller.attemptCnt = null |
3392 |
| - show.replyCornerBtns() ; api.clearTimedOut(caller.triedAPIs) |
3393 |
| - } return |
3394 |
| - } |
| 3404 | + if (done) { handleProcessCompletion() ; return } |
3395 | 3405 | let chunk = new TextDecoder('utf8').decode(new Uint8Array(value))
|
| 3406 | + if (chunk.includes(apis[caller.api].watermark)) { handleProcessCompletion() ; return } |
3396 | 3407 | if (caller.api == 'MixerBox AI') { // pre-process chunks
|
3397 | 3408 | const extractedChunks = Array.from(chunk.matchAll(/data:(.*)/g), match => match[1]
|
3398 | 3409 | .replace(/\[SPACE\]/g, ' ').replace(/\[NEWLINE\]/g, '\n'))
|
|
3427 | 3438 | processStreamText({ done, value })
|
3428 | 3439 | }).catch(err => log.error('Error reading stream', err.message))
|
3429 | 3440 | }
|
| 3441 | + |
| 3442 | + function handleProcessCompletion() { |
| 3443 | + caller.sender = null |
| 3444 | + if (appDiv.querySelector('.loading')) // no text shown |
| 3445 | + api.tryNew(caller) |
| 3446 | + else { // text was shown |
| 3447 | + caller.status = 'done' ; caller.attemptCnt = null |
| 3448 | + show.replyCornerBtns() ; api.clearTimedOut(caller.triedAPIs) |
| 3449 | + } return |
| 3450 | + } |
3430 | 3451 | },
|
3431 | 3452 |
|
3432 | 3453 | text(caller, resp) {
|
|
3458 | 3479 | } catch (err) { handleProcessError(err) }
|
3459 | 3480 | }
|
3460 | 3481 | } else if (resp.responseText) {
|
3461 |
| - if (/AIchatOS|FREEGPT/.test(caller.api)) { |
| 3482 | + if (/AIchatOS|ToYaml.com|FREEGPT/.test(caller.api)) { |
3462 | 3483 | try { // to show response or return related queries
|
3463 | 3484 | const text = resp.responseText, chunkSize = 1024
|
3464 | 3485 | let currentIdx = 0
|
|
3495 | 3516 | api.tryNew(caller)
|
3496 | 3517 | } else {
|
3497 | 3518 | caller.status = 'done' ; api.clearTimedOut(caller.triedAPIs) ; caller.attemptCnt = null
|
| 3519 | + respText = respText.replace(apis[caller.api].watermark, '').trim() |
3498 | 3520 | if (caller == get.reply) { show.reply(respText, footerContent) ; show.replyCornerBtns() }
|
3499 | 3521 | else resolve(arrayify(respText))
|
3500 |
| - }}} |
| 3522 | + } |
| 3523 | + } |
| 3524 | + } |
3501 | 3525 |
|
3502 | 3526 | function handleProcessError(err) { // suggest proxy or try diff API
|
3503 | 3527 | log.debug('Response text', resp.response)
|
|
0 commit comments