-
-
Notifications
You must be signed in to change notification settings - Fork 18
/
duckduckgpt.user.js
232 lines (210 loc) · 9.55 KB
/
duckduckgpt.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// ==UserScript==
// @name DuckDuckGPT 🤖
// @version 2023.02.22
// @author Adam Lui
// @namespace https://github.com/adamlui
// @description Adds ChatGPT answers to DuckDuckGo sidebar
// @homepageURL https://www.duckduckgpt.com
// @supportURL https://github.com/adamlui/userscripts/issues
// @license MIT
// @icon https://raw.githubusercontent.com/adamlui/userscripts/master/chatgpt/duckduckgpt/media/images/ddgpt-icon48.png
// @icon64 https://raw.githubusercontent.com/adamlui/userscripts/master/chatgpt/duckduckgpt/media/images/ddgpt-icon64.png
// @compatible chrome
// @compatible firefox
// @compatible edge
// @compatible brave
// @compatible vivaldi
// @match https://duckduckgo.com/*
// @include https://auth0.openai.com
// @connect chat.openai.com
// @grant GM.deleteValue
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.info
// @grant GM.xmlHttpRequest
// @grant GM_cookie
// @updateURL https://www.duckduckgpt.com/userscript/code/duckduckgpt.meta.js
// @downloadURL https://www.duckduckgpt.com/userscript/code/duckduckgpt.user.js
// ==/UserScript==
var GM_setValue = (() => GM.setValue)()
var GM_deleteValue = (() => GM.deleteValue)()
var GM_info = (() => GM.info)()
var GM_xmlhttpRequest = (() => GM.xmlHttpRequest)()
var GM_getValue = (() => GM.getValue)()
var alerts = {
waitingResponse: "Waiting for ChatGPT response...",
login: "Please login @ ",
tooManyRequests: "ChatGPT is flooded with too many requests. Check back later!",
checkCloudflare: "Please pass Cloudflare security check @ ",
unknowError: "Oops, maybe it is a bug, please check or submit ",
networkException: "Network exception, please refresh the page."
}
function getUserscriptManager() {
try { return GM_info.scriptHandler } catch (error) { return "other" }}
function deleteOpenAIcookies() {
if (getUserscriptManager() !== "Tampermonkey") return
var openAIauthURL = 'https://auth0.openai.com'
GM_cookie.list({ url: openAIauthURL }, function(cookies, error) {
if (!error) { for (var i = 0; i < cookies.length; i++) {
GM_cookie.delete({ url: openAIauthURL, name: cookies[i].name })
}}})}
function uuidv4() {
var s = [], hexDigits = "0123456789abcdef"
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 16), 1) }
s[14] = "4"
s[19] = hexDigits.substr(s[19] & 3 | 8, 1)
s[8] = s[13] = s[18] = s[23] = "-"
var uuid = s.join("")
return uuid
}
function i18n(name, param) {
return alerts[name] ? alerts[name].replace("#t#", param) : name }
var container = document.createElement("div")
function getContainer() { return container }
function containerShow(answer) {
var container2 = getContainer()
container2.innerHTML = '<p><span class="prefix">ChatGPT</span><pre></pre></p>'
container2.querySelector("pre").textContent = answer
}
function containerAlert(htmlStr) {
var container2 = getContainer()
container2.innerHTML = htmlStr
}
function alertLogin() {
deleteOpenAIcookies()
containerAlert(`<p>${i18n("login")}<a href="https://chat.openai.com" target="_blank" rel="noreferrer">chat.openai.com</a></p>`)
}
function alertBlockedByCloudflare() {
containerAlert(`<p>${i18n("checkCloudflare")}<a href="https://chat.openai.com" target="_blank" rel="noreferrer">chat.openai.com</a></p>`) }
function alertFrequentRequests() {
containerAlert(`<p>${i18n("tooManyRequests")}</p>`) }
function isBlockedbyCloudflare(resp) {
try {
var html = new DOMParser().parseFromString(resp, "text/html")
var title = html.querySelector("title")
return title.innerText === "Just a moment..."
} catch (error) { return false }
}
var timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => { reject(new Error('Timeout occurred')) }, 3000) })
async function getAnswer(question, callback) {
try {
var accessToken = await Promise.race([getAccessToken(), timeoutPromise])
GM_xmlhttpRequest({
method: "POST",
url: "https://chat.openai.com/backend-api/conversation",
headers: {
"Content-Type": " application/json",
Authorization: `Bearer ${accessToken}`
},
responseType: responseType(),
data: JSON.stringify({
action: "next",
messages: [
{
id: uuidv4(),
role: "user",
content: {
content_type: "text",
parts: [question]
}
}
],
model: "text-davinci-002-render",
parent_message_id: uuidv4()
}),
onloadstart: onLoadStart(),
onload: onLoad()
})
} catch (error) {
if (error === "UNAUTHORIZED") {
GM_deleteValue("accessToken")
alertLogin()
}
console.error("getAnswer error: ", error)
}
function responseType() {
if (getUserscriptManager() === "Tampermonkey") {
return "stream" } else { return "text" }
}
function onLoad() {
return function(event) {
if (event.status === 401) { GM_deleteValue("accessToken") ; alertLogin() }
if (event.status === 403) { alertBlockedByCloudflare() }
if (event.status === 429) { alertFrequentRequests() }
if (getUserscriptManager() !== "Tampermonkey") {
if (event.response) {
const answer = JSON.parse(event.response
.split("\n\n").slice(-3, -2)[0].slice(6)).message.content.parts[0]
containerShow(answer)
}}}
}
function onLoadStart() {
if (getUserscriptManager() === "Tampermonkey") {
return function(stream) {
var reader = stream.response.getReader()
reader.read().then(function processText({ done, value }) {
if (done) { return }
let responseItem = String.fromCharCode(...Array.from(value))
var items = responseItem.split("\n\n")
if (items.length > 2) {
var lastItem = items.slice(-3, -2)[0]
if (lastItem.startsWith("data: [DONE]")) {
responseItem = items.slice(-4, -3)[0]
} else { responseItem = lastItem }
}
if (responseItem.startsWith("data: {")) {
var answer = JSON.parse(responseItem.slice(6)).message.content.parts[0]
containerShow(answer)
} else if (responseItem.startsWith("data: [DONE]")) { return }
return reader.read().then(processText)
})}}}
}
function getAccessToken() {
return new Promise(async (resolve, rejcet) => {
var accessToken = await GM_getValue("accessToken")
if (!accessToken) {
GM_xmlhttpRequest({
url: "https://chat.openai.com/api/auth/session",
onload: function(response) {
if (isBlockedbyCloudflare(response.responseText)) {
alertLogin() ; return }
var accessToken2 = JSON.parse(response.responseText).accessToken;
if (!accessToken2) { rejcet("UNAUTHORIZED") }
GM_setValue("accessToken", accessToken2)
resolve(accessToken2)
},
onerror: function(error) { rejcet(error) },
ontimeout: () => { console.error("getAccessToken timeout!") }
})
} else { resolve(accessToken) }
})}
// Stylize ChatGPT container to match DDG's
var css = `
.chatgpt-container { border-radius: 8px ; border: 1px solid #dadce0 ; padding: 15px ; flex-basis: 0 ;
flex-grow: 1 ; word-wrap: break-word ; white-space: pre-wrap ; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.06) }
.chatgpt-container p { margin: 0 }
.chatgpt-container .prefix { font-weight: 700 }
.chatgpt-container .loading { color: #b6b8ba ; animation: pulse 2s cubic-bezier(.4,0,.6,1) infinite }
.chatgpt-container.sidebar-free { margin-left: 60px ; height: fit-content }
.chatgpt-container pre { white-space: pre-wrap ; min-width: 0 ; margin-bottom: 0 ; line-height: 20px }
@keyframes pulse { 0%, to { opacity: 1 } 50% { opacity: .5 }}
.chatgpt-feedback { margin: 2px 0 25px 0 }`
var styleNode = document.createElement('style') ; styleNode.innerText = css
document.head.appendChild(styleNode)
// Run injection function
async function main() {
// Initialize container
var container2 = getContainer();
container2.className = "chatgpt-container";
container2.innerHTML = `<p class="loading">${i18n("waitingResponse")}</p>`;
// Initialize feedback link
var container3 = document.createElement("div");
container3.className = "feedback-prompt chatgpt-feedback";
container3.innerHTML = `<a href="https://github.com/adamlui/userscripts/discussions/new/choose" class="feedback-prompt__link" target="_blank">Share Feedback</a>`;
// Inject container + feedback link
var siderbarContainer = document.getElementsByClassName("results--sidebar")[0];
siderbarContainer.prepend(container2, container3);
getAnswer(new URL(window.location.href).searchParams.get("q"));
} ; main()