|
148 | 148 | // @description:zu Yengeza izimpendulo ze-AI ku-DuckDuckGo (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.ddgpt.com/images/icons/duckduckgpt/icon48.png?v=06af076
|
154 | 154 | // @icon64 https://assets.ddgpt.com/images/icons/duckduckgpt/icon64.png?v=06af076
|
|
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=
|
|
611 | 612 | },
|
612 | 613 | expectedOrigin: { url: 'https://chatgpt.com', headers: { 'Priority': 'u=4' }},
|
613 | 614 | method: 'POST', streamable: true
|
| 615 | + }, |
| 616 | + 'ToYaml.com': { |
| 617 | + endpoint: 'https://toyaml.com/streams', |
| 618 | + expectedOrigin: { url: 'https://toyaml.com/chat.html', headers: { 'x-requested-with': 'XMLHttpRequest' }}, |
| 619 | + method: 'GET', streamable: true, watermark: '【本答案来自 toyaml.com】' |
614 | 620 | }
|
615 | 621 | }
|
616 | 622 |
|
|
3038 | 3044 | const ip = ipv4.generate({ verbose: false })
|
3039 | 3045 | const headers = {
|
3040 | 3046 | 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br, zstd',
|
3041 |
| - 'Connection': 'keep-alive', 'Content-Type': 'application/json', 'DNT': '1', |
3042 |
| - 'Host': new URL(apis[api].endpoints?.completions || apis[api].endpoint).hostname, |
3043 |
| - 'Origin': apis[api].expectedOrigin.url, 'Sec-Fetch-Site': 'same-origin', |
3044 |
| - 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'X-Forwarded-For': ip, 'X-Real-IP': ip |
| 3047 | + 'Connection': 'keep-alive', 'DNT': '1', |
| 3048 | + 'Origin': apis[api].expectedOrigin.url, 'X-Forwarded-For': ip, 'X-Real-IP': ip |
3045 | 3049 | }
|
3046 | 3050 | headers.Referer = headers.Origin + '/'
|
3047 |
| - if (api == 'OpenAI') headers.Authorization = 'Bearer ' + config.openAIkey |
3048 |
| - Object.assign(headers, apis[api].expectedOrigin.headers) |
| 3051 | + if (apis[api].method == 'POST') Object.assign(headers, { |
| 3052 | + 'Content-Type': 'application/json', |
| 3053 | + 'Host': new URL(apis[api].endpoints?.completions || apis[api].endpoint).hostname, |
| 3054 | + 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors' |
| 3055 | + }) |
| 3056 | + else if (apis[api].method == 'GET') headers['x-requested-with'] = 'XMLHttpRequest' |
| 3057 | + Object.assign(headers, apis[api].expectedOrigin.headers) // API-specific ones |
| 3058 | + if (api == 'OpenAI') headers.Authorization = `Bearer ${config.openAIkey}` |
3049 | 3059 | return headers
|
3050 | 3060 | },
|
3051 | 3061 |
|
|
3169 | 3179 | }
|
3170 | 3180 |
|
3171 | 3181 | // Get/show answer from AI
|
3172 |
| - xhr({ |
3173 |
| - method: apis[get.reply.api].method, |
3174 |
| - url: apis[get.reply.api].endpoints?.completions || apis[get.reply.api].endpoint, |
| 3182 | + const reqMethod = apis[get.reply.api].method |
| 3183 | + const xhrConfig = { |
| 3184 | + headers: api.createHeaders(get.reply.api), method: reqMethod, |
3175 | 3185 | responseType: config.streamingDisabled || !config.proxyAPIenabled ? 'text' : 'stream',
|
3176 |
| - headers: api.createHeaders(get.reply.api), data: await api.createPayload(get.reply.api, msgChain), |
3177 |
| - onload: resp => dataProcess.text(get.reply, resp), |
3178 |
| - onloadstart: resp => dataProcess.stream(get.reply, resp), |
3179 | 3186 | onerror: err => { log.error(err)
|
3180 | 3187 | if (!config.proxyAPIenabled)
|
3181 | 3188 | appAlert(!config.openAIkey ? 'login' : ['openAInotWorking', 'suggestProxy'])
|
3182 | 3189 | else api.tryNew(get.reply)
|
3183 |
| - } |
3184 |
| - }) |
| 3190 | + }, |
| 3191 | + onload: resp => dataProcess.text(get.reply, resp), |
| 3192 | + onloadstart: resp => dataProcess.stream(get.reply, resp), |
| 3193 | + url: ( apis[get.reply.api].endpoints?.completions || apis[get.reply.api].endpoint ) |
| 3194 | + + ( reqMethod == 'GET' ? `?q=${encodeURIComponent(msgChain[msgChain.length -1].content)}` : '' ) |
| 3195 | + } |
| 3196 | + if (reqMethod == 'POST') xhrConfig.data = await api.createPayload(get.reply.api, msgChain) |
| 3197 | + xhr(xhrConfig) |
3185 | 3198 |
|
3186 | 3199 | // Get/show related queries if enabled on 1st get.reply()
|
3187 | 3200 | if (!config.rqDisabled && get.reply.attemptCnt == 1)
|
|
3238 | 3251 |
|
3239 | 3252 | // Get queries
|
3240 | 3253 | const payload = await api.createPayload(get.related.api, [{ role: 'user', content: rqPrompt }])
|
3241 |
| - return new Promise(resolve => xhr({ |
3242 |
| - method: apis[get.related.api].method, |
3243 |
| - url: apis[get.related.api].endpoints?.completions || apis[get.related.api].endpoint, |
3244 |
| - responseType: 'text', headers: api.createHeaders(get.related.api), |
3245 |
| - data: payload, |
3246 |
| - onload: resp => dataProcess.text(get.related, resp).then(resolve), |
3247 |
| - onerror: err => { log.error(err) ; api.tryNew(get.related) } |
3248 |
| - })) |
| 3254 | + return new Promise(resolve => { |
| 3255 | + const reqMethod = apis[get.related.api].method |
| 3256 | + const xhrConfig = { |
| 3257 | + headers: api.createHeaders(get.related.api), method: reqMethod, responseType: 'text', |
| 3258 | + onerror: err => { log.error(err) ; api.tryNew(get.related) }, |
| 3259 | + onload: resp => dataProcess.text(get.related, resp).then(resolve), |
| 3260 | + url: ( apis[get.related.api].endpoints?.completions || apis[get.related.api].endpoint ) |
| 3261 | + + ( reqMethod == 'GET' ? `?q=${rqPrompt}` : '' ) |
| 3262 | + } |
| 3263 | + if (reqMethod == 'POST') xhrConfig.data = payload |
| 3264 | + xhr(xhrConfig) |
| 3265 | + }) |
3249 | 3266 | }
|
3250 | 3267 | }
|
3251 | 3268 |
|
|
3267 | 3284 | reader.read().then(processStreamText).catch(err => log.error('Error processing stream', err.message))
|
3268 | 3285 |
|
3269 | 3286 | function processStreamText({ done, value }) {
|
3270 |
| - if (done) { caller.sender = null |
3271 |
| - if (appDiv.querySelector('.loading')) // no text shown |
3272 |
| - api.tryNew(caller) |
3273 |
| - else { // text was shown |
3274 |
| - caller.status = 'done' ; caller.attemptCnt = null |
3275 |
| - show.replyCornerBtns() ; api.clearTimedOut(caller.triedAPIs) |
3276 |
| - } return |
3277 |
| - } |
| 3287 | + if (done) { handleProcessCompletion() ; return } |
3278 | 3288 | let chunk = new TextDecoder('utf8').decode(new Uint8Array(value))
|
| 3289 | + if (chunk.includes(apis[caller.api].watermark)) { handleProcessCompletion() ; return } |
3279 | 3290 | if (caller.api == 'MixerBox AI') { // pre-process chunks
|
3280 | 3291 | const extractedChunks = Array.from(chunk.matchAll(/data:(.*)/g), match => match[1]
|
3281 | 3292 | .replace(/\[SPACE\]/g, ' ').replace(/\[NEWLINE\]/g, '\n'))
|
|
3310 | 3321 | processStreamText({ done, value })
|
3311 | 3322 | }).catch(err => log.error('Error reading stream', err.message))
|
3312 | 3323 | }
|
| 3324 | + |
| 3325 | + function handleProcessCompletion() { |
| 3326 | + caller.sender = null |
| 3327 | + if (appDiv.querySelector('.loading')) // no text shown |
| 3328 | + api.tryNew(caller) |
| 3329 | + else { // text was shown |
| 3330 | + caller.status = 'done' ; caller.attemptCnt = null |
| 3331 | + show.replyCornerBtns() ; api.clearTimedOut(caller.triedAPIs) |
| 3332 | + } return |
| 3333 | + } |
3313 | 3334 | },
|
3314 | 3335 |
|
3315 | 3336 | text(caller, resp) {
|
|
3341 | 3362 | } catch (err) { handleProcessError(err) }
|
3342 | 3363 | }
|
3343 | 3364 | } else if (resp.responseText) {
|
3344 |
| - if (/AIchatOS|FREEGPT/.test(caller.api)) { |
| 3365 | + if (/AIchatOS|ToYaml.com|FREEGPT/.test(caller.api)) { |
3345 | 3366 | try { // to show response or return related queries
|
3346 | 3367 | const text = resp.responseText, chunkSize = 1024
|
3347 | 3368 | let currentIdx = 0
|
|
3378 | 3399 | api.tryNew(caller)
|
3379 | 3400 | } else {
|
3380 | 3401 | caller.status = 'done' ; api.clearTimedOut(caller.triedAPIs) ; caller.attemptCnt = null
|
| 3402 | + respText = respText.replace(apis[caller.api].watermark, '').trim() |
3381 | 3403 | if (caller == get.reply) { show.reply(respText) ; show.replyCornerBtns() }
|
3382 | 3404 | else resolve(arrayify(respText))
|
3383 |
| - }}} |
| 3405 | + } |
| 3406 | + } |
| 3407 | + } |
3384 | 3408 |
|
3385 | 3409 | function handleProcessError(err) { // suggest proxy or try diff API
|
3386 | 3410 | log.debug('Response text', resp.response)
|
|
0 commit comments