|
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/[email protected]/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/[email protected]/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