@kudoai/chatgpt.js 3.6.3 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -7
- package/chatgpt.js +925 -934
- package/dist/chatgpt.min.js +16 -6
- package/docs/README.md +25 -8
- package/docs/USERGUIDE.md +63 -3
- package/package.json +7 -7
- package/starters/chrome/extension/components/icons.js +1 -1
- package/starters/chrome/extension/components/modals.js +8 -5
- package/starters/chrome/extension/content.js +8 -6
- package/starters/chrome/extension/lib/chatgpt.js +925 -934
- package/starters/chrome/extension/lib/dom.js +6 -6
- package/starters/chrome/extension/lib/settings.js +2 -2
- package/starters/chrome/extension/manifest.json +1 -1
- package/starters/chrome/extension/popup/controller.js +66 -74
- package/starters/chrome/extension/popup/index.html +1 -6
- package/starters/chrome/extension/popup/style.css +33 -34
- package/starters/chrome/extension/service-worker.js +12 -11
- package/starters/greasemonkey/chatgpt.js-greasemonkey-starter.user.js +2 -2
package/chatgpt.js
CHANGED
|
@@ -4,60 +4,83 @@
|
|
|
4
4
|
// Latest minified release: https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js/chatgpt.min.js
|
|
5
5
|
|
|
6
6
|
// Init feedback props
|
|
7
|
-
localStorage.alertQueue = JSON.stringify([])
|
|
8
|
-
localStorage.notifyProps = JSON.stringify({ queue: { topRight: [], bottomRight: [], bottomLeft: [], topLeft: [] }})
|
|
7
|
+
localStorage.alertQueue = JSON.stringify([])
|
|
8
|
+
localStorage.notifyProps = JSON.stringify({ queue: { topRight: [], bottomRight: [], bottomLeft: [], topLeft: [] }})
|
|
9
9
|
|
|
10
10
|
// Define chatgpt API
|
|
11
11
|
const chatgpt = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
|
|
13
|
+
endpoints: {
|
|
14
|
+
assets: 'https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js',
|
|
15
|
+
openAI: {
|
|
16
|
+
session: 'https://chatgpt.com/api/auth/session',
|
|
17
|
+
chats: 'https://chatgpt.com/backend-api/conversations',
|
|
18
|
+
chat: 'https://chatgpt.com/backend-api/conversation',
|
|
19
|
+
share_create: 'https://chatgpt.com/backend-api/share/create',
|
|
20
|
+
share: 'https://chatgpt.com/backend-api/share',
|
|
21
|
+
instructions: 'https://chatgpt.com/backend-api/user_system_messages'
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
selectors: {
|
|
26
|
+
btns: {
|
|
27
|
+
continue: 'button.btn:has([d^="M4.47189"])', login: '[data-testid*=login]',
|
|
28
|
+
newChat: 'button[data-testid*=new-chat-button],' // sidebar button (when logged in)
|
|
29
|
+
+ 'button:has([d^="M3.06957"]),' // Cycle Arrows icon (Temp chat mode)
|
|
30
|
+
+ 'button:has([d^="M15.6729"])', // Pencil icon (recorded chat mode)
|
|
31
|
+
regen: 'button:has([d^="M3.06957"])', scroll: 'button:has([d^="M12 21C11.7348"])',
|
|
32
|
+
send: '[data-testid=send-button]', sidebar: 'button[data-testid*=sidebar-button]',
|
|
33
|
+
stop: 'button[data-testid=stop-button]', voice: 'button[data-testid*=composer-speech-button]'
|
|
34
|
+
},
|
|
35
|
+
chatDivs: {
|
|
36
|
+
convo: 'main > div > div > div > div > div > div[class*=group]',
|
|
37
|
+
msg: 'div[data-message-author-role]', reply: 'div[data-message-author-role=assistant]'
|
|
38
|
+
},
|
|
39
|
+
chatHistory: 'nav',
|
|
40
|
+
errors: { txt: '[class*=text-error]' },
|
|
41
|
+
footer: '.min-h-4',
|
|
42
|
+
header: 'main .sticky',
|
|
43
|
+
links: { newChat: 'nav a[href="/"]', sidebarItem: 'nav a' },
|
|
44
|
+
sidebar: 'div[class*=sidebar]',
|
|
45
|
+
ssgManifest: 'script[src*="_ssgManifest.js"]'
|
|
46
|
+
},
|
|
22
47
|
|
|
23
48
|
actAs(persona) {
|
|
24
49
|
// Prompts ChatGPT to act as a persona from https://github.com/KudoAI/chat-prompts/blob/main/personas.json
|
|
25
50
|
|
|
26
|
-
const promptsUrl = 'https://cdn.jsdelivr.net/gh/KudoAI/chat-prompts/dist/personas.min.json'
|
|
51
|
+
const promptsUrl = 'https://cdn.jsdelivr.net/gh/KudoAI/chat-prompts/dist/personas.min.json'
|
|
27
52
|
return new Promise((resolve, reject) => {
|
|
28
|
-
const xhr = new XMLHttpRequest()
|
|
29
|
-
xhr.open('GET', promptsUrl, true); xhr.send()
|
|
53
|
+
const xhr = new XMLHttpRequest()
|
|
54
|
+
xhr.open('GET', promptsUrl, true) ; xhr.send()
|
|
30
55
|
xhr.onload = () => {
|
|
31
|
-
if (xhr.status
|
|
32
|
-
const data = JSON.parse(xhr.responseText).personas
|
|
56
|
+
if (xhr.status != 200) return reject('🤖 chatgpt.js >> Request failed. Cannot retrieve prompts data.')
|
|
57
|
+
const data = JSON.parse(xhr.responseText).personas
|
|
33
58
|
if (!persona) {
|
|
34
59
|
console.log('\n%c🤖 chatgpt.js personas\n',
|
|
35
|
-
'font-family: sans-serif ; font-size: xxx-large ; font-weight: bold')
|
|
60
|
+
'font-family: sans-serif ; font-size: xxx-large ; font-weight: bold')
|
|
36
61
|
for (const prompt of data) // list personas
|
|
37
|
-
console.log(`%c${ prompt.title }`, 'font-family: monospace ; font-size: larger ;')
|
|
38
|
-
return resolve()
|
|
62
|
+
console.log(`%c${ prompt.title }`, 'font-family: monospace ; font-size: larger ;')
|
|
63
|
+
return resolve()
|
|
39
64
|
}
|
|
40
|
-
const selectedPrompt = data.find(obj => obj.title.toLowerCase() == persona.toLowerCase())
|
|
65
|
+
const selectedPrompt = data.find(obj => obj.title.toLowerCase() == persona.toLowerCase())
|
|
41
66
|
if (!selectedPrompt)
|
|
42
|
-
return reject(`🤖 chatgpt.js >> Persona '${ persona }' was not found!`)
|
|
43
|
-
chatgpt.send(selectedPrompt.prompt, 'click')
|
|
44
|
-
console.info(`Loading ${ persona } persona...`)
|
|
45
|
-
chatgpt.isIdle().then(() =>
|
|
46
|
-
return resolve()
|
|
47
|
-
}
|
|
48
|
-
})
|
|
67
|
+
return reject(`🤖 chatgpt.js >> Persona '${ persona }' was not found!`)
|
|
68
|
+
chatgpt.send(selectedPrompt.prompt, 'click')
|
|
69
|
+
console.info(`Loading ${ persona } persona...`)
|
|
70
|
+
chatgpt.isIdle().then(() => console.info('Persona activated!'))
|
|
71
|
+
return resolve()
|
|
72
|
+
}
|
|
73
|
+
})
|
|
49
74
|
},
|
|
50
75
|
|
|
51
76
|
activateDarkMode() {
|
|
52
|
-
document.documentElement.classList.replace('light', 'dark')
|
|
53
|
-
document.documentElement.style.colorScheme = 'dark'
|
|
54
|
-
localStorage.setItem('theme', 'dark');
|
|
77
|
+
document.documentElement.classList.replace('light', 'dark')
|
|
78
|
+
document.documentElement.style.colorScheme = localStorage.theme = 'dark'
|
|
55
79
|
},
|
|
56
80
|
|
|
57
81
|
activateLightMode() {
|
|
58
|
-
document.documentElement.classList.replace('dark', 'light')
|
|
59
|
-
document.documentElement.style.colorScheme = 'light'
|
|
60
|
-
localStorage.setItem('theme', 'light');
|
|
82
|
+
document.documentElement.classList.replace('dark', 'light')
|
|
83
|
+
document.documentElement.style.colorScheme = localStorage.theme = 'light'
|
|
61
84
|
},
|
|
62
85
|
|
|
63
86
|
alert(title, msg, btns, checkbox, width) {
|
|
@@ -66,7 +89,7 @@ const chatgpt = {
|
|
|
66
89
|
|
|
67
90
|
// Init env context
|
|
68
91
|
const scheme = chatgpt.isDarkMode() ? 'dark' : 'light',
|
|
69
|
-
isMobile = chatgpt.browser.isMobile()
|
|
92
|
+
isMobile = chatgpt.browser.isMobile()
|
|
70
93
|
|
|
71
94
|
// Define event handlers
|
|
72
95
|
const handlers = {
|
|
@@ -85,52 +108,56 @@ const chatgpt = {
|
|
|
85
108
|
if (!alert || alert.style.display == 'none') return
|
|
86
109
|
if (event.key.startsWith('Esc') || event.keyCode == 27) dismissAlert() // and do nothing
|
|
87
110
|
else { // Space/Enter pressed
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
111
|
+
const mainBtn = alert.querySelector('.modal-buttons').lastChild // look for main button
|
|
112
|
+
if (mainBtn) { mainBtn.click() ; event.preventDefault() } // click if found
|
|
90
113
|
}
|
|
91
114
|
}
|
|
92
115
|
}
|
|
93
116
|
},
|
|
94
117
|
|
|
95
118
|
drag: {
|
|
96
|
-
mousedown(event) { // find modal, attach listeners, init XY offsets
|
|
119
|
+
mousedown(event) { // find modal, update styles, attach listeners, init XY offsets
|
|
97
120
|
if (event.button != 0) return // prevent non-left-click drag
|
|
98
121
|
if (getComputedStyle(event.target).cursor == 'pointer') return // prevent drag on interactive elems
|
|
99
|
-
chatgpt.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
[
|
|
122
|
+
chatgpt.draggingModal = event.currentTarget
|
|
123
|
+
event.preventDefault() // prevent sub-elems like icons being draggable
|
|
124
|
+
Object.assign(chatgpt.draggingModal.style, {
|
|
125
|
+
cursor: 'grabbing', transition: '0.1s', willChange: 'transform', transform: 'scale(1.05)' });
|
|
126
|
+
[...chatgpt.draggingModal.children] // prevent hover FX if drag lags behind cursor
|
|
127
|
+
.forEach(child => child.style.pointerEvents = 'none');
|
|
128
|
+
['mousemove', 'mouseup'].forEach(eventType => // add listeners
|
|
104
129
|
document.addEventListener(eventType, handlers.drag[eventType]))
|
|
105
|
-
const
|
|
106
|
-
handlers.drag.offsetX = event.clientX -
|
|
107
|
-
handlers.drag.offsetY = event.clientY -
|
|
130
|
+
const draggingModalRect = chatgpt.draggingModal.getBoundingClientRect()
|
|
131
|
+
handlers.drag.offsetX = event.clientX - draggingModalRect.left +21
|
|
132
|
+
handlers.drag.offsetY = event.clientY - draggingModalRect.top +12
|
|
108
133
|
},
|
|
109
134
|
|
|
110
135
|
mousemove(event) { // drag modal
|
|
111
|
-
if (!chatgpt.
|
|
136
|
+
if (!chatgpt.draggingModal) return
|
|
112
137
|
const newX = event.clientX - handlers.drag.offsetX,
|
|
113
138
|
newY = event.clientY - handlers.drag.offsetY
|
|
114
|
-
Object.assign(chatgpt.
|
|
139
|
+
Object.assign(chatgpt.draggingModal.style, { left: `${newX}px`, top: `${newY}px` })
|
|
115
140
|
},
|
|
116
141
|
|
|
117
|
-
mouseup() { // remove listeners, reset chatgpt.
|
|
118
|
-
Object.assign(chatgpt.
|
|
142
|
+
mouseup() { // restore styles/pointer events, remove listeners, reset chatgpt.draggingModal
|
|
143
|
+
Object.assign(chatgpt.draggingModal.style, { // restore styles
|
|
119
144
|
cursor: 'inherit', transition: 'inherit', willChange: 'auto', transform: 'scale(1)' });
|
|
120
|
-
[
|
|
145
|
+
[...chatgpt.draggingModal.children] // restore pointer events
|
|
146
|
+
.forEach(child => child.style.pointerEvents = '');
|
|
147
|
+
['mousemove', 'mouseup'].forEach(eventType => // remove listeners
|
|
121
148
|
document.removeEventListener(eventType, handlers.drag[eventType]))
|
|
122
|
-
chatgpt.
|
|
149
|
+
chatgpt.draggingModal = null
|
|
123
150
|
}
|
|
124
151
|
}
|
|
125
152
|
}
|
|
126
153
|
|
|
127
|
-
// Create modal parent/children
|
|
128
|
-
const modalContainer = document.createElement('div')
|
|
129
|
-
modalContainer.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now()
|
|
130
|
-
modalContainer.classList.add('chatgpt-modal')
|
|
154
|
+
// Create modal parent/children elems
|
|
155
|
+
const modalContainer = document.createElement('div')
|
|
156
|
+
modalContainer.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now()
|
|
157
|
+
modalContainer.classList.add('chatgpt-modal') // add class to main div
|
|
131
158
|
const modal = document.createElement('div'),
|
|
132
159
|
modalTitle = document.createElement('h2'),
|
|
133
|
-
modalMessage = document.createElement('p')
|
|
160
|
+
modalMessage = document.createElement('p')
|
|
134
161
|
|
|
135
162
|
// Create/append/update modal style (if missing or outdated)
|
|
136
163
|
const thisUpdated = 1739338889852 // timestamp of last edit for this file's `modalStyle`
|
|
@@ -226,88 +253,85 @@ const chatgpt = {
|
|
|
226
253
|
.chatgpt-modal input[type=checkbox]:focus {
|
|
227
254
|
outline: none ; box-shadow: none ; -webkit-box-shadow: none ; -moz-box-shadow: none }
|
|
228
255
|
.chatgpt-modal .checkbox-group label {
|
|
229
|
-
font-size: 14px ; color: ${ scheme == 'dark' ? '#e1e1e1' : '#1e1e1e' }}`
|
|
256
|
+
cursor: pointer ; font-size: 14px ; color: ${ scheme == 'dark' ? '#e1e1e1' : '#1e1e1e' }}`
|
|
230
257
|
)
|
|
231
258
|
}
|
|
232
259
|
|
|
233
|
-
// Insert text into
|
|
234
|
-
modalTitle.innerText = title || '';
|
|
235
|
-
modalMessage.innerText = msg || ''; chatgpt.renderHTML(modalMessage);
|
|
260
|
+
// Insert text into elems
|
|
261
|
+
modalTitle.innerText = title || '' ; modalMessage.innerText = msg || '' ; chatgpt.renderHTML(modalMessage)
|
|
236
262
|
|
|
237
263
|
// Create/append buttons (if provided) to buttons div
|
|
238
|
-
const modalButtons = document.createElement('div')
|
|
239
|
-
modalButtons.classList.add('modal-buttons', 'no-mobile-tap-outline')
|
|
264
|
+
const modalButtons = document.createElement('div')
|
|
265
|
+
modalButtons.classList.add('modal-buttons', 'no-mobile-tap-outline')
|
|
240
266
|
if (btns) { // are supplied
|
|
241
|
-
if (!Array.isArray(btns)) btns = [btns]
|
|
267
|
+
if (!Array.isArray(btns)) btns = [btns] // convert single button to array if necessary
|
|
242
268
|
btns.forEach((buttonFn) => { // create title-cased labels + attach listeners
|
|
243
|
-
const button = document.createElement('button')
|
|
269
|
+
const button = document.createElement('button')
|
|
244
270
|
button.textContent = buttonFn.name
|
|
245
271
|
.replace(/[_-]\w/g, match => match.slice(1).toUpperCase()) // convert snake/kebab to camel case
|
|
246
272
|
.replace(/([A-Z])/g, ' $1') // insert spaces
|
|
247
|
-
.replace(/^\w/, firstChar => firstChar.toUpperCase())
|
|
248
|
-
button.onclick = () => { dismissAlert(); buttonFn()
|
|
249
|
-
modalButtons.insertBefore(button, modalButtons.firstChild)
|
|
250
|
-
})
|
|
273
|
+
.replace(/^\w/, firstChar => firstChar.toUpperCase()) // capitalize first letter
|
|
274
|
+
button.onclick = () => { dismissAlert() ; buttonFn() }
|
|
275
|
+
modalButtons.insertBefore(button, modalButtons.firstChild)
|
|
276
|
+
})
|
|
251
277
|
}
|
|
252
278
|
|
|
253
279
|
// Create/append OK/dismiss button to buttons div
|
|
254
|
-
const dismissBtn = document.createElement('button')
|
|
255
|
-
dismissBtn.textContent = btns ? 'Dismiss' : 'OK'
|
|
256
|
-
modalButtons.insertBefore(dismissBtn, modalButtons.firstChild)
|
|
280
|
+
const dismissBtn = document.createElement('button')
|
|
281
|
+
dismissBtn.textContent = btns ? 'Dismiss' : 'OK'
|
|
282
|
+
modalButtons.insertBefore(dismissBtn, modalButtons.firstChild)
|
|
257
283
|
|
|
258
284
|
// Highlight primary button
|
|
259
|
-
modalButtons.lastChild.classList.add('primary-modal-btn')
|
|
285
|
+
modalButtons.lastChild.classList.add('primary-modal-btn')
|
|
260
286
|
|
|
261
287
|
// Create/append checkbox (if provided) to checkbox group div
|
|
262
|
-
const checkboxDiv = document.createElement('div')
|
|
288
|
+
const checkboxDiv = document.createElement('div')
|
|
263
289
|
if (checkbox) { // is supplied
|
|
264
|
-
checkboxDiv.classList.add('checkbox-group')
|
|
290
|
+
checkboxDiv.classList.add('checkbox-group')
|
|
265
291
|
const checkboxFn = checkbox, // assign the named function to checkboxFn
|
|
266
|
-
checkboxInput = document.createElement('input')
|
|
267
|
-
checkboxInput.type = 'checkbox';
|
|
268
|
-
checkboxInput.onchange = checkboxFn;
|
|
292
|
+
checkboxInput = document.createElement('input')
|
|
293
|
+
checkboxInput.type = 'checkbox' ; checkboxInput.onchange = checkboxFn
|
|
269
294
|
|
|
270
295
|
// Create/show label
|
|
271
|
-
const checkboxLabel = document.createElement('label')
|
|
272
|
-
checkboxLabel.onclick = () => { checkboxInput.checked = !checkboxInput.checked; checkboxFn()
|
|
296
|
+
const checkboxLabel = document.createElement('label')
|
|
297
|
+
checkboxLabel.onclick = () => { checkboxInput.checked = !checkboxInput.checked ; checkboxFn() }
|
|
273
298
|
checkboxLabel.textContent = checkboxFn.name.charAt(0).toUpperCase() // capitalize first char
|
|
274
299
|
+ checkboxFn.name.slice(1) // format remaining chars
|
|
275
300
|
.replace(/([A-Z])/g, (match, letter) => ' ' + letter.toLowerCase()) // insert spaces, convert to lowercase
|
|
276
301
|
.replace(/\b(\w+)nt\b/gi, '$1n\'t') // insert apostrophe in 'nt' suffixes
|
|
277
|
-
.trim()
|
|
302
|
+
.trim() // trim leading/trailing spaces
|
|
278
303
|
|
|
279
|
-
checkboxDiv.append(checkboxInput); checkboxDiv.append(checkboxLabel)
|
|
304
|
+
checkboxDiv.append(checkboxInput) ; checkboxDiv.append(checkboxLabel)
|
|
280
305
|
}
|
|
281
306
|
|
|
282
307
|
// Create close button
|
|
283
|
-
const closeBtn = document.createElement('div')
|
|
284
|
-
closeBtn.title = 'Close'; closeBtn.classList.add('modal-close-btn', 'no-mobile-tap-outline')
|
|
285
|
-
const closeSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
|
286
|
-
closeSVG.setAttribute('height', '10px')
|
|
287
|
-
closeSVG.setAttribute('viewBox', '0 0 14 14')
|
|
288
|
-
closeSVG.setAttribute('fill', 'none')
|
|
289
|
-
const closeSVGpath = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
|
290
|
-
closeSVGpath.setAttribute('fill-rule', 'evenodd')
|
|
291
|
-
closeSVGpath.setAttribute('clip-rule', 'evenodd')
|
|
292
|
-
closeSVGpath.setAttribute('fill', chatgpt.isDarkMode() ? 'white' : 'black')
|
|
293
|
-
closeSVGpath.setAttribute('d', 'M13.7071 1.70711C14.0976 1.31658 14.0976 0.683417 13.7071 0.292893C13.3166 -0.0976312 12.6834 -0.0976312 12.2929 0.292893L7 5.58579L1.70711 0.292893C1.31658 -0.0976312 0.683417 -0.0976312 0.292893 0.292893C-0.0976312 0.683417 -0.0976312 1.31658 0.292893 1.70711L5.58579 7L0.292893 12.2929C-0.0976312 12.6834 -0.0976312 13.3166 0.292893 13.7071C0.683417 14.0976 1.31658 14.0976 1.70711 13.7071L7 8.41421L12.2929 13.7071C12.6834 14.0976 13.3166 14.0976 13.7071 13.7071C14.0976 13.3166 14.0976 12.6834 13.7071 12.2929L8.41421 7L13.7071 1.70711Z')
|
|
294
|
-
closeSVG.append(closeSVGpath); closeBtn.append(closeSVG)
|
|
308
|
+
const closeBtn = document.createElement('div')
|
|
309
|
+
closeBtn.title = 'Close' ; closeBtn.classList.add('modal-close-btn', 'no-mobile-tap-outline')
|
|
310
|
+
const closeSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
|
311
|
+
closeSVG.setAttribute('height', '10px')
|
|
312
|
+
closeSVG.setAttribute('viewBox', '0 0 14 14')
|
|
313
|
+
closeSVG.setAttribute('fill', 'none')
|
|
314
|
+
const closeSVGpath = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
|
315
|
+
closeSVGpath.setAttribute('fill-rule', 'evenodd')
|
|
316
|
+
closeSVGpath.setAttribute('clip-rule', 'evenodd')
|
|
317
|
+
closeSVGpath.setAttribute('fill', chatgpt.isDarkMode() ? 'white' : 'black')
|
|
318
|
+
closeSVGpath.setAttribute('d', 'M13.7071 1.70711C14.0976 1.31658 14.0976 0.683417 13.7071 0.292893C13.3166 -0.0976312 12.6834 -0.0976312 12.2929 0.292893L7 5.58579L1.70711 0.292893C1.31658 -0.0976312 0.683417 -0.0976312 0.292893 0.292893C-0.0976312 0.683417 -0.0976312 1.31658 0.292893 1.70711L5.58579 7L0.292893 12.2929C-0.0976312 12.6834 -0.0976312 13.3166 0.292893 13.7071C0.683417 14.0976 1.31658 14.0976 1.70711 13.7071L7 8.41421L12.2929 13.7071C12.6834 14.0976 13.3166 14.0976 13.7071 13.7071C14.0976 13.3166 14.0976 12.6834 13.7071 12.2929L8.41421 7L13.7071 1.70711Z')
|
|
319
|
+
closeSVG.append(closeSVGpath) ; closeBtn.append(closeSVG)
|
|
295
320
|
|
|
296
321
|
// Assemble/append div
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
modal.
|
|
300
|
-
modalContainer.append(modal); document.body.append(modalContainer);
|
|
322
|
+
modal.append(closeBtn, modalTitle, modalMessage, checkboxDiv, modalButtons)
|
|
323
|
+
modal.style.width = `${ width || 458 }px`
|
|
324
|
+
modalContainer.append(modal) ; document.body.append(modalContainer)
|
|
301
325
|
|
|
302
326
|
// Enqueue alert
|
|
303
|
-
let alertQueue = JSON.parse(localStorage.alertQueue)
|
|
304
|
-
alertQueue.push(modalContainer.id)
|
|
305
|
-
localStorage.alertQueue = JSON.stringify(alertQueue)
|
|
327
|
+
let alertQueue = JSON.parse(localStorage.alertQueue)
|
|
328
|
+
alertQueue.push(modalContainer.id)
|
|
329
|
+
localStorage.alertQueue = JSON.stringify(alertQueue)
|
|
306
330
|
|
|
307
331
|
// Show alert if none active
|
|
308
|
-
modalContainer.style.display = 'none'
|
|
309
|
-
if (alertQueue.length
|
|
310
|
-
modalContainer.style.display = ''
|
|
332
|
+
modalContainer.style.display = 'none'
|
|
333
|
+
if (alertQueue.length == 1) {
|
|
334
|
+
modalContainer.style.display = ''
|
|
311
335
|
setTimeout(() => { // dim bg
|
|
312
336
|
modal.parentNode.style.backgroundColor = `rgba(67,70,72,${ scheme == 'dark' ? 0.62 : 0.33 })`
|
|
313
337
|
modal.parentNode.classList.add('animated')
|
|
@@ -315,9 +339,8 @@ const chatgpt = {
|
|
|
315
339
|
}
|
|
316
340
|
|
|
317
341
|
// Add listeners
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
document.addEventListener('keydown', handlers.dismiss.key);
|
|
342
|
+
[modalContainer, closeBtn, closeSVG, dismissBtn].forEach(elem => elem.onclick = handlers.dismiss.click)
|
|
343
|
+
document.addEventListener('keydown', handlers.dismiss.key)
|
|
321
344
|
modal.onmousedown = handlers.drag.mousedown // enable click-dragging
|
|
322
345
|
|
|
323
346
|
// Define alert dismisser
|
|
@@ -348,78 +371,80 @@ const chatgpt = {
|
|
|
348
371
|
},
|
|
349
372
|
|
|
350
373
|
async askAndGetReply(query) {
|
|
351
|
-
chatgpt.send(query); await chatgpt.isIdle()
|
|
352
|
-
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
374
|
+
chatgpt.send(query) ; await chatgpt.isIdle()
|
|
375
|
+
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
353
376
|
},
|
|
354
377
|
|
|
355
378
|
autoRefresh: {
|
|
356
379
|
activate(interval) {
|
|
357
|
-
if (this.isActive)
|
|
358
|
-
console.log(
|
|
380
|
+
if (this.isActive) // already running, do nothing
|
|
381
|
+
return console.log(
|
|
382
|
+
`↻ ChatGPT >> [${chatgpt.autoRefresh.nowTimeStamp()}] Auto refresh already active!`)
|
|
359
383
|
|
|
360
|
-
const autoRefresh = this
|
|
384
|
+
const autoRefresh = this
|
|
361
385
|
|
|
362
386
|
// Run main activate routine
|
|
363
|
-
this.toggle.refreshFrame()
|
|
387
|
+
this.toggle.refreshFrame()
|
|
364
388
|
const scheduleRefreshes = interval => {
|
|
365
|
-
const randomDelay = Math.max(2, Math.floor(chatgpt.randomFloat() * 21 - 10))
|
|
389
|
+
const randomDelay = Math.max(2, Math.floor(chatgpt.randomFloat() * 21 - 10)) // set random delay up to ±10 secs
|
|
366
390
|
autoRefresh.isActive = setTimeout(() => {
|
|
367
|
-
const manifestScript = document.querySelector(
|
|
391
|
+
const manifestScript = document.querySelector(chatgpt.selectors.ssgManifest)
|
|
368
392
|
if (manifestScript) {
|
|
369
|
-
document.querySelector('#refresh-frame').src = manifestScript.src + '?' + Date.now()
|
|
370
|
-
console.log(
|
|
393
|
+
document.querySelector('#refresh-frame').src = manifestScript.src + '?' + Date.now()
|
|
394
|
+
console.log(`↻ ChatGPT >> [${autoRefresh.nowTimeStamp()}] ChatGPT session refreshed`)
|
|
371
395
|
}
|
|
372
|
-
scheduleRefreshes(interval)
|
|
373
|
-
}, (interval + randomDelay) * 1000)
|
|
396
|
+
scheduleRefreshes(interval)
|
|
397
|
+
}, (interval + randomDelay) * 1000)
|
|
374
398
|
};
|
|
375
|
-
scheduleRefreshes( interval ? parseInt(interval, 10) : 30 )
|
|
376
|
-
console.log(
|
|
399
|
+
scheduleRefreshes( interval ? parseInt(interval, 10) : 30 )
|
|
400
|
+
console.log(`↻ ChatGPT >> [${chatgpt.autoRefresh.nowTimeStamp()}] Auto refresh activated`)
|
|
377
401
|
|
|
378
402
|
// Add listener to send beacons in Chromium to thwart auto-discards if Page Visibility API supported
|
|
379
|
-
if (navigator.userAgent.includes('Chrome') && typeof document.hidden
|
|
380
|
-
document.addEventListener('visibilitychange', this.toggle.beacons)
|
|
403
|
+
if (navigator.userAgent.includes('Chrome') && typeof document.hidden != 'undefined')
|
|
404
|
+
document.addEventListener('visibilitychange', this.toggle.beacons)
|
|
381
405
|
},
|
|
382
406
|
|
|
383
407
|
deactivate() {
|
|
384
408
|
if (this.isActive) {
|
|
385
|
-
this.toggle.refreshFrame()
|
|
386
|
-
document.removeEventListener('visibilitychange', this.toggle.beacons)
|
|
387
|
-
clearTimeout(this.isActive); this.isActive = null
|
|
388
|
-
console.log(
|
|
389
|
-
} else
|
|
409
|
+
this.toggle.refreshFrame()
|
|
410
|
+
document.removeEventListener('visibilitychange', this.toggle.beacons)
|
|
411
|
+
clearTimeout(this.isActive) ; this.isActive = null
|
|
412
|
+
console.log(`↻ ChatGPT >> [${ chatgpt.autoRefresh.nowTimeStamp()}] Auto refresh de-activated`)
|
|
413
|
+
} else
|
|
414
|
+
console.log(`↻ ChatGPT >> [${chatgpt.autoRefresh.nowTimeStamp()}] Auto refresh already inactive!`)
|
|
390
415
|
},
|
|
391
416
|
|
|
392
417
|
nowTimeStamp() {
|
|
393
|
-
const now = new Date()
|
|
394
|
-
const hours = now.getHours() % 12 || 12
|
|
395
|
-
let minutes = now.getMinutes(), seconds = now.getSeconds()
|
|
396
|
-
if (minutes < 10) minutes = '0' + minutes; if (seconds < 10) seconds = '0' + seconds
|
|
397
|
-
const meridiem = now.getHours() < 12 ? 'AM' : 'PM'
|
|
398
|
-
return hours
|
|
418
|
+
const now = new Date()
|
|
419
|
+
const hours = now.getHours() % 12 || 12 // convert to 12h format
|
|
420
|
+
let minutes = now.getMinutes(), seconds = now.getSeconds()
|
|
421
|
+
if (minutes < 10) minutes = '0' + minutes; if (seconds < 10) seconds = '0' + seconds
|
|
422
|
+
const meridiem = now.getHours() < 12 ? 'AM' : 'PM'
|
|
423
|
+
return `${hours}:${minutes}:${seconds} ${meridiem}`
|
|
399
424
|
},
|
|
400
425
|
|
|
401
426
|
toggle: {
|
|
402
427
|
|
|
403
428
|
beacons() {
|
|
404
429
|
if (chatgpt.autoRefresh.beaconID) {
|
|
405
|
-
clearInterval(chatgpt.autoRefresh.beaconID); chatgpt.autoRefresh.beaconID = null
|
|
406
|
-
console.log(
|
|
430
|
+
clearInterval(chatgpt.autoRefresh.beaconID) ; chatgpt.autoRefresh.beaconID = null
|
|
431
|
+
console.log(`↻ ChatGPT >> [${chatgpt.autoRefresh.nowTimeStamp()}] Beacons de-activated`)
|
|
407
432
|
} else {
|
|
408
433
|
chatgpt.autoRefresh.beaconID = setInterval(() => {
|
|
409
|
-
navigator.sendBeacon('https://httpbin.org/post', new Uint8Array())
|
|
410
|
-
console.log(
|
|
411
|
-
}, 90000)
|
|
412
|
-
console.log(
|
|
434
|
+
navigator.sendBeacon('https://httpbin.org/post', new Uint8Array())
|
|
435
|
+
console.log(`↻ ChatGPT >> [${chatgpt.autoRefresh.nowTimeStamp()}] Beacon sent`)
|
|
436
|
+
}, 90000)
|
|
437
|
+
console.log(`ChatGPT >> [${chatgpt.autoRefresh.nowTimeStamp()}] Beacons activated`)
|
|
413
438
|
}
|
|
414
439
|
},
|
|
415
440
|
|
|
416
441
|
refreshFrame() {
|
|
417
|
-
let refreshFrame = document.querySelector('#refresh-frame')
|
|
418
|
-
if (refreshFrame) refreshFrame.remove()
|
|
442
|
+
let refreshFrame = document.querySelector('#refresh-frame')
|
|
443
|
+
if (refreshFrame) refreshFrame.remove()
|
|
419
444
|
else {
|
|
420
445
|
refreshFrame = Object.assign(document.createElement('iframe'),
|
|
421
|
-
{ id: 'refresh-frame', style: 'display: none' })
|
|
422
|
-
document.head.prepend(refreshFrame)
|
|
446
|
+
{ id: 'refresh-frame', style: 'display: none' })
|
|
447
|
+
document.head.prepend(refreshFrame)
|
|
423
448
|
}
|
|
424
449
|
}
|
|
425
450
|
}
|
|
@@ -427,167 +452,165 @@ const chatgpt = {
|
|
|
427
452
|
|
|
428
453
|
browser: {
|
|
429
454
|
|
|
430
|
-
isLightMode() { return window.matchMedia?.('(prefers-color-scheme: light)')?.matches
|
|
431
|
-
isDarkMode() { return window.matchMedia?.('(prefers-color-scheme: dark)')?.matches
|
|
432
|
-
isChromium() { return !!JSON.stringify(navigator.userAgentData?.brands)?.includes('Chromium')
|
|
433
|
-
isChrome() { return !!JSON.stringify(navigator.userAgentData?.brands)?.includes('Chrome')
|
|
434
|
-
isEdge() { return !!JSON.stringify(navigator.userAgentData?.brands)?.includes('Edge')
|
|
435
|
-
isBrave() { return !!JSON.stringify(navigator.userAgentData?.brands)?.includes('Brave')
|
|
436
|
-
isFirefox() { return navigator.userAgent.includes('Firefox')
|
|
455
|
+
isLightMode() { return window.matchMedia?.('(prefers-color-scheme: light)')?.matches },
|
|
456
|
+
isDarkMode() { return window.matchMedia?.('(prefers-color-scheme: dark)')?.matches },
|
|
457
|
+
isChromium() { return !!JSON.stringify(navigator.userAgentData?.brands)?.includes('Chromium') },
|
|
458
|
+
isChrome() { return !!JSON.stringify(navigator.userAgentData?.brands)?.includes('Chrome') },
|
|
459
|
+
isEdge() { return !!JSON.stringify(navigator.userAgentData?.brands)?.includes('Edge') },
|
|
460
|
+
isBrave() { return !!JSON.stringify(navigator.userAgentData?.brands)?.includes('Brave') },
|
|
461
|
+
isFirefox() { return navigator.userAgent.includes('Firefox') },
|
|
437
462
|
|
|
438
463
|
isFullScreen() {
|
|
439
|
-
const userAgentStr = navigator.userAgent
|
|
464
|
+
const userAgentStr = navigator.userAgent
|
|
440
465
|
return userAgentStr.includes('Chrome') ? window.matchMedia('(display-mode: fullscreen)').matches
|
|
441
466
|
: userAgentStr.includes('Firefox') ? window.fullScreen
|
|
442
|
-
: /MSIE|rv:/.test(userAgentStr) ? document.msFullscreenElement : document.webkitIsFullScreen
|
|
467
|
+
: /MSIE|rv:/.test(userAgentStr) ? document.msFullscreenElement : document.webkitIsFullScreen
|
|
443
468
|
},
|
|
444
469
|
|
|
445
470
|
isMobile() {
|
|
446
|
-
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
|
471
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) }
|
|
447
472
|
},
|
|
448
473
|
|
|
449
474
|
async clearChats() { // back-end method
|
|
450
|
-
return new Promise((resolve, reject) =>
|
|
475
|
+
return new Promise((resolve, reject) =>
|
|
451
476
|
chatgpt.getAccessToken().then(token => {
|
|
452
|
-
const xhr = new XMLHttpRequest()
|
|
453
|
-
xhr.open('PATCH', chatgpt.endpoints.openAI.chats, true)
|
|
454
|
-
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
455
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
477
|
+
const xhr = new XMLHttpRequest()
|
|
478
|
+
xhr.open('PATCH', chatgpt.endpoints.openAI.chats, true)
|
|
479
|
+
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
480
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
456
481
|
xhr.onload = () => {
|
|
457
|
-
if (xhr.status
|
|
458
|
-
console.info('Chats successfully cleared'); resolve()
|
|
459
|
-
}
|
|
460
|
-
xhr.send(JSON.stringify({ is_visible: false }))
|
|
461
|
-
}).catch(err => reject(new Error(err.message)))
|
|
462
|
-
|
|
482
|
+
if (xhr.status != 200) return reject('🤖 chatgpt.js >> Request failed. Cannot clear chats.')
|
|
483
|
+
console.info('Chats successfully cleared') ; resolve()
|
|
484
|
+
}
|
|
485
|
+
xhr.send(JSON.stringify({ is_visible: false }))
|
|
486
|
+
}).catch(err => reject(new Error(err.message)))
|
|
487
|
+
)
|
|
463
488
|
},
|
|
464
489
|
|
|
465
490
|
code: {
|
|
466
491
|
// Tip: Use template literals for easier passing of code arguments. Ensure backticks and `$`s are escaped (using `\`)
|
|
467
492
|
|
|
468
493
|
async execute(code) {
|
|
469
|
-
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
470
|
-
if (typeof code
|
|
471
|
-
chatgpt.send('Display the output as if you were terminal:\n\n' + code)
|
|
472
|
-
console.info('Executing code...')
|
|
473
|
-
await chatgpt.isIdle()
|
|
474
|
-
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
494
|
+
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
495
|
+
if (typeof code != 'string') return console.error('Code argument must be a string!')
|
|
496
|
+
chatgpt.send('Display the output as if you were terminal:\n\n' + code)
|
|
497
|
+
console.info('Executing code...')
|
|
498
|
+
await chatgpt.isIdle()
|
|
499
|
+
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
475
500
|
},
|
|
476
501
|
|
|
477
502
|
extract(msg) { // extract pure code from response (targets last block)
|
|
478
|
-
const codeBlocks = msg.match(/(?<=```.*\n)[\s\S]*?(?=```)/g)
|
|
479
|
-
return codeBlocks ? codeBlocks[codeBlocks.length - 1] : msg
|
|
503
|
+
const codeBlocks = msg.match(/(?<=```.*\n)[\s\S]*?(?=```)/g)
|
|
504
|
+
return codeBlocks ? codeBlocks[codeBlocks.length - 1] : msg
|
|
480
505
|
},
|
|
481
506
|
|
|
482
507
|
async isIdle(timeout = null) {
|
|
483
|
-
const obsConfig = { childList: true, subtree: true }
|
|
484
|
-
selectors = { msgDiv: 'div[data-message-author-role]',
|
|
485
|
-
replyDiv: 'div[data-message-author-role=assistant]' };
|
|
508
|
+
const obsConfig = { childList: true, subtree: true }
|
|
486
509
|
|
|
487
510
|
// Create promises
|
|
488
|
-
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
511
|
+
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
489
512
|
const isIdlePromise = (async () => {
|
|
490
513
|
await new Promise(resolve => { // when on convo page
|
|
491
|
-
if (document.querySelector(selectors.
|
|
514
|
+
if (document.querySelector(chatgpt.selectors.chatDivs.msg)) resolve()
|
|
492
515
|
else new MutationObserver((_, obs) => {
|
|
493
|
-
if (document.querySelector(selectors.
|
|
494
|
-
}).observe(document.body, obsConfig)
|
|
495
|
-
})
|
|
496
|
-
await new Promise(resolve =>
|
|
516
|
+
if (document.querySelector(chatgpt.selectors.chatDivs.msg)) { obs.disconnect() ; resolve() }
|
|
517
|
+
}).observe(document.body, obsConfig)
|
|
518
|
+
})
|
|
519
|
+
await new Promise(resolve => // when reply starts generating
|
|
497
520
|
new MutationObserver((_, obs) => {
|
|
498
|
-
if (chatgpt.getStopBtn()) { obs.disconnect(); resolve()
|
|
499
|
-
}).observe(document.body, { childList: true, subtree: true })
|
|
500
|
-
|
|
501
|
-
const replyDivs = document.querySelectorAll(selectors.
|
|
502
|
-
lastReplyDiv = replyDivs[replyDivs.length - 1]
|
|
503
|
-
await new Promise(resolve =>
|
|
521
|
+
if (chatgpt.getStopBtn()) { obs.disconnect() ; resolve() }
|
|
522
|
+
}).observe(document.body, { childList: true, subtree: true })
|
|
523
|
+
)
|
|
524
|
+
const replyDivs = document.querySelectorAll(chatgpt.selectors.chatDivs.reply),
|
|
525
|
+
lastReplyDiv = replyDivs[replyDivs.length - 1]
|
|
526
|
+
await new Promise(resolve => // when code starts generating
|
|
504
527
|
new MutationObserver((_, obs) => {
|
|
505
|
-
if (lastReplyDiv?.querySelector('pre')) { obs.disconnect(); resolve()
|
|
506
|
-
}).observe(document.body, obsConfig)
|
|
507
|
-
|
|
508
|
-
return new Promise(resolve =>
|
|
528
|
+
if (lastReplyDiv?.querySelector('pre')) { obs.disconnect() ; resolve() }
|
|
529
|
+
}).observe(document.body, obsConfig)
|
|
530
|
+
)
|
|
531
|
+
return new Promise(resolve => // when code stops generating
|
|
509
532
|
new MutationObserver((_, obs) => {
|
|
510
533
|
if (lastReplyDiv?.querySelector('pre')?.nextElementSibling // code block not last child of reply div
|
|
511
534
|
|| !chatgpt.getStopBtn() // ...or reply outright stopped generating
|
|
512
|
-
) { obs.disconnect(); resolve(true)
|
|
513
|
-
}).observe(document.body, obsConfig)
|
|
514
|
-
|
|
515
|
-
})()
|
|
535
|
+
) { obs.disconnect() ; resolve(true) }
|
|
536
|
+
}).observe(document.body, obsConfig)
|
|
537
|
+
)
|
|
538
|
+
})()
|
|
516
539
|
|
|
517
|
-
return await (timeoutPromise ? Promise.race([isIdlePromise, timeoutPromise]) : isIdlePromise)
|
|
540
|
+
return await (timeoutPromise ? Promise.race([isIdlePromise, timeoutPromise]) : isIdlePromise)
|
|
518
541
|
},
|
|
519
542
|
|
|
520
543
|
async minify(code) {
|
|
521
|
-
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
522
|
-
if (typeof code
|
|
523
|
-
chatgpt.send('Minify the following code:\n\n' + code)
|
|
524
|
-
console.info('Minifying code...')
|
|
525
|
-
await chatgpt.isIdle()
|
|
526
|
-
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
544
|
+
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
545
|
+
if (typeof code != 'string') return console.error('Code argument must be a string!')
|
|
546
|
+
chatgpt.send('Minify the following code:\n\n' + code)
|
|
547
|
+
console.info('Minifying code...')
|
|
548
|
+
await chatgpt.isIdle()
|
|
549
|
+
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
527
550
|
},
|
|
528
551
|
|
|
529
552
|
async obfuscate(code) {
|
|
530
|
-
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
531
|
-
if (typeof code
|
|
532
|
-
chatgpt.send('Obfuscate the following code:\n\n' + code)
|
|
533
|
-
console.info('Obfuscating code...')
|
|
534
|
-
await chatgpt.isIdle()
|
|
535
|
-
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
553
|
+
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
554
|
+
if (typeof code != 'string') return console.error('Code argument must be a string!')
|
|
555
|
+
chatgpt.send('Obfuscate the following code:\n\n' + code)
|
|
556
|
+
console.info('Obfuscating code...')
|
|
557
|
+
await chatgpt.isIdle()
|
|
558
|
+
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
536
559
|
},
|
|
537
560
|
|
|
538
561
|
async refactor(code, objective) {
|
|
539
|
-
if (!code) return console.error('Code (1st) argument not supplied. Pass some code!')
|
|
540
|
-
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i]
|
|
541
|
-
return console.error(`Argument ${ i + 1 } must be a string.`)
|
|
542
|
-
chatgpt.send(
|
|
543
|
-
console.info('Refactoring code...')
|
|
544
|
-
await chatgpt.isIdle()
|
|
545
|
-
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
562
|
+
if (!code) return console.error('Code (1st) argument not supplied. Pass some code!')
|
|
563
|
+
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] != 'string')
|
|
564
|
+
return console.error(`Argument ${ i + 1 } must be a string.`)
|
|
565
|
+
chatgpt.send(`Refactor the following code for ${ objective || 'brevity' }:\n\n${code}`)
|
|
566
|
+
console.info('Refactoring code...')
|
|
567
|
+
await chatgpt.isIdle()
|
|
568
|
+
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
546
569
|
},
|
|
547
570
|
|
|
548
571
|
async review(code) {
|
|
549
|
-
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
550
|
-
if (typeof code
|
|
551
|
-
chatgpt.send('Review the following code for me:\n\n' + code)
|
|
552
|
-
console.info('Reviewing code...')
|
|
553
|
-
await chatgpt.isIdle()
|
|
554
|
-
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
572
|
+
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
573
|
+
if (typeof code == 'string') return console.error('Code argument must be a string!')
|
|
574
|
+
chatgpt.send('Review the following code for me:\n\n' + code)
|
|
575
|
+
console.info('Reviewing code...')
|
|
576
|
+
await chatgpt.isIdle()
|
|
577
|
+
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
555
578
|
},
|
|
556
579
|
|
|
557
580
|
async unminify(code) {
|
|
558
|
-
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
559
|
-
if (typeof code
|
|
560
|
-
chatgpt.send('Unminify the following code.:\n\n' + code)
|
|
561
|
-
console.info('Unminifying code...')
|
|
562
|
-
await chatgpt.isIdle()
|
|
563
|
-
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
581
|
+
if (!code) return console.error('Code argument not supplied. Pass some code!')
|
|
582
|
+
if (typeof code != 'string') return console.error('Code argument must be a string!')
|
|
583
|
+
chatgpt.send('Unminify the following code.:\n\n' + code)
|
|
584
|
+
console.info('Unminifying code...')
|
|
585
|
+
await chatgpt.isIdle()
|
|
586
|
+
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
564
587
|
},
|
|
565
588
|
|
|
566
589
|
async write(prompt, outputLang) {
|
|
567
|
-
if (!prompt) return console.error('Prompt (1st) argument not supplied. Pass a prompt!')
|
|
568
|
-
if (!outputLang) return console.error('outputLang (2nd) argument not supplied. Pass a language!')
|
|
569
|
-
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i]
|
|
570
|
-
return console.error(`Argument ${ i + 1 } must be a string.`)
|
|
571
|
-
chatgpt.send(prompt
|
|
572
|
-
console.info('Writing code...')
|
|
573
|
-
await chatgpt.isIdle()
|
|
574
|
-
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
590
|
+
if (!prompt) return console.error('Prompt (1st) argument not supplied. Pass a prompt!')
|
|
591
|
+
if (!outputLang) return console.error('outputLang (2nd) argument not supplied. Pass a language!')
|
|
592
|
+
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] != 'string')
|
|
593
|
+
return console.error(`Argument ${ i + 1 } must be a string.`)
|
|
594
|
+
chatgpt.send(`${prompt}\n\nWrite this as code in ${outputLang}`)
|
|
595
|
+
console.info('Writing code...')
|
|
596
|
+
await chatgpt.isIdle()
|
|
597
|
+
return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'))
|
|
575
598
|
}
|
|
576
599
|
},
|
|
577
600
|
|
|
578
|
-
continue() { chatgpt.response.continue()
|
|
601
|
+
continue() { chatgpt.response.continue() },
|
|
579
602
|
|
|
580
603
|
async detectLanguage(text) {
|
|
581
|
-
if (!text) return console.error('Text argument not supplied. Pass some text!')
|
|
582
|
-
if (typeof text
|
|
583
|
-
chatgpt.send(
|
|
584
|
-
+ '\n\nOnly respond with the name of the language')
|
|
585
|
-
console.info('Reviewing text...')
|
|
586
|
-
await chatgpt.isIdle()
|
|
587
|
-
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
604
|
+
if (!text) return console.error('Text argument not supplied. Pass some text!')
|
|
605
|
+
if (typeof text != 'string') return console.error('Text argument must be a string!')
|
|
606
|
+
chatgpt.send(`Detect the language of the following text:\n\n${text}`
|
|
607
|
+
+ '\n\nOnly respond with the name of the language')
|
|
608
|
+
console.info('Reviewing text...')
|
|
609
|
+
await chatgpt.isIdle()
|
|
610
|
+
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
588
611
|
},
|
|
589
612
|
|
|
590
|
-
executeCode() { chatgpt.code.execute()
|
|
613
|
+
executeCode() { chatgpt.code.execute() },
|
|
591
614
|
|
|
592
615
|
async exportChat(chatToGet, format) {
|
|
593
616
|
// chatToGet = 'active' (default) | 'latest' | index|title|id of chat to get
|
|
@@ -597,12 +620,12 @@ const chatgpt = {
|
|
|
597
620
|
chatToGet = !chatToGet ? 'active' // default to 'active' if unpassed
|
|
598
621
|
: Number.isInteger(chatToGet) || /^\d+$/.test(chatToGet) ? // else if string/int num passed
|
|
599
622
|
parseInt(chatToGet, 10) // parse as integer
|
|
600
|
-
: chatToGet
|
|
601
|
-
format = format.toLowerCase() || 'html'
|
|
623
|
+
: chatToGet // else preserve non-num string as 'active', 'latest' or title/id of chat to get
|
|
624
|
+
format = format.toLowerCase() || 'html' // default to 'html' if unpassed
|
|
602
625
|
|
|
603
626
|
// Create transcript + filename
|
|
604
|
-
console.info('Generating transcript...')
|
|
605
|
-
let transcript = '', filename
|
|
627
|
+
console.info('Generating transcript...')
|
|
628
|
+
let transcript = '', filename
|
|
606
629
|
if (/te?xt/.test(format)) { // generate plain transcript + filename for TXT export
|
|
607
630
|
|
|
608
631
|
// Format filename using date/time
|
|
@@ -611,106 +634,104 @@ const chatgpt = {
|
|
|
611
634
|
month = (now.getMonth() + 1).toString().padStart(2, '0'),
|
|
612
635
|
year = now.getFullYear(),
|
|
613
636
|
hour = now.getHours().toString().padStart(2, '0'),
|
|
614
|
-
minute = now.getMinutes().toString().padStart(2, '0')
|
|
615
|
-
filename = `ChatGPT_${ day }-${ month }-${ year }_${ hour }-${ minute }.txt
|
|
637
|
+
minute = now.getMinutes().toString().padStart(2, '0')
|
|
638
|
+
filename = `ChatGPT_${ day }-${ month }-${ year }_${ hour }-${ minute }.txt`
|
|
616
639
|
|
|
617
640
|
// Create transcript from active chat
|
|
618
641
|
if (chatToGet == 'active' && /\/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/.test(window.location.href)) {
|
|
619
|
-
const chatDivs = document.querySelectorAll(
|
|
620
|
-
if (!chatDivs.length) return console.error('Chat is empty!')
|
|
621
|
-
const msgs = []; let isUserMsg = true
|
|
622
|
-
chatDivs.forEach(
|
|
623
|
-
const sender = isUserMsg ? 'USER' : 'CHATGPT'; isUserMsg = !isUserMsg
|
|
642
|
+
const chatDivs = document.querySelectorAll(chatgpt.selectors.chatDivs.convo)
|
|
643
|
+
if (!chatDivs.length) return console.error('Chat is empty!')
|
|
644
|
+
const msgs = [] ; let isUserMsg = true
|
|
645
|
+
chatDivs.forEach(div => {
|
|
646
|
+
const sender = isUserMsg ? 'USER' : 'CHATGPT'; isUserMsg = !isUserMsg
|
|
624
647
|
const msg = Array.from(div.childNodes).map(node => node.innerText)
|
|
625
648
|
.join('\n\n') // insert double line breaks between paragraphs
|
|
626
|
-
.replace('Copy code', '')
|
|
627
|
-
msgs.push(sender
|
|
628
|
-
})
|
|
629
|
-
transcript = msgs.join('\n\n')
|
|
649
|
+
.replace('Copy code', '')
|
|
650
|
+
msgs.push(`${sender}: ${msg}`)
|
|
651
|
+
})
|
|
652
|
+
transcript = msgs.join('\n\n')
|
|
630
653
|
|
|
631
654
|
// ...or from getChatData(chatToGet)
|
|
632
|
-
} else
|
|
633
|
-
for (const entry of await chatgpt.getChatData(chatToGet, 'msg', 'both', 'all'))
|
|
634
|
-
transcript += `USER: ${
|
|
635
|
-
transcript += `CHATGPT: ${ entry.chatgpt }\n\n`;
|
|
636
|
-
}}
|
|
655
|
+
} else
|
|
656
|
+
for (const entry of await chatgpt.getChatData(chatToGet, 'msg', 'both', 'all'))
|
|
657
|
+
transcript += `USER: ${entry.user}\n\nCHATGPT: ${entry.chatgpt}\n\n`
|
|
637
658
|
|
|
638
659
|
} else { // generate rich transcript + filename for HTML/MD/PDF export
|
|
639
660
|
|
|
640
661
|
// Fetch HTML transcript from OpenAI
|
|
641
662
|
const response = await fetch(await chatgpt.shareChat(chatToGet)),
|
|
642
|
-
htmlContent = await response.text()
|
|
663
|
+
htmlContent = await response.text()
|
|
643
664
|
|
|
644
665
|
// Format filename after <title>
|
|
645
666
|
const parser = new DOMParser(),
|
|
646
|
-
parsedHtml = parser.parseFromString(htmlContent, 'text/html')
|
|
647
|
-
filename = `${ parsedHtml.querySelector('title').textContent || 'ChatGPT conversation' }.html
|
|
667
|
+
parsedHtml = parser.parseFromString(htmlContent, 'text/html')
|
|
668
|
+
filename = `${ parsedHtml.querySelector('title').textContent || 'ChatGPT conversation' }.html`
|
|
648
669
|
|
|
649
670
|
// Convert relative CSS paths to absolute ones
|
|
650
|
-
const cssLinks = parsedHtml.querySelectorAll('link[rel=stylesheet]')
|
|
671
|
+
const cssLinks = parsedHtml.querySelectorAll('link[rel=stylesheet]')
|
|
651
672
|
cssLinks.forEach(link => {
|
|
652
|
-
const href = link.getAttribute('href')
|
|
653
|
-
if (href?.startsWith('/')) link.setAttribute('href', 'https://chat.openai.com' + href)
|
|
673
|
+
const href = link.getAttribute('href')
|
|
674
|
+
if (href?.startsWith('/')) link.setAttribute('href', 'https://chat.openai.com' + href)
|
|
654
675
|
});
|
|
655
676
|
|
|
656
677
|
// Serialize updated HTML to string
|
|
657
|
-
transcript = new XMLSerializer().serializeToString(parsedHtml)
|
|
678
|
+
transcript = new XMLSerializer().serializeToString(parsedHtml)
|
|
658
679
|
}
|
|
659
680
|
|
|
660
681
|
// Export transcript
|
|
661
|
-
console.info(`Exporting transcript as ${ format.toUpperCase() }...`)
|
|
682
|
+
console.info(`Exporting transcript as ${ format.toUpperCase() }...`)
|
|
662
683
|
if (format == 'pdf') { // convert SVGs + launch PDF printer
|
|
663
684
|
|
|
664
685
|
// Convert SVG icons to data URLs for proper PDF rendering
|
|
665
686
|
transcript = transcript.replace(/<svg.*?<\/svg>/g, (match) => {
|
|
666
|
-
const dataURL = 'data:image/svg+xml,' + encodeURIComponent(match)
|
|
667
|
-
return `<img src="${ dataURL }"
|
|
668
|
-
})
|
|
687
|
+
const dataURL = 'data:image/svg+xml,' + encodeURIComponent(match)
|
|
688
|
+
return `<img src="${ dataURL }">`
|
|
689
|
+
})
|
|
669
690
|
|
|
670
691
|
// Launch PDF printer
|
|
671
|
-
const transcriptPopup = window.open('', '', 'toolbar=0, location=0, menubar=0, height=600, width=800')
|
|
672
|
-
transcriptPopup.document.write(transcript)
|
|
673
|
-
setTimeout(() => { transcriptPopup.print({ toPDF: true })
|
|
692
|
+
const transcriptPopup = window.open('', '', 'toolbar=0, location=0, menubar=0, height=600, width=800')
|
|
693
|
+
transcriptPopup.document.write(transcript)
|
|
694
|
+
setTimeout(() => { transcriptPopup.print({ toPDF: true }) }, 100)
|
|
674
695
|
|
|
675
696
|
} else { // auto-save to file
|
|
676
697
|
|
|
677
698
|
if (format == 'md') { // remove extraneous HTML + fix file extension
|
|
678
|
-
const mdMatch = /<.*<h1(.|\n)*?href=".*?continue[^"]*".*?\/a>.*?<[^/]/.exec(transcript)[1]
|
|
679
|
-
transcript = mdMatch || transcript; filename = filename.replace('.html', '.md')
|
|
699
|
+
const mdMatch = /<.*<h1(.|\n)*?href=".*?continue[^"]*".*?\/a>.*?<[^/]/.exec(transcript)[1]
|
|
700
|
+
transcript = mdMatch || transcript; filename = filename.replace('.html', '.md')
|
|
680
701
|
}
|
|
681
702
|
const blob = new Blob([transcript],
|
|
682
|
-
{ type: 'text/' + ( format == 'html' ? 'html' : format == 'md' ? 'markdown' : 'plain' )})
|
|
683
|
-
const link = document.createElement('a'), blobURL = URL.createObjectURL(blob)
|
|
684
|
-
link.href = blobURL; link.download = filename; document.body.append(link)
|
|
685
|
-
link.click(); document.body.removeChild(link); URL.revokeObjectURL(blobURL)
|
|
703
|
+
{ type: 'text/' + ( format == 'html' ? 'html' : format == 'md' ? 'markdown' : 'plain' )})
|
|
704
|
+
const link = document.createElement('a'), blobURL = URL.createObjectURL(blob)
|
|
705
|
+
link.href = blobURL ; link.download = filename ; document.body.append(link)
|
|
706
|
+
link.click() ; document.body.removeChild(link) ; URL.revokeObjectURL(blobURL)
|
|
686
707
|
}
|
|
687
708
|
},
|
|
688
709
|
|
|
689
|
-
extractCode() { chatgpt.code.extract()
|
|
690
|
-
focusChatbar() { chatgpt.getChatBox()?.focus()
|
|
710
|
+
extractCode() { chatgpt.code.extract() },
|
|
711
|
+
focusChatbar() { chatgpt.getChatBox()?.focus() },
|
|
691
712
|
|
|
692
713
|
footer: {
|
|
693
|
-
get() { return document.querySelector(
|
|
714
|
+
get() { return document.querySelector(chatgpt.selectors.footer) },
|
|
694
715
|
|
|
695
716
|
hide() {
|
|
696
|
-
const footer = chatgpt.footer.get()
|
|
697
|
-
if (!footer) return console.error('Footer element not found!')
|
|
698
|
-
if (footer.style.visibility == 'hidden') return console.info('Footer already hidden!')
|
|
699
|
-
footer.style.display = 'none'
|
|
717
|
+
const footer = chatgpt.footer.get()
|
|
718
|
+
if (!footer) return console.error('Footer element not found!')
|
|
719
|
+
if (footer.style.visibility == 'hidden') return console.info('Footer already hidden!')
|
|
720
|
+
footer.style.display = 'none'
|
|
700
721
|
},
|
|
701
722
|
|
|
702
723
|
show() {
|
|
703
|
-
const footer = chatgpt.footer.get()
|
|
704
|
-
if (!footer) return console.error('Footer element not found!')
|
|
705
|
-
if (footer.style.visibility != 'hidden') return console.info('Footer already shown!')
|
|
724
|
+
const footer = chatgpt.footer.get()
|
|
725
|
+
if (!footer) return console.error('Footer element not found!')
|
|
726
|
+
if (footer.style.visibility != 'hidden') return console.info('Footer already shown!')
|
|
706
727
|
footer.style.display = 'inherit'
|
|
707
728
|
}
|
|
708
729
|
},
|
|
709
730
|
|
|
710
731
|
generateRandomIP() {
|
|
711
|
-
const ip = Array.from({length: 4}, () => Math.floor(chatgpt.randomFloat() * 256)).join('.')
|
|
712
|
-
console.info('IP generated: ' + ip)
|
|
713
|
-
return ip
|
|
732
|
+
const ip = Array.from({length: 4}, () => Math.floor(chatgpt.randomFloat() * 256)).join('.')
|
|
733
|
+
console.info('IP generated: ' + ip)
|
|
734
|
+
return ip
|
|
714
735
|
},
|
|
715
736
|
|
|
716
737
|
get(targetType, targetName = '') {
|
|
@@ -718,87 +739,83 @@ const chatgpt = {
|
|
|
718
739
|
// targetName = from get[targetName][targetType] methods, e.g. 'send'
|
|
719
740
|
|
|
720
741
|
// Validate argument types to be string only
|
|
721
|
-
if (typeof targetType
|
|
722
|
-
throw new TypeError('Invalid arguments. Both arguments must be strings.')
|
|
742
|
+
if (typeof targetType != 'string' || typeof targetName != 'string')
|
|
743
|
+
throw new TypeError('Invalid arguments. Both arguments must be strings.')
|
|
723
744
|
|
|
724
745
|
// Validate targetType
|
|
725
|
-
if (!cjsTargetTypes.includes(targetType.toLowerCase()))
|
|
726
|
-
throw new Error(
|
|
727
|
-
+ '. Valid values are: ' + JSON.stringify(cjsTargetTypes)); }
|
|
746
|
+
if (!cjsTargetTypes.includes(targetType.toLowerCase()))
|
|
747
|
+
throw new Error(`Invalid targetType: ${targetType}. Valid values are: ${JSON.stringify(cjsTargetTypes)}`)
|
|
728
748
|
|
|
729
749
|
// Validate targetName scoped to pre-validated targetType
|
|
730
|
-
const targetNames = [], reTargetName = new RegExp(
|
|
750
|
+
const targetNames = [], reTargetName = new RegExp(`^get(.*)${targetType}$`, 'i')
|
|
731
751
|
for (const prop in chatgpt) {
|
|
732
752
|
if (typeof chatgpt[prop] == 'function' && reTargetName.test(prop)) {
|
|
733
753
|
targetNames.push( // add found targetName to valid array
|
|
734
|
-
prop.replace(reTargetName, '$1').toLowerCase())
|
|
754
|
+
prop.replace(reTargetName, '$1').toLowerCase())
|
|
735
755
|
}}
|
|
736
|
-
if (!targetNames.includes(targetName.toLowerCase()))
|
|
737
|
-
throw new Error(
|
|
756
|
+
if (!targetNames.includes(targetName.toLowerCase()))
|
|
757
|
+
throw new Error(`Invalid targetName: ${targetName}. `
|
|
738
758
|
+ (targetNames.length > 0 ? 'Valid values are: ' + JSON.stringify(targetNames)
|
|
739
|
-
: 'targetType ' + targetType.toLowerCase() + ' does not require additional options.'))
|
|
740
|
-
}
|
|
759
|
+
: 'targetType ' + targetType.toLowerCase() + ' does not require additional options.'))
|
|
741
760
|
|
|
742
761
|
// Call target function using pre-validated name components
|
|
743
|
-
const targetFuncNameLower = ('get' + targetName + targetType).toLowerCase()
|
|
762
|
+
const targetFuncNameLower = ('get' + targetName + targetType).toLowerCase()
|
|
744
763
|
const targetFuncName = Object.keys(this).find( // find originally cased target function name
|
|
745
|
-
(name) => { return name.toLowerCase() == targetFuncNameLower
|
|
746
|
-
return this[targetFuncName]()
|
|
764
|
+
(name) => { return name.toLowerCase() == targetFuncNameLower }) // test for match
|
|
765
|
+
return this[targetFuncName]() // call found function
|
|
747
766
|
},
|
|
748
767
|
|
|
749
768
|
getAccessToken() {
|
|
750
769
|
return new Promise((resolve, reject) => {
|
|
751
|
-
if (
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
xhr.
|
|
756
|
-
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
770
|
+
if (chatgpt.accessToken && (Date.parse(chatgpt.accessToken.expireDate) - Date.parse(new Date()) >= 0))
|
|
771
|
+
return resolve(chatgpt.accessToken.token) // unexpired one exists already
|
|
772
|
+
const xhr = new XMLHttpRequest()
|
|
773
|
+
xhr.open('GET', chatgpt.endpoints.openAI.session, true)
|
|
774
|
+
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
757
775
|
xhr.onload = () => {
|
|
758
|
-
if (xhr.status
|
|
759
|
-
console.info(
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
expireDate: JSON.parse(xhr.responseText).expires
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
});
|
|
776
|
+
if (xhr.status != 200) return reject('🤖 chatgpt.js >> Request failed. Cannot retrieve access token.')
|
|
777
|
+
console.info(`Token expiration: ${
|
|
778
|
+
new Date(JSON.parse(xhr.responseText).expires).toLocaleString().replace(',', ' at')}`)
|
|
779
|
+
chatgpt.accessToken = {
|
|
780
|
+
token: JSON.parse(xhr.responseText).accessToken, expireDate: JSON.parse(xhr.responseText).expires }
|
|
781
|
+
resolve(chatgpt.accessToken.token)
|
|
782
|
+
}
|
|
783
|
+
xhr.send()
|
|
784
|
+
})
|
|
768
785
|
},
|
|
769
786
|
|
|
770
787
|
getAccountDetails(...details) {
|
|
771
788
|
// details = [email|id|image|name|picture] = optional
|
|
772
789
|
|
|
773
790
|
// Build details array
|
|
774
|
-
const validDetails = [ 'email', 'id', 'image', 'name', 'picture' ]
|
|
791
|
+
const validDetails = [ 'email', 'id', 'image', 'name', 'picture' ]
|
|
775
792
|
details = ( !arguments[0] ? validDetails // no details passed, populate w/ all valid ones
|
|
776
793
|
: Array.isArray(arguments[0]) ? arguments[0] // details array passed, do nothing
|
|
777
|
-
: Array.from(arguments) )
|
|
794
|
+
: Array.from(arguments) ) // details arg(s) passed, convert to array
|
|
778
795
|
|
|
779
796
|
// Validate detail args
|
|
780
|
-
for (const detail of details)
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
+
|
|
797
|
+
for (const detail of details) if (!validDetails.includes(detail))
|
|
798
|
+
return console.error(
|
|
799
|
+
`Invalid detail arg '${detail}' supplied. Valid details are:\n`
|
|
800
|
+
+ ` [${validDetails}]`)
|
|
784
801
|
|
|
785
802
|
// Return account details
|
|
786
803
|
return new Promise((resolve, reject) => {
|
|
787
|
-
const xhr = new XMLHttpRequest()
|
|
788
|
-
xhr.open('GET', chatgpt.endpoints.openAI.session, true)
|
|
789
|
-
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
804
|
+
const xhr = new XMLHttpRequest()
|
|
805
|
+
xhr.open('GET', chatgpt.endpoints.openAI.session, true)
|
|
806
|
+
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
790
807
|
xhr.onload = () => {
|
|
791
|
-
if (xhr.status
|
|
792
|
-
const data = JSON.parse(xhr.responseText).user, detailsToReturn = {}
|
|
793
|
-
for (const detail of details) detailsToReturn[detail] = data[detail]
|
|
794
|
-
return resolve(detailsToReturn)
|
|
795
|
-
} else return reject('🤖 chatgpt.js >> Request failed. Cannot retrieve account details.')
|
|
796
|
-
}
|
|
797
|
-
xhr.send()
|
|
798
|
-
})
|
|
808
|
+
if (xhr.status == 200) {
|
|
809
|
+
const data = JSON.parse(xhr.responseText).user, detailsToReturn = {}
|
|
810
|
+
for (const detail of details) detailsToReturn[detail] = data[detail]
|
|
811
|
+
return resolve(detailsToReturn)
|
|
812
|
+
} else return reject('🤖 chatgpt.js >> Request failed. Cannot retrieve account details.')
|
|
813
|
+
}
|
|
814
|
+
xhr.send()
|
|
815
|
+
})
|
|
799
816
|
},
|
|
800
817
|
|
|
801
|
-
getChatBox() { return document.getElementById('prompt-textarea')
|
|
818
|
+
getChatBox() { return document.getElementById('prompt-textarea') },
|
|
802
819
|
|
|
803
820
|
getChatData(chatToGet = 1, detailsToGet = 'all', sender = 'all', msgToGet = 'all') {
|
|
804
821
|
// chatToGet = 'active' | 'latest' | index|title|id of chat to get (defaults to active OpenAI chat > latest chat)
|
|
@@ -807,167 +824,164 @@ const chatgpt = {
|
|
|
807
824
|
// msgToGet = 'all' | 'latest' | index of msg to get (defaults to 'all', requires 2nd param = 'msg')
|
|
808
825
|
|
|
809
826
|
// Init args
|
|
810
|
-
const validDetails = [ 'all', 'id', 'title', 'create_time', 'update_time', 'msg' ]
|
|
811
|
-
const validSenders = [ 'all', 'both', 'user', 'chatgpt' ]
|
|
827
|
+
const validDetails = [ 'all', 'id', 'title', 'create_time', 'update_time', 'msg' ]
|
|
828
|
+
const validSenders = [ 'all', 'both', 'user', 'chatgpt' ]
|
|
812
829
|
chatToGet = !chatToGet ? 'active' // if '' passed, set to active
|
|
813
830
|
: Number.isInteger(chatToGet) || /^\d+$/.test(chatToGet) ? // else if string/int num passed
|
|
814
|
-
( parseInt(chatToGet, 10)
|
|
815
|
-
: chatToGet
|
|
831
|
+
( parseInt(chatToGet, 10) == 0 ? 0 : parseInt(chatToGet, 10) - 1 ) // ...offset -1 or keep as 0
|
|
832
|
+
: chatToGet // else preserve non-num string as 'active', 'latest' or title/id of chat to get
|
|
816
833
|
detailsToGet = ['all', ''].includes(detailsToGet) ? // if '' or 'all' passed
|
|
817
834
|
validDetails.filter(detail => /^(?!all$|msg$).*/.test(detail)) // populate w/ [validDetails] except 'all' & 'msg'
|
|
818
|
-
: Array.isArray(detailsToGet) ? detailsToGet : [detailsToGet]
|
|
835
|
+
: Array.isArray(detailsToGet) ? detailsToGet : [detailsToGet] // else convert to array if needed
|
|
819
836
|
sender = !sender ? 'all' // if '' or unpassed, set to 'all'
|
|
820
|
-
: validSenders.includes(sender) ? sender : 'invalid'
|
|
837
|
+
: validSenders.includes(sender) ? sender : 'invalid' // else set to validSenders or 'invalid'
|
|
821
838
|
msgToGet = Number.isInteger(msgToGet) || /^\d+$/.test(msgToGet) ? // if string/int num passed
|
|
822
|
-
( parseInt(msgToGet, 10)
|
|
839
|
+
( parseInt(msgToGet, 10) == 0 ? 0 : parseInt(msgToGet, 10) - 1 ) // ...offset -1 or keep as 0
|
|
823
840
|
: ['all', 'latest'].includes(msgToGet.toLowerCase()) ? // else if 'all' or 'latest' passed
|
|
824
841
|
msgToGet.toLowerCase() // ...preserve it
|
|
825
842
|
: !msgToGet ? 'all' // else if '', set to 'all'
|
|
826
|
-
: 'invalid'
|
|
843
|
+
: 'invalid' // else set 'invalid' for validation step
|
|
827
844
|
|
|
828
845
|
// Validate args
|
|
829
|
-
for (const detail of detailsToGet)
|
|
830
|
-
if (!validDetails.includes(detail))
|
|
831
|
-
|
|
832
|
-
+
|
|
833
|
-
if (sender == 'invalid')
|
|
846
|
+
for (const detail of detailsToGet)
|
|
847
|
+
if (!validDetails.includes(detail)) return console.error(
|
|
848
|
+
`Invalid detail arg '${detail}' passed. Valid details are:\n`
|
|
849
|
+
+ ` [${validDetails}]`)
|
|
850
|
+
if (sender == 'invalid') return console.error(
|
|
834
851
|
'Invalid sender arg passed. Valid senders are:\n'
|
|
835
|
-
+
|
|
836
|
-
if (msgToGet == 'invalid')
|
|
837
|
-
|
|
838
|
-
+
|
|
852
|
+
+ ` [${validSenders}]`)
|
|
853
|
+
if (msgToGet == 'invalid') return console.error(
|
|
854
|
+
`Invalid msgToGet arg passed. Valid msg's to get are:\n`
|
|
855
|
+
+ ` [ 'all' | 'latest' | index of msg to get ]`)
|
|
839
856
|
|
|
840
857
|
const getChatDetails = (token, detailsToGet) => {
|
|
841
|
-
const re_chatID = /\w{8}-\w{4}-\w{4}-\w{4}-\w{12}
|
|
858
|
+
const re_chatID = /\w{8}-\w{4}-\w{4}-\w{4}-\w{12}/
|
|
842
859
|
return new Promise((resolve, reject) => {
|
|
843
|
-
const xhr = new XMLHttpRequest()
|
|
844
|
-
xhr.open('GET', chatgpt.endpoints.openAI.chats, true)
|
|
845
|
-
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
846
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
860
|
+
const xhr = new XMLHttpRequest()
|
|
861
|
+
xhr.open('GET', chatgpt.endpoints.openAI.chats, true)
|
|
862
|
+
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
863
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
847
864
|
xhr.onload = () => {
|
|
848
|
-
if (xhr.status
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
865
|
+
if (xhr.status != 200)
|
|
866
|
+
return reject('🤖 chatgpt.js >> Request failed. Cannot retrieve chat details.')
|
|
867
|
+
const data = JSON.parse(xhr.responseText).items
|
|
868
|
+
if (data.length <= 0) return reject('🤖 chatgpt.js >> Chat list is empty.')
|
|
869
|
+
const detailsToReturn = {}
|
|
852
870
|
|
|
853
871
|
// Return by index if num, 'latest', or 'active' passed but not truly active
|
|
854
872
|
if (Number.isInteger(chatToGet) || chatToGet == 'latest' ||
|
|
855
|
-
(chatToGet == 'active' && !new RegExp(
|
|
856
|
-
chatToGet = Number.isInteger(chatToGet) ? chatToGet : 0
|
|
857
|
-
if (chatToGet > data.length)
|
|
858
|
-
return reject(
|
|
859
|
-
+
|
|
860
|
-
for (const detail of detailsToGet) detailsToReturn[detail] = data[chatToGet][detail]
|
|
861
|
-
return resolve(detailsToReturn)
|
|
873
|
+
(chatToGet == 'active' && !new RegExp(`\/${re_chatID.source}$`).test(location.href))) {
|
|
874
|
+
chatToGet = Number.isInteger(chatToGet) ? chatToGet : 0 // preserve index, otherwise get latest
|
|
875
|
+
if (chatToGet > data.length) // reject if index out-of-bounds
|
|
876
|
+
return reject(`🤖 chatgpt.js >> Chat with index ${ chatToGet + 1 }`
|
|
877
|
+
+ ` is out of bounds. Only ${data.length} chats exist!`)
|
|
878
|
+
for (const detail of detailsToGet) detailsToReturn[detail] = data[chatToGet][detail]
|
|
879
|
+
return resolve(detailsToReturn)
|
|
862
880
|
}
|
|
863
881
|
|
|
864
882
|
// Return by title, ID or active chat
|
|
865
883
|
const chatIdentifier = ( // determine to check by ID or title
|
|
866
|
-
chatToGet == 'active' ||
|
|
884
|
+
chatToGet == 'active' ||
|
|
885
|
+
new RegExp(`^${re_chatID.source}$`).test(chatToGet) ? 'id' : 'title' )
|
|
867
886
|
if (chatToGet == 'active') // replace chatToGet w/ actual ID
|
|
868
|
-
chatToGet = re_chatID.exec(window.location.href)[0]
|
|
869
|
-
let idx, chatFound
|
|
870
|
-
for (idx = 0; idx < data.length; idx++) { // search for id/title to set chatFound flag
|
|
871
|
-
if (data[idx][chatIdentifier] == chatToGet) { chatFound = true; break
|
|
887
|
+
chatToGet = re_chatID.exec(window.location.href)[0]
|
|
888
|
+
let idx, chatFound // index of potentially found chat, flag if found
|
|
889
|
+
for (idx = 0 ; idx < data.length ; idx++) { // search for id/title to set chatFound flag
|
|
890
|
+
if (data[idx][chatIdentifier] == chatToGet) { chatFound = true ; break }}
|
|
872
891
|
if (!chatFound) // exit
|
|
873
|
-
return reject(
|
|
874
|
-
for (const detail of detailsToGet) detailsToReturn[detail] = data[idx][detail]
|
|
875
|
-
return resolve(detailsToReturn)
|
|
876
|
-
}
|
|
877
|
-
xhr.send()
|
|
878
|
-
})
|
|
892
|
+
return reject(`🤖 chatgpt.js >> No chat with ${chatIdentifier} = ${chatToGet} found.`)
|
|
893
|
+
for (const detail of detailsToGet) detailsToReturn[detail] = data[idx][detail]
|
|
894
|
+
return resolve(detailsToReturn)
|
|
895
|
+
}
|
|
896
|
+
xhr.send()
|
|
897
|
+
})}
|
|
879
898
|
|
|
880
899
|
const getChatMsgs = token => {
|
|
881
900
|
return new Promise((resolve, reject) => {
|
|
882
|
-
const xhr = new XMLHttpRequest()
|
|
901
|
+
const xhr = new XMLHttpRequest()
|
|
883
902
|
getChatDetails(token, ['id']).then(chat => {
|
|
884
|
-
xhr.open('GET', `${chatgpt.endpoints.openAI.chat}/${chat.id}`, true)
|
|
885
|
-
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
886
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
903
|
+
xhr.open('GET', `${chatgpt.endpoints.openAI.chat}/${chat.id}`, true)
|
|
904
|
+
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
905
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
887
906
|
xhr.onload = () => {
|
|
888
|
-
if (xhr.status
|
|
907
|
+
if (xhr.status != 200)
|
|
908
|
+
return reject('🤖 chatgpt.js >> Request failed. Cannot retrieve chat messages.')
|
|
889
909
|
|
|
890
910
|
// Init const's
|
|
891
|
-
const data = JSON.parse(xhr.responseText).mapping
|
|
892
|
-
const userMessages = [], chatGPTMessages = [], msgsToReturn = []
|
|
911
|
+
const data = JSON.parse(xhr.responseText).mapping // get chat messages
|
|
912
|
+
const userMessages = [], chatGPTMessages = [], msgsToReturn = []
|
|
893
913
|
|
|
894
914
|
// Fill [userMessages]
|
|
895
915
|
for (const key in data)
|
|
896
916
|
if (data[key].message != null && data[key].message.author.role == 'user')
|
|
897
|
-
userMessages.push({ id: data[key].id, msg: data[key].message })
|
|
898
|
-
userMessages.sort((a, b) => a.msg.create_time - b.msg.create_time)
|
|
917
|
+
userMessages.push({ id: data[key].id, msg: data[key].message })
|
|
918
|
+
userMessages.sort((a, b) => a.msg.create_time - b.msg.create_time) // sort in chronological order
|
|
899
919
|
|
|
900
920
|
if (parseInt(msgToGet, 10) + 1 > userMessages.length) // reject if index out of bounds
|
|
901
|
-
return reject(
|
|
902
|
-
+
|
|
921
|
+
return reject(`🤖 chatgpt.js >> Message/response with index ${ msgToGet + 1 }`
|
|
922
|
+
+ ` is out of bounds. Only ${userMessages.length} messages/responses exist!`)
|
|
903
923
|
|
|
904
924
|
// Fill [chatGPTMessages]
|
|
905
925
|
for (const userMessage of userMessages) {
|
|
906
|
-
let sub = []
|
|
926
|
+
let sub = []
|
|
907
927
|
for (const key in data) {
|
|
908
|
-
if (data[key].message != null && data[key].message.author.role == 'assistant'
|
|
909
|
-
|
|
910
|
-
|
|
928
|
+
if (data[key].message != null && data[key].message.author.role == 'assistant'
|
|
929
|
+
&& data[key].parent == userMessage.id)
|
|
930
|
+
sub.push(data[key].message)
|
|
911
931
|
}
|
|
912
|
-
sub.sort((a, b) => a.create_time - b.create_time)
|
|
932
|
+
sub.sort((a, b) => a.create_time - b.create_time) // sort in chronological order
|
|
913
933
|
sub = sub.map(x => { // pull out msgs after sorting
|
|
914
934
|
switch(x.content.content_type) {
|
|
915
|
-
case 'code': return x.content.text
|
|
916
|
-
case 'text': return x.content.parts[0]
|
|
917
|
-
default: return
|
|
935
|
+
case 'code': return x.content.text
|
|
936
|
+
case 'text': return x.content.parts[0]
|
|
937
|
+
default: return
|
|
918
938
|
}
|
|
919
|
-
})
|
|
920
|
-
sub = sub.length
|
|
921
|
-
chatGPTMessages.push(sub)
|
|
939
|
+
})
|
|
940
|
+
sub = sub.length == 1 ? sub[0] : sub // convert not regenerated responses to strings
|
|
941
|
+
chatGPTMessages.push(sub) // array of arrays (length > 1 = regenerated responses)
|
|
922
942
|
}
|
|
923
943
|
|
|
924
944
|
if (sender == 'user') // Fill [msgsToReturn] with user messages
|
|
925
945
|
for (const userMessage in userMessages)
|
|
926
|
-
msgsToReturn.push(userMessages[userMessage].msg.content.parts[0])
|
|
946
|
+
msgsToReturn.push(userMessages[userMessage].msg.content.parts[0])
|
|
927
947
|
else if (sender == 'chatgpt') // Fill [msgsToReturn] with ChatGPT responses
|
|
928
948
|
for (const chatGPTMessage of chatGPTMessages)
|
|
929
949
|
msgsToReturn.push(msgToGet == 'latest' ? chatGPTMessages[chatGPTMessages.length - 1] : chatGPTMessage );
|
|
930
950
|
else { // Fill [msgsToReturn] with objects of user messages and chatgpt response(s)
|
|
931
|
-
let i = 0
|
|
951
|
+
let i = 0
|
|
932
952
|
for (const message in userMessages) {
|
|
933
953
|
msgsToReturn.push({
|
|
934
954
|
user: userMessages[message].msg.content.parts[0],
|
|
935
955
|
chatgpt: msgToGet == 'latest' ? chatGPTMessages[i][chatGPTMessages[i].length - 1] : chatGPTMessages[i]
|
|
936
|
-
})
|
|
937
|
-
i
|
|
956
|
+
})
|
|
957
|
+
i++
|
|
938
958
|
}
|
|
939
959
|
}
|
|
940
960
|
return resolve(msgToGet == 'all' ? msgsToReturn // if 'all' passed, return array
|
|
941
961
|
: msgToGet == 'latest' ? msgsToReturn[msgsToReturn.length - 1] // else if 'latest' passed, return latest
|
|
942
|
-
: msgsToReturn[msgToGet] )
|
|
943
|
-
}
|
|
944
|
-
xhr.send()
|
|
945
|
-
})
|
|
962
|
+
: msgsToReturn[msgToGet] ) // else return element of array
|
|
963
|
+
}
|
|
964
|
+
xhr.send()
|
|
965
|
+
})})}
|
|
946
966
|
|
|
947
967
|
// Return chat data
|
|
948
968
|
return new Promise(resolve => chatgpt.getAccessToken().then(token => {
|
|
949
969
|
return resolve(detailsToGet.includes('msg') ? getChatMsgs(token)
|
|
950
|
-
: getChatDetails(token, detailsToGet))
|
|
951
|
-
}))
|
|
970
|
+
: getChatDetails(token, detailsToGet))
|
|
971
|
+
}))
|
|
952
972
|
},
|
|
953
973
|
|
|
954
|
-
getChatInput() { return chatgpt.getChatBox().firstChild.innerText
|
|
955
|
-
getContinueButton() { return document.querySelector(
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
+ 'button:has([d^="M15.6729"])' // Pencil icon (recorded chat mode)
|
|
966
|
-
)
|
|
967
|
-
},
|
|
968
|
-
|
|
969
|
-
getNewChatLink() { return document.querySelector('nav a[href="/"]'); },
|
|
970
|
-
getRegenerateButton() { return document.querySelector('button:has([d^="M3.06957"])'); },
|
|
974
|
+
getChatInput() { return chatgpt.getChatBox().firstChild.innerText },
|
|
975
|
+
getContinueButton() { return document.querySelector(chatgpt.selectors.btns.continue) },
|
|
976
|
+
getErrorMsg() { return document.querySelector(`${chatgpt.selectors.errors.txt}:last-of-type`)?.innerText },
|
|
977
|
+
getFooterDiv() { return chatgpt.footer.get() },
|
|
978
|
+
getHeaderDiv() { return chatgpt.header.get() },
|
|
979
|
+
getLastPrompt() { return chatgpt.getChatData('active', 'msg', 'user', 'latest') },
|
|
980
|
+
getLastResponse() { return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest') },
|
|
981
|
+
getLoginButton() { return document.querySelector(chatgpt.selectors.btns.login) },
|
|
982
|
+
getNewChatButton() { return document.querySelector(chatgpt.selectors.btns.newChat) },
|
|
983
|
+
getNewChatLink() { return document.querySelector(chatgpt.selectors.links.newChat) },
|
|
984
|
+
getRegenerateButton() { return document.querySelector(chatgpt.selectors.btns.regen) },
|
|
971
985
|
|
|
972
986
|
getResponse() {
|
|
973
987
|
// * Returns response via DOM by index arg if OpenAI chat page is active, otherwise uses API w/ following args:
|
|
@@ -975,36 +989,38 @@ const chatgpt = {
|
|
|
975
989
|
// responseToGet = index of response to get (defaults to latest if '' unpassed)
|
|
976
990
|
// regenResponseToGet = index of regenerated response to get (defaults to latest if '' unpassed)
|
|
977
991
|
|
|
978
|
-
return chatgpt.response.get(...arguments)
|
|
992
|
+
return chatgpt.response.get(...arguments)
|
|
979
993
|
},
|
|
980
994
|
|
|
981
|
-
getResponseFromAPI(chatToGet, responseToGet) { return chatgpt.response.getFromAPI(chatToGet, responseToGet)
|
|
982
|
-
getResponseFromDOM(pos) { return chatgpt.response.getFromDOM(pos)
|
|
983
|
-
getScrollToBottomButton() { return document.querySelector(
|
|
984
|
-
getSendButton() { return document.querySelector(
|
|
985
|
-
getStopButton() { return document.querySelector(
|
|
995
|
+
getResponseFromAPI(chatToGet, responseToGet) { return chatgpt.response.getFromAPI(chatToGet, responseToGet) },
|
|
996
|
+
getResponseFromDOM(pos) { return chatgpt.response.getFromDOM(pos) },
|
|
997
|
+
getScrollToBottomButton() { return document.querySelector(chatgpt.selectors.btns.scroll) },
|
|
998
|
+
getSendButton() { return document.querySelector(chatgpt.selectors.btns.send) },
|
|
999
|
+
getStopButton() { return document.querySelector(chatgpt.selectors.btns.stop) },
|
|
986
1000
|
|
|
987
1001
|
getUserLanguage() {
|
|
988
|
-
return navigator.languages[0] || navigator.language || navigator.browserLanguage
|
|
989
|
-
navigator.systemLanguage || navigator.userLanguage || ''
|
|
1002
|
+
return navigator.languages[0] || navigator.language || navigator.browserLanguage
|
|
1003
|
+
|| navigator.systemLanguage || navigator.userLanguage || ''
|
|
990
1004
|
},
|
|
991
1005
|
|
|
1006
|
+
getVoiceButton() { return document.querySelector(chatgpt.selectors.btns.voice) },
|
|
1007
|
+
|
|
992
1008
|
header: {
|
|
993
|
-
get() { return document.querySelector(
|
|
994
|
-
hide() { chatgpt.header.get().style.display = 'none'
|
|
995
|
-
show() { chatgpt.header.get().style.display = 'flex'
|
|
1009
|
+
get() { return document.querySelector(chatgpt.selectors.header) },
|
|
1010
|
+
hide() { chatgpt.header.get().style.display = 'none' },
|
|
1011
|
+
show() { chatgpt.header.get().style.display = 'flex' }
|
|
996
1012
|
},
|
|
997
1013
|
|
|
998
|
-
hideFooter() { chatgpt.footer.hide()
|
|
999
|
-
hideHeader() { chatgpt.header.hide()
|
|
1014
|
+
hideFooter() { chatgpt.footer.hide() },
|
|
1015
|
+
hideHeader() { chatgpt.header.hide() },
|
|
1000
1016
|
|
|
1001
1017
|
history: {
|
|
1002
1018
|
async isLoaded(timeout = null) {
|
|
1003
1019
|
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
1004
1020
|
const isLoadedPromise = new Promise(resolve => {
|
|
1005
|
-
if (document.querySelector(
|
|
1021
|
+
if (document.querySelector(chatgpt.selectors.chatHistory)) resolve(true)
|
|
1006
1022
|
else new MutationObserver((_, obs) => {
|
|
1007
|
-
if (document.querySelector(
|
|
1023
|
+
if (document.querySelector(chatgpt.selectors.chatHistory)) { obs.disconnect() ; resolve(true) }
|
|
1008
1024
|
}).observe(document.documentElement, { childList: true, subtree: true })
|
|
1009
1025
|
})
|
|
1010
1026
|
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
@@ -1015,156 +1031,149 @@ const chatgpt = {
|
|
|
1015
1031
|
// NOTE: DOM is not updated to reflect new instructions added/removed or toggle state (until session refresh)
|
|
1016
1032
|
|
|
1017
1033
|
add(instruction, target) {
|
|
1018
|
-
if (!instruction) return console.error('Please provide an instruction')
|
|
1019
|
-
if (typeof instruction
|
|
1020
|
-
const validTargets = ['user', 'chatgpt']
|
|
1021
|
-
if (!target) return console.error('Please provide a valid target!')
|
|
1022
|
-
if (typeof target
|
|
1023
|
-
target = target.toLowerCase()
|
|
1034
|
+
if (!instruction) return console.error('Please provide an instruction')
|
|
1035
|
+
if (typeof instruction != 'string') return console.error('Instruction must be a string')
|
|
1036
|
+
const validTargets = ['user', 'chatgpt'] // valid targets
|
|
1037
|
+
if (!target) return console.error('Please provide a valid target!')
|
|
1038
|
+
if (typeof target != 'string') return console.error('Target must be a string')
|
|
1039
|
+
target = target.toLowerCase() // lowercase target
|
|
1024
1040
|
if (!validTargets.includes(target))
|
|
1025
|
-
return console.error(`Invalid target ${target}. Valid targets are [${validTargets}]`)
|
|
1041
|
+
return console.error(`Invalid target ${target}. Valid targets are [${validTargets}]`)
|
|
1026
1042
|
|
|
1027
|
-
instruction = `\n\n${instruction}
|
|
1043
|
+
instruction = `\n\n${instruction}` // add 2 newlines to the new instruction
|
|
1028
1044
|
|
|
1029
1045
|
return new Promise(resolve => {
|
|
1030
1046
|
chatgpt.getAccessToken().then(async token => {
|
|
1031
|
-
const instructionsData = await this.fetchData()
|
|
1047
|
+
const instructionsData = await this.fetchData()
|
|
1032
1048
|
|
|
1033
1049
|
// Concatenate old instructions with new instruction
|
|
1034
|
-
if (target == 'user') instructionsData.about_user_message += instruction
|
|
1035
|
-
else if (target == 'chatgpt') instructionsData.about_model_message += instruction
|
|
1050
|
+
if (target == 'user') instructionsData.about_user_message += instruction
|
|
1051
|
+
else if (target == 'chatgpt') instructionsData.about_model_message += instruction
|
|
1036
1052
|
|
|
1037
|
-
await this.sendRequest('POST', token, instructionsData)
|
|
1053
|
+
await this.sendRequest('POST', token, instructionsData)
|
|
1038
1054
|
return resolve();
|
|
1039
1055
|
});
|
|
1040
1056
|
});
|
|
1041
1057
|
},
|
|
1042
1058
|
|
|
1043
1059
|
clear(target) {
|
|
1044
|
-
const validTargets = ['user', 'chatgpt']
|
|
1045
|
-
if (!target) return console.error('Please provide a valid target!')
|
|
1046
|
-
if (typeof target
|
|
1047
|
-
target = target.toLowerCase()
|
|
1060
|
+
const validTargets = ['user', 'chatgpt'] // valid targets
|
|
1061
|
+
if (!target) return console.error('Please provide a valid target!')
|
|
1062
|
+
if (typeof target != 'string') return console.error('Target must be a string')
|
|
1063
|
+
target = target.toLowerCase() // lowercase target
|
|
1048
1064
|
if (!validTargets.includes(target))
|
|
1049
|
-
return console.error(`Invalid target ${target}. Valid targets are [${validTargets}]`)
|
|
1065
|
+
return console.error(`Invalid target ${target}. Valid targets are [${validTargets}]`)
|
|
1050
1066
|
|
|
1051
1067
|
return new Promise(resolve => {
|
|
1052
1068
|
chatgpt.getAccessToken().then(async token => {
|
|
1053
|
-
const instructionsData = await this.fetchData()
|
|
1069
|
+
const instructionsData = await this.fetchData()
|
|
1054
1070
|
|
|
1055
1071
|
// Clear target's instructions
|
|
1056
|
-
if (target == 'user') instructionsData.about_user_message = ''
|
|
1057
|
-
else if (target == 'chatgpt') instructionsData.about_model_message = ''
|
|
1072
|
+
if (target == 'user') instructionsData.about_user_message = ''
|
|
1073
|
+
else if (target == 'chatgpt') instructionsData.about_model_message = ''
|
|
1058
1074
|
|
|
1059
|
-
await this.sendRequest('POST', token, instructionsData)
|
|
1060
|
-
return resolve()
|
|
1061
|
-
})
|
|
1075
|
+
await this.sendRequest('POST', token, instructionsData)
|
|
1076
|
+
return resolve()
|
|
1077
|
+
})})
|
|
1062
1078
|
},
|
|
1063
1079
|
|
|
1064
1080
|
fetchData() {
|
|
1065
1081
|
// INTERNAL METHOD
|
|
1066
|
-
return new Promise(resolve =>
|
|
1067
|
-
chatgpt.getAccessToken().then(async token =>
|
|
1068
|
-
|
|
1069
|
-
});});
|
|
1082
|
+
return new Promise(resolve =>
|
|
1083
|
+
chatgpt.getAccessToken().then(async token =>
|
|
1084
|
+
resolve(await this.sendRequest('GET', token)))) // return API data
|
|
1070
1085
|
},
|
|
1071
1086
|
|
|
1072
1087
|
sendRequest(method, token, body) {
|
|
1073
1088
|
// INTERNAL METHOD
|
|
1074
1089
|
// Validate args
|
|
1075
|
-
for (let i = 0; i < arguments.length - 1; i++) if (typeof arguments[i]
|
|
1076
|
-
return console.error(`Argument ${ i + 1 } must be a string`)
|
|
1077
|
-
const validMethods = ['POST', 'GET']
|
|
1078
|
-
method = (method || '').trim().toUpperCase()
|
|
1090
|
+
for (let i = 0 ; i < arguments.length - 1 ; i++) if (typeof arguments[i] == 'string')
|
|
1091
|
+
return console.error(`Argument ${ i + 1 } must be a string`)
|
|
1092
|
+
const validMethods = ['POST', 'GET']
|
|
1093
|
+
method = (method || '').trim().toUpperCase()
|
|
1079
1094
|
if (!method || !validMethods.includes(method)) // reject if not valid method
|
|
1080
|
-
return console.error(`Valid methods are ${ validMethods }`)
|
|
1081
|
-
if (!token) return console.error('Please provide a valid access token!')
|
|
1082
|
-
if (body && typeof body
|
|
1083
|
-
return console.error(`Invalid body data type. Got ${ typeof body }, expected object`)
|
|
1095
|
+
return console.error(`Valid methods are ${ validMethods }`)
|
|
1096
|
+
if (!token) return console.error('Please provide a valid access token!')
|
|
1097
|
+
if (body && typeof body != 'object') // reject if body is passed but not an object
|
|
1098
|
+
return console.error(`Invalid body data type. Got ${ typeof body }, expected object`)
|
|
1084
1099
|
|
|
1085
1100
|
return new Promise((resolve, reject) => {
|
|
1086
|
-
const xhr = new XMLHttpRequest()
|
|
1087
|
-
xhr.open(method, chatgpt.endpoints.openAI.instructions, true)
|
|
1101
|
+
const xhr = new XMLHttpRequest()
|
|
1102
|
+
xhr.open(method, chatgpt.endpoints.openAI.instructions, true)
|
|
1088
1103
|
// Set headers
|
|
1089
|
-
xhr.setRequestHeader('Accept-Language', 'en-US')
|
|
1090
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
1091
|
-
if (method == 'POST') xhr.setRequestHeader('Content-Type', 'application/json')
|
|
1104
|
+
xhr.setRequestHeader('Accept-Language', 'en-US')
|
|
1105
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
1106
|
+
if (method == 'POST') xhr.setRequestHeader('Content-Type', 'application/json')
|
|
1092
1107
|
|
|
1093
1108
|
xhr.onload = () => {
|
|
1094
|
-
const responseData = JSON.parse(xhr.responseText)
|
|
1095
|
-
if (xhr.status
|
|
1109
|
+
const responseData = JSON.parse(xhr.responseText)
|
|
1110
|
+
if (xhr.status == 422)
|
|
1096
1111
|
return reject('🤖 chatgpt.js >> Character limit exceeded. Custom instructions can have a maximum length of 1500 characters.');
|
|
1097
|
-
else if (xhr.status
|
|
1098
|
-
return reject('🤖 chatgpt.js >> ' + responseData.detail.description)
|
|
1099
|
-
else if (xhr.status
|
|
1100
|
-
return reject('🤖 chatgpt.js >> Request failed. Cannot contact custom instructions endpoint.')
|
|
1101
|
-
console.info(`Custom instructions successfully contacted with method ${ method }`)
|
|
1102
|
-
return resolve(responseData || {})
|
|
1103
|
-
}
|
|
1104
|
-
xhr.send(JSON.stringify(body) || '')
|
|
1105
|
-
})
|
|
1112
|
+
else if (xhr.status == 403 && responseData.detail.reason == 'content_policy')
|
|
1113
|
+
return reject('🤖 chatgpt.js >> ' + responseData.detail.description)
|
|
1114
|
+
else if (xhr.status != 200)
|
|
1115
|
+
return reject('🤖 chatgpt.js >> Request failed. Cannot contact custom instructions endpoint.')
|
|
1116
|
+
console.info(`Custom instructions successfully contacted with method ${ method }`)
|
|
1117
|
+
return resolve(responseData || {}) // return response data no matter what the method is
|
|
1118
|
+
}
|
|
1119
|
+
xhr.send(JSON.stringify(body) || '') // if body is passed send it, else just send the request
|
|
1120
|
+
})
|
|
1106
1121
|
},
|
|
1107
1122
|
|
|
1108
1123
|
turnOff() {
|
|
1109
|
-
return new Promise(resolve => {
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
});
|
|
1116
|
-
});
|
|
1124
|
+
return new Promise(resolve => chatgpt.getAccessToken().then(async token => {
|
|
1125
|
+
const instructionsData = await this.fetchData()
|
|
1126
|
+
instructionsData.enabled = false
|
|
1127
|
+
await this.sendRequest('POST', token, instructionsData)
|
|
1128
|
+
return resolve()
|
|
1129
|
+
}))
|
|
1117
1130
|
},
|
|
1118
1131
|
|
|
1119
1132
|
turnOn() {
|
|
1120
|
-
return new Promise(resolve => {
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
});
|
|
1127
|
-
});
|
|
1133
|
+
return new Promise(resolve => chatgpt.getAccessToken().then(async token => {
|
|
1134
|
+
const instructionsData = await this.fetchData()
|
|
1135
|
+
instructionsData.enabled = true
|
|
1136
|
+
await this.sendRequest('POST', token, instructionsData)
|
|
1137
|
+
return resolve()
|
|
1138
|
+
}))
|
|
1128
1139
|
},
|
|
1129
1140
|
|
|
1130
1141
|
toggle() {
|
|
1131
|
-
return new Promise(resolve => {
|
|
1132
|
-
this.
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
});});
|
|
1142
|
+
return new Promise(resolve => this.fetchData().then(async instructionsData => {
|
|
1143
|
+
await (instructionsData.enabled ? this.turnOff() : this.turnOn())
|
|
1144
|
+
return resolve()
|
|
1145
|
+
}))
|
|
1136
1146
|
}
|
|
1137
1147
|
},
|
|
1138
1148
|
|
|
1139
1149
|
isDarkMode() { return document.documentElement.className.includes('dark') },
|
|
1140
|
-
isFullScreen() { return chatgpt.browser.isFullScreen()
|
|
1150
|
+
isFullScreen() { return chatgpt.browser.isFullScreen() },
|
|
1141
1151
|
|
|
1142
1152
|
async isIdle(timeout = null) {
|
|
1143
|
-
const obsConfig = { childList: true, subtree: true }
|
|
1144
|
-
msgDivSelector = 'div[data-message-author-role]';
|
|
1153
|
+
const obsConfig = { childList: true, subtree: true }
|
|
1145
1154
|
|
|
1146
1155
|
// Create promises
|
|
1147
|
-
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
1156
|
+
const timeoutPromise = timeout ? new Promise(resolve => setTimeout(() => resolve(false), timeout)) : null
|
|
1148
1157
|
const isIdlePromise = (async () => {
|
|
1149
1158
|
await new Promise(resolve => { // when on convo page
|
|
1150
|
-
if (document.querySelector(
|
|
1159
|
+
if (document.querySelector(chatgpt.selectors.chatDivs.msg)) resolve()
|
|
1151
1160
|
else new MutationObserver((_, obs) => {
|
|
1152
|
-
if (document.querySelector(
|
|
1153
|
-
}).observe(document.body, obsConfig)
|
|
1154
|
-
})
|
|
1155
|
-
await new Promise(resolve =>
|
|
1161
|
+
if (document.querySelector(chatgpt.selectors.chatDivs.msg)) { obs.disconnect() ; resolve() }
|
|
1162
|
+
}).observe(document.body, obsConfig)
|
|
1163
|
+
})
|
|
1164
|
+
await new Promise(resolve => // when reply starts generating
|
|
1156
1165
|
new MutationObserver((_, obs) => {
|
|
1157
|
-
if (chatgpt.getStopBtn()) { obs.disconnect(); resolve()
|
|
1158
|
-
}).observe(document.body, obsConfig)
|
|
1159
|
-
|
|
1160
|
-
return new Promise(resolve =>
|
|
1166
|
+
if (chatgpt.getStopBtn()) { obs.disconnect() ; resolve() }
|
|
1167
|
+
}).observe(document.body, obsConfig)
|
|
1168
|
+
)
|
|
1169
|
+
return new Promise(resolve => // when reply stops generating
|
|
1161
1170
|
new MutationObserver((_, obs) => {
|
|
1162
|
-
if (!chatgpt.getStopBtn()) { obs.disconnect(); resolve(true)
|
|
1163
|
-
}).observe(document.body, obsConfig)
|
|
1164
|
-
|
|
1165
|
-
})()
|
|
1171
|
+
if (!chatgpt.getStopBtn()) { obs.disconnect() ; resolve(true) }
|
|
1172
|
+
}).observe(document.body, obsConfig)
|
|
1173
|
+
)
|
|
1174
|
+
})()
|
|
1166
1175
|
|
|
1167
|
-
return await (timeoutPromise ? Promise.race([isIdlePromise, timeoutPromise]) : isIdlePromise)
|
|
1176
|
+
return await (timeoutPromise ? Promise.race([isIdlePromise, timeoutPromise]) : isIdlePromise)
|
|
1168
1177
|
},
|
|
1169
1178
|
|
|
1170
1179
|
async isLoaded(timeout = null) {
|
|
@@ -1178,107 +1187,92 @@ const chatgpt = {
|
|
|
1178
1187
|
return await ( timeoutPromise ? Promise.race([isLoadedPromise, timeoutPromise]) : isLoadedPromise )
|
|
1179
1188
|
},
|
|
1180
1189
|
|
|
1181
|
-
isLightMode() { return document.documentElement.classList.toString().includes('light')
|
|
1190
|
+
isLightMode() { return document.documentElement.classList.toString().includes('light') },
|
|
1191
|
+
isTempChat() { return location.search == '?temporary-chat=true' },
|
|
1182
1192
|
isTyping() { return !!this.getStopButton() },
|
|
1183
|
-
|
|
1184
|
-
logout() { window.location.href = 'https://chat.openai.com/auth/logout'
|
|
1193
|
+
login() { window.location.href = 'https://chat.openai.com/auth/login' },
|
|
1194
|
+
logout() { window.location.href = 'https://chat.openai.com/auth/logout' },
|
|
1185
1195
|
|
|
1186
1196
|
menu: {
|
|
1187
|
-
|
|
1188
|
-
addedEvent: false,
|
|
1197
|
+
elems: [],
|
|
1189
1198
|
|
|
1190
|
-
append(
|
|
1191
|
-
//
|
|
1199
|
+
append(elem, attrs = {}) {
|
|
1200
|
+
// elem = 'button' | 'dropdown' REQUIRED (no default value)
|
|
1192
1201
|
// attrs = { ... }
|
|
1193
1202
|
// attrs for 'button': 'icon' = src string, 'label' = string, 'onclick' = function
|
|
1194
1203
|
// attrs for 'dropdown': 'items' = [ { text: string, value: string }, ... ] array of objects
|
|
1195
1204
|
// where 'text' is the displayed text of the option and 'value' is the value of the option
|
|
1196
1205
|
|
|
1197
|
-
const
|
|
1198
|
-
if (!
|
|
1199
|
-
return console.error('🤖 chatgpt.js >> Please supply a valid string element name!')
|
|
1200
|
-
|
|
1201
|
-
if (!
|
|
1202
|
-
return console.error(`🤖 chatgpt.js >> Invalid element! Valid
|
|
1203
|
-
|
|
1204
|
-
const newElement = document.createElement(
|
|
1205
|
-
element == 'dropdown' ? 'select' :
|
|
1206
|
-
element == 'button' ? 'a' : element
|
|
1207
|
-
);
|
|
1208
|
-
newElement.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now(); // add random id to the element
|
|
1209
|
-
|
|
1210
|
-
if (element == 'button') {
|
|
1211
|
-
newElement.textContent = attrs?.label && typeof attrs.label == 'string'
|
|
1212
|
-
? attrs.label
|
|
1213
|
-
: 'chatgpt.js button';
|
|
1206
|
+
const validElems = ['button', 'dropdown']
|
|
1207
|
+
if (!elem || typeof elem != 'string') // element not passed or invalid type
|
|
1208
|
+
return console.error('🤖 chatgpt.js >> Please supply a valid string element name!')
|
|
1209
|
+
elem = elem.toLowerCase()
|
|
1210
|
+
if (!validElems.includes(elem)) // element not in list
|
|
1211
|
+
return console.error(`🤖 chatgpt.js >> Invalid element! Valid elems are [${validElems}]`)
|
|
1214
1212
|
|
|
1215
|
-
|
|
1213
|
+
const newElem = document.createElement(elem == 'dropdown' ? 'select' : elem == 'button' ? 'a' : elem)
|
|
1214
|
+
newElem.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now()
|
|
1215
|
+
|
|
1216
|
+
if (elem == 'button') {
|
|
1217
|
+
newElem.textContent = attrs?.label && typeof attrs.label == 'string' ? attrs.label : 'chatgpt.js button'
|
|
1218
|
+
const icon = document.createElement('img')
|
|
1216
1219
|
icon.src = attrs?.icon && typeof attrs.icon == 'string' // can also be base64 encoded image string
|
|
1217
1220
|
? attrs.icon // add icon to button element if given, else default one
|
|
1218
|
-
:
|
|
1219
|
-
icon.width = 18
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
newElement.onclick = attrs?.onclick && typeof attrs.onclick == 'function'
|
|
1223
|
-
? attrs.onclick
|
|
1224
|
-
: function() {};
|
|
1221
|
+
: `${chatgpt.endpoints.assets}/starters/chrome/extension/icons/icon128.png`
|
|
1222
|
+
icon.width = 18
|
|
1223
|
+
newElem.firstChild.before(icon)
|
|
1224
|
+
newElem.onclick = attrs?.onclick && typeof attrs.onclick == 'function' ? attrs.onclick : function(){}
|
|
1225
1225
|
}
|
|
1226
1226
|
|
|
1227
|
-
else if (
|
|
1227
|
+
else if (elem == 'dropdown') {
|
|
1228
1228
|
if (!attrs?.items || // there no are options to add
|
|
1229
1229
|
!Array.isArray(attrs.items) || // it's not an array
|
|
1230
1230
|
!attrs.items.length) // the array is empty
|
|
1231
|
-
attrs.items = [{ text: '🤖 chatgpt.js option', value: 'chatgpt.js option value' }]
|
|
1231
|
+
attrs.items = [{ text: '🤖 chatgpt.js option', value: 'chatgpt.js option value' }] // set default dropdown entry
|
|
1232
1232
|
|
|
1233
1233
|
if (!attrs.items.every(el => typeof el == 'object')) // the entries of the array are not objects
|
|
1234
|
-
return console.error('\'items\' must be an array of objects!')
|
|
1234
|
+
return console.error('\'items\' must be an array of objects!')
|
|
1235
1235
|
|
|
1236
|
-
|
|
1236
|
+
newElem.style = 'background-color: #000; width: 100%; border: none;'
|
|
1237
1237
|
|
|
1238
1238
|
attrs.items.forEach(item => {
|
|
1239
|
-
const optionElement = document.createElement('option')
|
|
1240
|
-
optionElement.textContent = item?.text
|
|
1241
|
-
optionElement.value = item?.value
|
|
1242
|
-
|
|
1243
|
-
})
|
|
1239
|
+
const optionElement = document.createElement('option')
|
|
1240
|
+
optionElement.textContent = item?.text
|
|
1241
|
+
optionElement.value = item?.value
|
|
1242
|
+
newElem.add(optionElement)
|
|
1243
|
+
})
|
|
1244
1244
|
}
|
|
1245
1245
|
|
|
1246
|
-
const
|
|
1247
|
-
const
|
|
1248
|
-
let cssClasses
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
if (!headlessNav.contains(element))
|
|
1260
|
-
try { headlessNav.insertBefore(element, headlessNav.firstChild); }
|
|
1261
|
-
catch (err) { console.error(err); }
|
|
1262
|
-
});
|
|
1263
|
-
};
|
|
1246
|
+
const addElemsToMenu = () => {
|
|
1247
|
+
const optionBtns = document.querySelectorAll('a[role=menuitem]')
|
|
1248
|
+
let cssClasses
|
|
1249
|
+
for (const navLink of optionBtns)
|
|
1250
|
+
if (navLink.textContent == 'Settings') { cssClasses = navLink.classList ; break }
|
|
1251
|
+
const headlessNav = optionBtns[0].parentNode
|
|
1252
|
+
chatgpt.menu.elems.forEach(elem => {
|
|
1253
|
+
elem.setAttribute('class', cssClasses)
|
|
1254
|
+
if (!headlessNav.contains(elem))
|
|
1255
|
+
try { headlessNav.firstChild.before(elem) }
|
|
1256
|
+
catch (err) { console.error(err) }
|
|
1257
|
+
})
|
|
1258
|
+
}
|
|
1264
1259
|
|
|
1265
|
-
this.
|
|
1266
|
-
const menuBtn = document.querySelector('nav button[id*=headless]')
|
|
1260
|
+
this.elems.push(newElem)
|
|
1261
|
+
const menuBtn = document.querySelector('nav button[id*=headless]')
|
|
1267
1262
|
if (!this.addedEvent) { // to prevent adding more than one event
|
|
1268
|
-
menuBtn?.addEventListener('click', () =>
|
|
1269
|
-
this.addedEvent = true; }
|
|
1263
|
+
menuBtn?.addEventListener('click', () => setTimeout(addElemsToMenu, 25)) ; this.addedEvent = true }
|
|
1270
1264
|
|
|
1271
|
-
return
|
|
1265
|
+
return newElem.id
|
|
1272
1266
|
},
|
|
1273
1267
|
|
|
1274
1268
|
close() {
|
|
1275
|
-
try { document.querySelector('nav [id*=menu-button][aria-expanded=true]').click()
|
|
1276
|
-
catch (err) { console.error(err.message)
|
|
1269
|
+
try { document.querySelector('nav [id*=menu-button][aria-expanded=true]').click() }
|
|
1270
|
+
catch (err) { console.error(err.message) }
|
|
1277
1271
|
},
|
|
1278
1272
|
|
|
1279
1273
|
open() {
|
|
1280
|
-
try { document.querySelector('nav [id*=menu-button][aria-expanded=false]').click()
|
|
1281
|
-
catch (err) { console.error(err.message)
|
|
1274
|
+
try { document.querySelector('nav [id*=menu-button][aria-expanded=false]').click() }
|
|
1275
|
+
catch (err) { console.error(err.message) }
|
|
1282
1276
|
}
|
|
1283
1277
|
},
|
|
1284
1278
|
|
|
@@ -1287,44 +1281,44 @@ const chatgpt = {
|
|
|
1287
1281
|
notify(msg, position, notifDuration, shadow) {
|
|
1288
1282
|
notifDuration = notifDuration ? +notifDuration : 1.75; // sec duration to maintain notification visibility
|
|
1289
1283
|
const fadeDuration = 0.35, // sec duration of fade-out
|
|
1290
|
-
vpYoffset = 23, vpXoffset = 27
|
|
1284
|
+
vpYoffset = 23, vpXoffset = 27 // px offset from viewport border
|
|
1291
1285
|
|
|
1292
1286
|
// Create/append notification div
|
|
1293
|
-
const notificationDiv = document.createElement('div')
|
|
1294
|
-
notificationDiv.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now()
|
|
1295
|
-
notificationDiv.classList.add('chatgpt-notif')
|
|
1296
|
-
notificationDiv.innerText = msg
|
|
1297
|
-
document.body.append(notificationDiv)
|
|
1287
|
+
const notificationDiv = document.createElement('div') // make div
|
|
1288
|
+
notificationDiv.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now()
|
|
1289
|
+
notificationDiv.classList.add('chatgpt-notif')
|
|
1290
|
+
notificationDiv.innerText = msg // insert msg
|
|
1291
|
+
document.body.append(notificationDiv) // insert into DOM
|
|
1298
1292
|
|
|
1299
1293
|
// Create/append close button
|
|
1300
|
-
const closeBtn = document.createElement('div')
|
|
1301
|
-
closeBtn.title = 'Dismiss'; closeBtn.classList.add('notif-close-btn', 'no-mobile-tap-outline')
|
|
1302
|
-
const closeSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
|
1303
|
-
closeSVG.setAttribute('height', '8px')
|
|
1304
|
-
closeSVG.setAttribute('viewBox', '0 0 14 14')
|
|
1305
|
-
closeSVG.setAttribute('fill', 'none')
|
|
1306
|
-
closeSVG.style.height = closeSVG.style.width = '8px'
|
|
1307
|
-
const closeSVGpath = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
|
1308
|
-
closeSVGpath.setAttribute('fill-rule', 'evenodd')
|
|
1309
|
-
closeSVGpath.setAttribute('clip-rule', 'evenodd')
|
|
1310
|
-
closeSVGpath.setAttribute('fill', 'white')
|
|
1294
|
+
const closeBtn = document.createElement('div')
|
|
1295
|
+
closeBtn.title = 'Dismiss'; closeBtn.classList.add('notif-close-btn', 'no-mobile-tap-outline')
|
|
1296
|
+
const closeSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
|
1297
|
+
closeSVG.setAttribute('height', '8px')
|
|
1298
|
+
closeSVG.setAttribute('viewBox', '0 0 14 14')
|
|
1299
|
+
closeSVG.setAttribute('fill', 'none')
|
|
1300
|
+
closeSVG.style.height = closeSVG.style.width = '8px' // override SVG styles on non-OpenAI sites
|
|
1301
|
+
const closeSVGpath = document.createElementNS('http://www.w3.org/2000/svg', 'path')
|
|
1302
|
+
closeSVGpath.setAttribute('fill-rule', 'evenodd')
|
|
1303
|
+
closeSVGpath.setAttribute('clip-rule', 'evenodd')
|
|
1304
|
+
closeSVGpath.setAttribute('fill', 'white')
|
|
1311
1305
|
closeSVGpath.setAttribute('d', 'M13.7071 1.70711C14.0976 1.31658 14.0976 0.683417 13.7071 0.292893C13.3166 -0.0976312 12.6834 -0.0976312 12.2929 0.292893L7 5.58579L1.70711 0.292893C1.31658 -0.0976312 0.683417 -0.0976312 0.292893 0.292893C-0.0976312 0.683417 -0.0976312 1.31658 0.292893 1.70711L5.58579 7L0.292893 12.2929C-0.0976312 12.6834 -0.0976312 13.3166 0.292893 13.7071C0.683417 14.0976 1.31658 14.0976 1.70711 13.7071L7 8.41421L12.2929 13.7071C12.6834 14.0976 13.3166 14.0976 13.7071 13.7071C14.0976 13.3166 14.0976 12.6834 13.7071 12.2929L8.41421 7L13.7071 1.70711Z');
|
|
1312
|
-
closeSVG.append(closeSVGpath); closeBtn.append(closeSVG); notificationDiv.append(closeBtn)
|
|
1306
|
+
closeSVG.append(closeSVGpath) ; closeBtn.append(closeSVG) ; notificationDiv.append(closeBtn)
|
|
1313
1307
|
|
|
1314
1308
|
// Determine div position/quadrant
|
|
1315
|
-
notificationDiv.isTop = !position || !/low|bottom/i.test(position)
|
|
1316
|
-
notificationDiv.isRight = !position || !/left/i.test(position)
|
|
1309
|
+
notificationDiv.isTop = !position || !/low|bottom/i.test(position)
|
|
1310
|
+
notificationDiv.isRight = !position || !/left/i.test(position)
|
|
1317
1311
|
notificationDiv.quadrant = (notificationDiv.isTop ? 'top' : 'bottom')
|
|
1318
|
-
+ (notificationDiv.isRight ? 'Right' : 'Left')
|
|
1312
|
+
+ (notificationDiv.isRight ? 'Right' : 'Left')
|
|
1319
1313
|
|
|
1320
1314
|
// Create/append/update notification style (if missing or outdated)
|
|
1321
1315
|
const thisUpdated = 1735767823541 // timestamp of last edit for this file's `notifStyle`
|
|
1322
|
-
let notifStyle = document.querySelector('#chatgpt-notif-style')
|
|
1316
|
+
let notifStyle = document.querySelector('#chatgpt-notif-style') // try to select existing style
|
|
1323
1317
|
if (!notifStyle || parseInt(notifStyle.getAttribute('last-updated'), 10) < thisUpdated) { // if missing or outdated
|
|
1324
1318
|
if (!notifStyle) { // outright missing, create/id/attr/append it first
|
|
1325
|
-
notifStyle = document.createElement('style'); notifStyle.id = 'chatgpt-notif-style'
|
|
1326
|
-
notifStyle.setAttribute('last-updated', thisUpdated.toString())
|
|
1327
|
-
document.head.append(notifStyle)
|
|
1319
|
+
notifStyle = document.createElement('style') ; notifStyle.id = 'chatgpt-notif-style'
|
|
1320
|
+
notifStyle.setAttribute('last-updated', thisUpdated.toString())
|
|
1321
|
+
document.head.append(notifStyle)
|
|
1328
1322
|
}
|
|
1329
1323
|
notifStyle.innerText = ( // update prev/new style contents
|
|
1330
1324
|
'.chatgpt-notif {'
|
|
@@ -1348,64 +1342,64 @@ const chatgpt = {
|
|
|
1348
1342
|
+ '15% { opacity: 0.35 ; transform: rotateX(-27deg) scale(1.05) }'
|
|
1349
1343
|
+ '45% { opacity: 0.05 ; transform: rotateX(-81deg) }'
|
|
1350
1344
|
+ '100% { opacity: 0 ; transform: rotateX(-180deg) scale(1.15) }}'
|
|
1351
|
-
)
|
|
1345
|
+
)
|
|
1352
1346
|
}
|
|
1353
1347
|
|
|
1354
1348
|
// Enqueue notification
|
|
1355
|
-
let notifyProps = JSON.parse(localStorage.notifyProps)
|
|
1356
|
-
notifyProps.queue[notificationDiv.quadrant].push(notificationDiv.id)
|
|
1357
|
-
localStorage.notifyProps = JSON.stringify(notifyProps)
|
|
1349
|
+
let notifyProps = JSON.parse(localStorage.notifyProps)
|
|
1350
|
+
notifyProps.queue[notificationDiv.quadrant].push(notificationDiv.id)
|
|
1351
|
+
localStorage.notifyProps = JSON.stringify(notifyProps)
|
|
1358
1352
|
|
|
1359
1353
|
// Position notification (defaults to top-right)
|
|
1360
|
-
notificationDiv.style.top = notificationDiv.isTop ? vpYoffset.toString() + 'px' : ''
|
|
1361
|
-
notificationDiv.style.bottom = !notificationDiv.isTop ? vpYoffset.toString() + 'px' : ''
|
|
1362
|
-
notificationDiv.style.right = notificationDiv.isRight ? vpXoffset.toString() + 'px' : ''
|
|
1363
|
-
notificationDiv.style.left = !notificationDiv.isRight ? vpXoffset.toString() + 'px' : ''
|
|
1354
|
+
notificationDiv.style.top = notificationDiv.isTop ? vpYoffset.toString() + 'px' : ''
|
|
1355
|
+
notificationDiv.style.bottom = !notificationDiv.isTop ? vpYoffset.toString() + 'px' : ''
|
|
1356
|
+
notificationDiv.style.right = notificationDiv.isRight ? vpXoffset.toString() + 'px' : ''
|
|
1357
|
+
notificationDiv.style.left = !notificationDiv.isRight ? vpXoffset.toString() + 'px' : ''
|
|
1364
1358
|
|
|
1365
1359
|
// Reposition old notifications
|
|
1366
|
-
const thisQuadrantQueue = notifyProps.queue[notificationDiv.quadrant]
|
|
1360
|
+
const thisQuadrantQueue = notifyProps.queue[notificationDiv.quadrant]
|
|
1367
1361
|
if (thisQuadrantQueue.length > 1) {
|
|
1368
1362
|
try { // to move old notifications
|
|
1369
1363
|
for (const divId of thisQuadrantQueue.slice(0, -1)) { // exclude new div
|
|
1370
1364
|
const oldDiv = document.getElementById(divId),
|
|
1371
1365
|
offsetProp = oldDiv.style.top ? 'top' : 'bottom', // pick property to change
|
|
1372
|
-
vOffset = +/\d+/.exec(oldDiv.style[offsetProp])[0] + 5 + oldDiv.getBoundingClientRect().height
|
|
1373
|
-
oldDiv.style[offsetProp] = `${ vOffset }px
|
|
1366
|
+
vOffset = +/\d+/.exec(oldDiv.style[offsetProp])[0] + 5 + oldDiv.getBoundingClientRect().height
|
|
1367
|
+
oldDiv.style[offsetProp] = `${ vOffset }px` // change prop
|
|
1374
1368
|
}
|
|
1375
1369
|
} catch (err) {}
|
|
1376
1370
|
}
|
|
1377
1371
|
|
|
1378
1372
|
// Show notification
|
|
1379
1373
|
setTimeout(() => {
|
|
1380
|
-
notificationDiv.style.opacity = chatgpt.isDarkMode() ? 0.8 : 0.67
|
|
1381
|
-
notificationDiv.style.transform = 'translateX(0)'
|
|
1382
|
-
notificationDiv.style.transition = 'transform 0.15s ease, opacity 0.15s ease'
|
|
1383
|
-
}, 10)
|
|
1374
|
+
notificationDiv.style.opacity = chatgpt.isDarkMode() ? 0.8 : 0.67 // show msg
|
|
1375
|
+
notificationDiv.style.transform = 'translateX(0)' // bring from off-screen
|
|
1376
|
+
notificationDiv.style.transition = 'transform 0.15s ease, opacity 0.15s ease'
|
|
1377
|
+
}, 10)
|
|
1384
1378
|
|
|
1385
1379
|
// Init delay before hiding
|
|
1386
1380
|
const hideDelay = fadeDuration > notifDuration ? 0 // don't delay if fade exceeds notification duration
|
|
1387
|
-
: notifDuration - fadeDuration
|
|
1381
|
+
: notifDuration - fadeDuration // otherwise delay for difference
|
|
1388
1382
|
|
|
1389
1383
|
// Add notification dismissal to timeout schedule + button clicks
|
|
1390
1384
|
const dismissNotif = () => {
|
|
1391
1385
|
notificationDiv.style.animation = `notif-zoom-fade-out ${ fadeDuration }s ease-out`;
|
|
1392
|
-
clearTimeout(dismissFuncTID)
|
|
1393
|
-
}
|
|
1394
|
-
const dismissFuncTID = setTimeout(dismissNotif, hideDelay * 1000)
|
|
1395
|
-
closeSVG.onclick = dismissNotif
|
|
1386
|
+
clearTimeout(dismissFuncTID)
|
|
1387
|
+
}
|
|
1388
|
+
const dismissFuncTID = setTimeout(dismissNotif, hideDelay * 1000) // maintain visibility for `hideDelay` secs, then dismiss
|
|
1389
|
+
closeSVG.onclick = dismissNotif // add to close button clicks
|
|
1396
1390
|
|
|
1397
1391
|
// Destroy notification
|
|
1398
1392
|
notificationDiv.onanimationend = () => {
|
|
1399
|
-
notificationDiv.remove()
|
|
1400
|
-
notifyProps = JSON.parse(localStorage.notifyProps)
|
|
1401
|
-
notifyProps.queue[notificationDiv.quadrant].shift()
|
|
1402
|
-
localStorage.notifyProps = JSON.stringify(notifyProps)
|
|
1403
|
-
}
|
|
1393
|
+
notificationDiv.remove() // remove from DOM
|
|
1394
|
+
notifyProps = JSON.parse(localStorage.notifyProps)
|
|
1395
|
+
notifyProps.queue[notificationDiv.quadrant].shift() // + memory
|
|
1396
|
+
localStorage.notifyProps = JSON.stringify(notifyProps) // + storage
|
|
1397
|
+
}
|
|
1404
1398
|
|
|
1405
|
-
return notificationDiv
|
|
1399
|
+
return notificationDiv
|
|
1406
1400
|
},
|
|
1407
1401
|
|
|
1408
|
-
obfuscate() { chatgpt.code.obfuscate()
|
|
1402
|
+
obfuscate() { chatgpt.code.obfuscate() },
|
|
1409
1403
|
|
|
1410
1404
|
printAllFunctions() {
|
|
1411
1405
|
|
|
@@ -1416,39 +1410,39 @@ const chatgpt = {
|
|
|
1416
1410
|
methodName: ['#005aff', '#ffa500'], // blue, orange
|
|
1417
1411
|
entryType: ['#467e06', '#b981f9'], // green, purple
|
|
1418
1412
|
srcMethod: ['#ff0000', '#00ffff'] // red, cyan
|
|
1419
|
-
}
|
|
1420
|
-
Object.keys(colors).forEach(
|
|
1421
|
-
colors[
|
|
1422
|
-
'#' + (Number(`0x1${ colors[
|
|
1423
|
-
.toString(16).substring(1).toUpperCase()
|
|
1424
|
-
})
|
|
1413
|
+
}
|
|
1414
|
+
Object.keys(colors).forEach(elem => { // populate dark scheme colors if missing
|
|
1415
|
+
colors[elem][1] = colors[elem][1] ||
|
|
1416
|
+
'#' + (Number(`0x1${ colors[elem][0].replace(/^#/, '') }`) ^ 0xFFFFFF)
|
|
1417
|
+
.toString(16).substring(1).toUpperCase() // convert to hex
|
|
1418
|
+
})
|
|
1425
1419
|
|
|
1426
1420
|
// Create [functionNames]
|
|
1427
|
-
const functionNames = []
|
|
1421
|
+
const functionNames = []
|
|
1428
1422
|
for (const prop in this) {
|
|
1429
1423
|
if (typeof this[prop] == 'function') {
|
|
1430
1424
|
const chatgptIsParent = !Object.keys(this)
|
|
1431
1425
|
.find(obj => Object.keys(this[obj]).includes(this[prop].name))
|
|
1432
|
-
const functionParent = chatgptIsParent ? 'chatgpt' : 'other'
|
|
1433
|
-
functionNames.push([functionParent, prop])
|
|
1426
|
+
const functionParent = chatgptIsParent ? 'chatgpt' : 'other'
|
|
1427
|
+
functionNames.push([functionParent, prop])
|
|
1434
1428
|
} else if (typeof this[prop] == 'object') {
|
|
1435
1429
|
for (const nestedProp in this[prop]) {
|
|
1436
1430
|
if (typeof this[prop][nestedProp] == 'function') {
|
|
1437
|
-
functionNames.push([prop, nestedProp])
|
|
1431
|
+
functionNames.push([prop, nestedProp])
|
|
1438
1432
|
}}}}
|
|
1439
|
-
functionNames.sort((a, b) =>
|
|
1433
|
+
functionNames.sort((a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1]))
|
|
1440
1434
|
|
|
1441
1435
|
// Print methods
|
|
1442
1436
|
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches,
|
|
1443
|
-
baseFontStyles = 'font-family: monospace ; font-size: larger ; '
|
|
1444
|
-
console.log('\n%c🤖 chatgpt.js methods\n', 'font-family: sans-serif ; font-size: xxx-large ; font-weight: bold')
|
|
1437
|
+
baseFontStyles = 'font-family: monospace ; font-size: larger ; '
|
|
1438
|
+
console.log('\n%c🤖 chatgpt.js methods\n', 'font-family: sans-serif ; font-size: xxx-large ; font-weight: bold')
|
|
1445
1439
|
for (const functionName of functionNames) {
|
|
1446
1440
|
const isChatGptObjParent = /chatgpt|other/.test(functionName[0]),
|
|
1447
1441
|
rootFunction = ( functionName[0] == 'chatgpt' ? this[functionName[1]].name
|
|
1448
|
-
: functionName[0]
|
|
1442
|
+
: functionName[0] != 'other' ? functionName[0] + '.' + functionName[1]
|
|
1449
1443
|
: (( Object.keys(this).find(obj => Object.keys(this[obj]).includes(this[functionName[1]].name)) + '.' )
|
|
1450
1444
|
+ this[functionName[1]].name )),
|
|
1451
|
-
isAsync = this[functionName[1]]?.constructor.name == 'AsyncFunction'
|
|
1445
|
+
isAsync = this[functionName[1]]?.constructor.name == 'AsyncFunction'
|
|
1452
1446
|
console.log('%c>> %c' + ( isChatGptObjParent ? '' : `${ functionName[0] }.%c`) + functionName[1]
|
|
1453
1447
|
+ ' - https://chatgptjs.org/userguide/' + /(?:.*\.)?(.*)/.exec(rootFunction)[1].toLowerCase() + ( isAsync ? '-async' : '' ) + '\n%c[%c'
|
|
1454
1448
|
+ ((( functionName[0] == 'chatgpt' && functionName[1] == this[functionName[1]].name ) || // parent is chatgpt + names match or
|
|
@@ -1470,28 +1464,28 @@ const chatgpt = {
|
|
|
1470
1464
|
+ 'color:' + ( isChatGptObjParent ? colors.srcMethod[+isDarkMode] : 'initial' ),
|
|
1471
1465
|
baseFontStyles + ( isChatGptObjParent ? 'font-weight: initial' : 'font-style: italic' ) + ';'
|
|
1472
1466
|
+ 'color:' + ( isChatGptObjParent ? 'initial' : colors.srcMethod[+isDarkMode] ),
|
|
1473
|
-
isChatGptObjParent ? '' : ( baseFontStyles + 'color: initial ; font-weight: initial' ))
|
|
1467
|
+
isChatGptObjParent ? '' : ( baseFontStyles + 'color: initial ; font-weight: initial' ))
|
|
1474
1468
|
}
|
|
1475
1469
|
},
|
|
1476
1470
|
|
|
1477
1471
|
randomFloat() {
|
|
1478
1472
|
// * Generates a random, cryptographically secure value between 0 (inclusive) & 1 (exclusive)
|
|
1479
|
-
const crypto = window.crypto || window.msCrypto
|
|
1480
|
-
return crypto?.getRandomValues(new Uint32Array(1))[0] / 0xFFFFFFFF || Math.random()
|
|
1473
|
+
const crypto = window.crypto || window.msCrypto
|
|
1474
|
+
return crypto?.getRandomValues(new Uint32Array(1))[0] / 0xFFFFFFFF || Math.random()
|
|
1481
1475
|
},
|
|
1482
1476
|
|
|
1483
|
-
refactor() { chatgpt.code.refactor()
|
|
1484
|
-
regenerate() { chatgpt.response.regenerate()
|
|
1477
|
+
refactor() { chatgpt.code.refactor() },
|
|
1478
|
+
regenerate() { chatgpt.response.regenerate() },
|
|
1485
1479
|
|
|
1486
1480
|
renderHTML(node) {
|
|
1487
1481
|
const reTags = /<([a-z\d]+)\b([^>]*)>([\s\S]*?)<\/\1>/g,
|
|
1488
|
-
|
|
1489
|
-
nodeContent = node.childNodes
|
|
1482
|
+
reAttrs = /(\S+)=['"]?((?:.(?!['"]?\s+\S+=|[>']))+.)['"]?/g, // eslint-disable-line
|
|
1483
|
+
nodeContent = node.childNodes
|
|
1490
1484
|
|
|
1491
1485
|
// Preserve consecutive spaces + line breaks
|
|
1492
1486
|
if (!chatgpt.renderHTML.preWrapSet) {
|
|
1493
|
-
node.style.whiteSpace = 'pre-wrap'; chatgpt.renderHTML.preWrapSet = true
|
|
1494
|
-
setTimeout(() =>
|
|
1487
|
+
node.style.whiteSpace = 'pre-wrap' ; chatgpt.renderHTML.preWrapSet = true
|
|
1488
|
+
setTimeout(() => chatgpt.renderHTML.preWrapSet = false, 100)
|
|
1495
1489
|
}
|
|
1496
1490
|
|
|
1497
1491
|
// Process child nodes
|
|
@@ -1500,44 +1494,44 @@ const chatgpt = {
|
|
|
1500
1494
|
// Process text node
|
|
1501
1495
|
if (childNode.nodeType == Node.TEXT_NODE) {
|
|
1502
1496
|
const text = childNode.nodeValue,
|
|
1503
|
-
elems = Array.from(text.matchAll(reTags))
|
|
1497
|
+
elems = Array.from(text.matchAll(reTags))
|
|
1504
1498
|
|
|
1505
1499
|
// Process 1st element to render
|
|
1506
1500
|
if (elems.length > 0) {
|
|
1507
1501
|
const elem = elems[0],
|
|
1508
|
-
[tagContent, tagName,
|
|
1509
|
-
tagNode = document.createElement(tagName); tagNode.textContent = tagText
|
|
1502
|
+
[tagContent, tagName, tagAttrs, tagText] = elem.slice(0, 4),
|
|
1503
|
+
tagNode = document.createElement(tagName) ; tagNode.textContent = tagText
|
|
1510
1504
|
|
|
1511
1505
|
// Extract/set attributes
|
|
1512
|
-
const
|
|
1513
|
-
|
|
1514
|
-
const name =
|
|
1515
|
-
tagNode.setAttribute(name, value)
|
|
1516
|
-
})
|
|
1506
|
+
const attrs = Array.from(tagAttrs.matchAll(reAttrs))
|
|
1507
|
+
attrs.forEach(attr => {
|
|
1508
|
+
const name = attr[1], value = attr[2].replace(/['"]/g, '')
|
|
1509
|
+
tagNode.setAttribute(name, value)
|
|
1510
|
+
})
|
|
1517
1511
|
|
|
1518
|
-
const renderedNode = chatgpt.renderHTML(tagNode)
|
|
1512
|
+
const renderedNode = chatgpt.renderHTML(tagNode) // render child elems of newly created node
|
|
1519
1513
|
|
|
1520
1514
|
// Insert newly rendered node
|
|
1521
1515
|
const beforeTextNode = document.createTextNode(text.substring(0, elem.index)),
|
|
1522
|
-
afterTextNode = document.createTextNode(text.substring(elem.index + tagContent.length))
|
|
1516
|
+
afterTextNode = document.createTextNode(text.substring(elem.index + tagContent.length))
|
|
1523
1517
|
|
|
1524
1518
|
// Replace text node with processed nodes
|
|
1525
|
-
node.replaceChild(beforeTextNode, childNode)
|
|
1526
|
-
node.insertBefore(renderedNode, beforeTextNode.nextSibling)
|
|
1527
|
-
node.insertBefore(afterTextNode, renderedNode.nextSibling)
|
|
1519
|
+
node.replaceChild(beforeTextNode, childNode)
|
|
1520
|
+
node.insertBefore(renderedNode, beforeTextNode.nextSibling)
|
|
1521
|
+
node.insertBefore(afterTextNode, renderedNode.nextSibling)
|
|
1528
1522
|
}
|
|
1529
1523
|
|
|
1530
1524
|
// Process element nodes recursively
|
|
1531
|
-
} else if (childNode.nodeType == Node.ELEMENT_NODE) chatgpt.renderHTML(childNode)
|
|
1525
|
+
} else if (childNode.nodeType == Node.ELEMENT_NODE) chatgpt.renderHTML(childNode)
|
|
1532
1526
|
}
|
|
1533
1527
|
|
|
1534
|
-
return node
|
|
1528
|
+
return node // if assignment used
|
|
1535
1529
|
},
|
|
1536
1530
|
|
|
1537
|
-
async resend() { chatgpt.send(await chatgpt.getChatData('latest', 'msg', 'user', 'latest'))
|
|
1531
|
+
async resend() { chatgpt.send(await chatgpt.getChatData('latest', 'msg', 'user', 'latest')) },
|
|
1538
1532
|
|
|
1539
1533
|
response: {
|
|
1540
|
-
continue() { try { chatgpt.getContinueBtn().click()
|
|
1534
|
+
continue() { try { chatgpt.getContinueBtn().click() } catch (err) { console.error(err.message) }},
|
|
1541
1535
|
|
|
1542
1536
|
get() {
|
|
1543
1537
|
// * Returns response via DOM by index arg if OpenAI chat page is active, otherwise uses API w/ following args:
|
|
@@ -1546,25 +1540,25 @@ const chatgpt = {
|
|
|
1546
1540
|
// regenResponseToGet = index of regenerated response to get (defaults to latest if '' unpassed)
|
|
1547
1541
|
|
|
1548
1542
|
if (window.location.href.startsWith('https://chatgpt.com/c/'))
|
|
1549
|
-
return this.getFromDOM.apply(null, arguments)
|
|
1550
|
-
else return this.getFromAPI.apply(null, arguments)
|
|
1543
|
+
return this.getFromDOM.apply(null, arguments)
|
|
1544
|
+
else return this.getFromAPI.apply(null, arguments)
|
|
1551
1545
|
},
|
|
1552
1546
|
|
|
1553
1547
|
getFromAPI(chatToGet, responseToGet) {
|
|
1554
1548
|
// chatToGet = index|title|id of chat to get (defaults to latest if '' or unpassed)
|
|
1555
1549
|
// responseToGet = index of response to get (defaults to latest if '' or unpassed)
|
|
1556
1550
|
|
|
1557
|
-
chatToGet = chatToGet || 'latest'; responseToGet = responseToGet || 'latest'
|
|
1558
|
-
return chatgpt.getChatData(chatToGet, 'msg', 'chatgpt', responseToGet)
|
|
1551
|
+
chatToGet = chatToGet || 'latest'; responseToGet = responseToGet || 'latest'
|
|
1552
|
+
return chatgpt.getChatData(chatToGet, 'msg', 'chatgpt', responseToGet)
|
|
1559
1553
|
},
|
|
1560
1554
|
|
|
1561
1555
|
getFromDOM(pos) {
|
|
1562
1556
|
const responseDivs = document.querySelectorAll('div[data-message-author-role=assistant]'),
|
|
1563
|
-
strPos = pos.toString().toLowerCase()
|
|
1564
|
-
let response = ''
|
|
1565
|
-
if (!responseDivs.length) return console.error('No conversation found!')
|
|
1557
|
+
strPos = pos.toString().toLowerCase()
|
|
1558
|
+
let response = ''
|
|
1559
|
+
if (!responseDivs.length) return console.error('No conversation found!')
|
|
1566
1560
|
if (/last|final/.test(strPos)) // get last response
|
|
1567
|
-
response = responseDivs[responseDivs.length - 1].textContent
|
|
1561
|
+
response = responseDivs[responseDivs.length - 1].textContent
|
|
1568
1562
|
else { // get nth response
|
|
1569
1563
|
const nthOfResponse = (
|
|
1570
1564
|
|
|
@@ -1587,140 +1581,142 @@ const chatgpt = {
|
|
|
1587
1581
|
* ( /(?:ty|ieth)$/.test(strPos) ? 10 : 1 ) // x 10 if -ty/ieth
|
|
1588
1582
|
+ ( /teen(?:th)?$/.test(strPos) ? 10 : 0 ) // + 10 if -teen/teenth
|
|
1589
1583
|
|
|
1590
|
-
)
|
|
1591
|
-
response = responseDivs[nthOfResponse - 1].textContent
|
|
1584
|
+
)
|
|
1585
|
+
response = responseDivs[nthOfResponse - 1].textContent
|
|
1592
1586
|
}
|
|
1593
|
-
response = response.replace(/^ChatGPT(?:ChatGPT)?/, '')
|
|
1594
|
-
return response
|
|
1587
|
+
response = response.replace(/^ChatGPT(?:ChatGPT)?/, '') // strip sender name
|
|
1588
|
+
return response
|
|
1595
1589
|
},
|
|
1596
1590
|
|
|
1597
|
-
getLast() { return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
1598
|
-
regenerate() { try { chatgpt.getRegenerateBtn().click()
|
|
1599
|
-
stopGenerating() { try { chatgpt.getStopBtn().click()
|
|
1591
|
+
getLast() { return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest') },
|
|
1592
|
+
regenerate() { try { chatgpt.getRegenerateBtn().click() } catch (err) { console.error(err.message) }},
|
|
1593
|
+
stopGenerating() { try { chatgpt.getStopBtn().click() } catch (err) { console.error(err.message) }}
|
|
1600
1594
|
},
|
|
1601
1595
|
|
|
1602
|
-
reviewCode() { chatgpt.code.review()
|
|
1603
|
-
scrollToBottom() { try { chatgpt.getScrollBtn().click()
|
|
1596
|
+
reviewCode() { chatgpt.code.review() },
|
|
1597
|
+
scrollToBottom() { try { chatgpt.getScrollBtn().click() } catch (err) { console.error(err.message) }},
|
|
1604
1598
|
|
|
1605
1599
|
send(msg, method='') {
|
|
1606
|
-
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i]
|
|
1607
|
-
return console.error(`Argument ${ i + 1 } must be a string!`)
|
|
1608
|
-
const textArea = chatgpt.getChatBox()
|
|
1609
|
-
if (!textArea) return console.error('Chatbar element not found!')
|
|
1610
|
-
const msgP = document.createElement('p'); msgP.textContent = msg
|
|
1611
|
-
textArea.replaceChild(msgP, textArea.querySelector('p'))
|
|
1612
|
-
textArea.dispatchEvent(new Event('input', { bubbles: true }))
|
|
1600
|
+
for (let i = 0 ; i < arguments.length ; i++) if (typeof arguments[i] != 'string')
|
|
1601
|
+
return console.error(`Argument ${ i + 1 } must be a string!`)
|
|
1602
|
+
const textArea = chatgpt.getChatBox()
|
|
1603
|
+
if (!textArea) return console.error('Chatbar element not found!')
|
|
1604
|
+
const msgP = document.createElement('p'); msgP.textContent = msg
|
|
1605
|
+
textArea.replaceChild(msgP, textArea.querySelector('p'))
|
|
1606
|
+
textArea.dispatchEvent(new Event('input', { bubbles: true })) // enable send button
|
|
1613
1607
|
setTimeout(function delaySend() {
|
|
1614
|
-
const sendBtn = chatgpt.getSendButton()
|
|
1608
|
+
const sendBtn = chatgpt.getSendButton()
|
|
1615
1609
|
if (!sendBtn?.hasAttribute('disabled')) { // send msg
|
|
1616
1610
|
method.toLowerCase() == 'click' || chatgpt.browser.isMobile() ? sendBtn.click()
|
|
1617
|
-
: textArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
|
|
1618
|
-
} else setTimeout(delaySend, 222)
|
|
1619
|
-
}, 222)
|
|
1611
|
+
: textArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
|
|
1612
|
+
} else setTimeout(delaySend, 222)
|
|
1613
|
+
}, 222)
|
|
1620
1614
|
},
|
|
1621
1615
|
|
|
1622
1616
|
sendInNewChat(msg) {
|
|
1623
|
-
if (typeof msg
|
|
1624
|
-
try { chatgpt.getNewChatBtn().click()
|
|
1625
|
-
setTimeout(() =>
|
|
1617
|
+
if (typeof msg != 'string') return console.error('Message must be a string!')
|
|
1618
|
+
try { chatgpt.getNewChatBtn().click() } catch (err) { return console.error(err.message) }
|
|
1619
|
+
setTimeout(() => chatgpt.send(msg), 500)
|
|
1626
1620
|
},
|
|
1627
1621
|
|
|
1628
1622
|
settings: {
|
|
1629
1623
|
scheme: {
|
|
1630
|
-
isDark() { return document.documentElement.classList.contains('dark')
|
|
1631
|
-
isLight() { return document.documentElement.classList.contains('light')
|
|
1624
|
+
isDark() { return document.documentElement.classList.contains('dark') },
|
|
1625
|
+
isLight() { return document.documentElement.classList.contains('light') },
|
|
1632
1626
|
set(value) {
|
|
1633
1627
|
|
|
1634
1628
|
// Validate value
|
|
1635
|
-
const validValues = ['dark', 'light', 'system']
|
|
1636
|
-
if (!value) return console.error('Please specify a scheme value!')
|
|
1637
|
-
if (!validValues.includes(value))
|
|
1629
|
+
const validValues = ['dark', 'light', 'system']
|
|
1630
|
+
if (!value) return console.error('Please specify a scheme value!')
|
|
1631
|
+
if (!validValues.includes(value))
|
|
1632
|
+
return console.error(`Invalid scheme value. Valid values are [${ validValues }]`)
|
|
1638
1633
|
|
|
1639
1634
|
// Determine scheme to set
|
|
1640
|
-
let schemeToSet = value
|
|
1641
|
-
if (value == 'system')
|
|
1642
|
-
|
|
1643
|
-
|
|
1635
|
+
let schemeToSet = value
|
|
1636
|
+
if (value == 'system')
|
|
1637
|
+
schemeToSet = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
1638
|
+
localStorage.setItem('theme', value)
|
|
1639
|
+
console.info(`Scheme set to ${ value.toUpperCase() }.`)
|
|
1644
1640
|
|
|
1645
1641
|
// Toggle scheme if necessary
|
|
1646
|
-
if (!document.documentElement.classList.contains(schemeToSet)) this.toggle()
|
|
1642
|
+
if (!document.documentElement.classList.contains(schemeToSet)) this.toggle()
|
|
1647
1643
|
},
|
|
1648
1644
|
toggle() {
|
|
1649
|
-
const [schemeToRemove, schemeToAdd] = this.isDark() ? ['dark', 'light'] : ['light', 'dark']
|
|
1650
|
-
document.documentElement.classList.replace(schemeToRemove, schemeToAdd)
|
|
1651
|
-
document.documentElement.style.colorScheme = schemeToAdd
|
|
1652
|
-
localStorage.setItem('theme', schemeToAdd)
|
|
1645
|
+
const [schemeToRemove, schemeToAdd] = this.isDark() ? ['dark', 'light'] : ['light', 'dark']
|
|
1646
|
+
document.documentElement.classList.replace(schemeToRemove, schemeToAdd)
|
|
1647
|
+
document.documentElement.style.colorScheme = schemeToAdd
|
|
1648
|
+
localStorage.setItem('theme', schemeToAdd)
|
|
1653
1649
|
}
|
|
1654
1650
|
}
|
|
1655
1651
|
},
|
|
1656
1652
|
|
|
1657
1653
|
async sentiment(text, entity) {
|
|
1658
|
-
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i]
|
|
1659
|
-
return console.error(`Argument ${ i + 1 } must be a string.`)
|
|
1654
|
+
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] != 'string')
|
|
1655
|
+
return console.error(`Argument ${ i + 1 } must be a string.`)
|
|
1660
1656
|
chatgpt.send('What is the sentiment of the following text'
|
|
1661
1657
|
+ ( entity ? ` towards the entity ${ entity },` : '')
|
|
1662
|
-
+ ' from strongly negative to strongly positive?\n\n' + text )
|
|
1663
|
-
console.info('Analyzing sentiment...')
|
|
1664
|
-
await chatgpt.isIdle()
|
|
1665
|
-
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
1658
|
+
+ ' from strongly negative to strongly positive?\n\n' + text )
|
|
1659
|
+
console.info('Analyzing sentiment...')
|
|
1660
|
+
await chatgpt.isIdle()
|
|
1661
|
+
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
1666
1662
|
},
|
|
1667
1663
|
|
|
1668
|
-
setScheme(value) { chatgpt.settings.scheme.set(value)
|
|
1664
|
+
setScheme(value) { chatgpt.settings.scheme.set(value) },
|
|
1669
1665
|
|
|
1670
1666
|
shareChat(chatToGet, method = 'clipboard') {
|
|
1671
1667
|
// chatToGet = index|title|id of chat to get (defaults to latest if '' or unpassed)
|
|
1672
1668
|
// method = [ 'alert'|'clipboard' ] (defaults to 'clipboard' if '' or unpassed)
|
|
1673
1669
|
|
|
1674
|
-
const validMethods = ['alert', 'notify', 'notification', 'clipboard', 'copy']
|
|
1670
|
+
const validMethods = ['alert', 'notify', 'notification', 'clipboard', 'copy']
|
|
1675
1671
|
if (!validMethods.includes(method)) return console.error(
|
|
1676
|
-
|
|
1672
|
+
`Invalid method '${method}' passed. Valid methods are [${validMethods}].`)
|
|
1677
1673
|
|
|
1678
1674
|
const getChatNode = token => {
|
|
1679
1675
|
return new Promise((resolve, reject) => {
|
|
1680
|
-
const xhr = new XMLHttpRequest()
|
|
1676
|
+
const xhr = new XMLHttpRequest()
|
|
1681
1677
|
chatgpt.getChatData(chatToGet).then(chat => {
|
|
1682
|
-
xhr.open('GET', `${ chatgpt.endpoints.openAI.chat }/${ chat.id }`, true)
|
|
1683
|
-
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
1684
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
1678
|
+
xhr.open('GET', `${ chatgpt.endpoints.openAI.chat }/${ chat.id }`, true)
|
|
1679
|
+
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
1680
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
1685
1681
|
xhr.onload = () => {
|
|
1686
|
-
if (xhr.status
|
|
1687
|
-
return reject('🤖 chatgpt.js >> Request failed. Cannot retrieve chat node.')
|
|
1688
|
-
return resolve(JSON.parse(xhr.responseText).current_node)
|
|
1689
|
-
}
|
|
1690
|
-
xhr.send()
|
|
1691
|
-
})
|
|
1682
|
+
if (xhr.status != 200)
|
|
1683
|
+
return reject('🤖 chatgpt.js >> Request failed. Cannot retrieve chat node.')
|
|
1684
|
+
return resolve(JSON.parse(xhr.responseText).current_node) // chat messages until now
|
|
1685
|
+
}
|
|
1686
|
+
xhr.send()
|
|
1687
|
+
})})}
|
|
1692
1688
|
|
|
1693
1689
|
const makeChatToShare = (token, node) => {
|
|
1694
1690
|
return new Promise((resolve, reject) => {
|
|
1695
|
-
const xhr = new XMLHttpRequest()
|
|
1691
|
+
const xhr = new XMLHttpRequest()
|
|
1696
1692
|
chatgpt.getChatData(chatToGet).then(chat => {
|
|
1697
|
-
xhr.open('POST', chatgpt.endpoints.openAI.share_create, true)
|
|
1698
|
-
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
1699
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
1693
|
+
xhr.open('POST', chatgpt.endpoints.openAI.share_create, true)
|
|
1694
|
+
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
1695
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
1700
1696
|
xhr.onload = () => {
|
|
1701
|
-
if (xhr.status
|
|
1702
|
-
return reject('🤖 chatgpt.js >> Request failed. Cannot initialize share chat.')
|
|
1703
|
-
return resolve(JSON.parse(xhr.responseText))
|
|
1704
|
-
}
|
|
1697
|
+
if (xhr.status != 200)
|
|
1698
|
+
return reject('🤖 chatgpt.js >> Request failed. Cannot initialize share chat.')
|
|
1699
|
+
return resolve(JSON.parse(xhr.responseText)) // return untouched data
|
|
1700
|
+
}
|
|
1705
1701
|
xhr.send(JSON.stringify({ // request body
|
|
1706
1702
|
current_node_id: node, // by getChatNode
|
|
1707
1703
|
conversation_id: chat.id, // current chat id
|
|
1708
1704
|
is_anonymous: true // show user name in the conversation or not
|
|
1709
|
-
}))
|
|
1710
|
-
})
|
|
1705
|
+
}))
|
|
1706
|
+
})})}
|
|
1711
1707
|
|
|
1712
1708
|
const confirmShareChat = (token, data) => {
|
|
1713
1709
|
return new Promise((resolve, reject) => {
|
|
1714
|
-
const xhr = new XMLHttpRequest()
|
|
1715
|
-
xhr.open('PATCH', `${ chatgpt.endpoints.openAI.share }/${ data.share_id }`, true)
|
|
1716
|
-
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
1717
|
-
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
1710
|
+
const xhr = new XMLHttpRequest()
|
|
1711
|
+
xhr.open('PATCH', `${ chatgpt.endpoints.openAI.share }/${ data.share_id }`, true)
|
|
1712
|
+
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
1713
|
+
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
|
1718
1714
|
xhr.onload = () => {
|
|
1719
|
-
if (xhr.status
|
|
1720
|
-
return reject('🤖 chatgpt.js >> Request failed. Cannot share chat.')
|
|
1721
|
-
console.info(`Chat shared at '${ data.share_url }'`)
|
|
1722
|
-
return resolve()
|
|
1723
|
-
}
|
|
1715
|
+
if (xhr.status != 200)
|
|
1716
|
+
return reject('🤖 chatgpt.js >> Request failed. Cannot share chat.')
|
|
1717
|
+
console.info(`Chat shared at '${ data.share_url }'`)
|
|
1718
|
+
return resolve() // the response has nothing useful
|
|
1719
|
+
}
|
|
1724
1720
|
xhr.send(JSON.stringify({ // request body
|
|
1725
1721
|
share_id: data.share_id,
|
|
1726
1722
|
highlighted_message_id: data.highlighted_message_id,
|
|
@@ -1728,155 +1724,146 @@ const chatgpt = {
|
|
|
1728
1724
|
is_public: true, // must be true or it'll cause a 404 error
|
|
1729
1725
|
is_visible: data.is_visible,
|
|
1730
1726
|
is_anonymous: data.is_anonymous
|
|
1731
|
-
}))
|
|
1732
|
-
})
|
|
1727
|
+
}))
|
|
1728
|
+
})}
|
|
1733
1729
|
|
|
1734
1730
|
return new Promise(resolve => {
|
|
1735
1731
|
chatgpt.getAccessToken().then(token => { // get access token
|
|
1736
1732
|
getChatNode(token).then(node => { // get chat node
|
|
1737
1733
|
makeChatToShare(token, node).then(data => {
|
|
1738
1734
|
confirmShareChat(token, data).then(() => {
|
|
1739
|
-
if (['copy', 'clipboard'].includes(method)) navigator.clipboard.writeText(data.share_url)
|
|
1735
|
+
if (['copy', 'clipboard'].includes(method)) navigator.clipboard.writeText(data.share_url)
|
|
1740
1736
|
else chatgpt.alert('🚀 Share link created!',
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
[ function openLink() { window.open(data.share_url, '_blank', 'noopener')
|
|
1744
|
-
function copyLink() { navigator.clipboard.writeText(data.share_url)
|
|
1745
|
-
resolve(data.share_url)
|
|
1746
|
-
})
|
|
1737
|
+
`"${data.title}" is available at: <a target="blank" rel="noopener" href="${
|
|
1738
|
+
data.share_url}">${data.share_url}</a>`,
|
|
1739
|
+
[ function openLink() { window.open(data.share_url, '_blank', 'noopener') },
|
|
1740
|
+
function copyLink() { navigator.clipboard.writeText(data.share_url) }])
|
|
1741
|
+
resolve(data.share_url)
|
|
1742
|
+
})})})})})
|
|
1747
1743
|
},
|
|
1748
1744
|
|
|
1749
|
-
showFooter() { chatgpt.footer.show()
|
|
1750
|
-
showHeader() { chatgpt.header.show()
|
|
1745
|
+
showFooter() { chatgpt.footer.show() },
|
|
1746
|
+
showHeader() { chatgpt.header.show() },
|
|
1751
1747
|
|
|
1752
1748
|
sidebar: {
|
|
1753
|
-
|
|
1749
|
+
elems: [], observer: {},
|
|
1754
1750
|
|
|
1755
1751
|
activateObserver() {
|
|
1756
1752
|
|
|
1757
1753
|
// Stop the previous observer to preserve resources
|
|
1758
1754
|
if (this.observer instanceof MutationObserver)
|
|
1759
|
-
try { this.observer.disconnect()
|
|
1755
|
+
try { this.observer.disconnect() } catch (e) {}
|
|
1760
1756
|
|
|
1761
|
-
if (!this.
|
|
1757
|
+
if (!this.elems.length) return console.error('🤖 chatgpt.js >> No elems to append!')
|
|
1762
1758
|
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
for (let navLink of document.querySelectorAll(
|
|
1759
|
+
// Grab CSS from original website elems
|
|
1760
|
+
let cssClasses
|
|
1761
|
+
for (let navLink of document.querySelectorAll(chatgpt.selectors.links.sidebarItem))
|
|
1766
1762
|
if (/.*chat/.exec(navLink.text)[0]) {
|
|
1767
|
-
cssClasses = navLink.classList
|
|
1768
|
-
navLink.parentNode.style.margin = '2px 0'
|
|
1769
|
-
break
|
|
1763
|
+
cssClasses = navLink.classList
|
|
1764
|
+
navLink.parentNode.style.margin = '2px 0' // add v-margins for consistency across all inserted btns
|
|
1765
|
+
break
|
|
1770
1766
|
}
|
|
1771
|
-
}
|
|
1772
1767
|
|
|
1773
|
-
// Apply CSS to make the added
|
|
1774
|
-
this.
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
})
|
|
1768
|
+
// Apply CSS to make the added elems look like they belong to the website
|
|
1769
|
+
this.elems.forEach(elem => {
|
|
1770
|
+
elem.setAttribute('class', cssClasses)
|
|
1771
|
+
elem.style.maxHeight = elem.style.minHeight = '44px' // fix the height of the element
|
|
1772
|
+
elem.style.margin = '2px 0'
|
|
1773
|
+
})
|
|
1779
1774
|
|
|
1780
1775
|
// Create MutationObserver instance
|
|
1781
|
-
const navBar = document.querySelector(
|
|
1782
|
-
if (!navBar) return console.error('Sidebar element not found!')
|
|
1783
|
-
this.observer = new MutationObserver(mutations =>
|
|
1776
|
+
const navBar = document.querySelector(chatgpt.selectors.chatHistory)
|
|
1777
|
+
if (!navBar) return console.error('Sidebar element not found!')
|
|
1778
|
+
this.observer = new MutationObserver(mutations =>
|
|
1784
1779
|
mutations.forEach(mutation => {
|
|
1785
1780
|
if ((mutation.type == 'childList' && mutation.addedNodes.length) ||
|
|
1786
1781
|
(mutation.type == 'attributes' && mutation.attributeName == 'data-chatgptjs')) // check for trigger
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
} catch (err) { console.error(err); }
|
|
1795
|
-
});
|
|
1796
|
-
});
|
|
1797
|
-
});
|
|
1782
|
+
this.elems.forEach(elem => { // try to insert each element...
|
|
1783
|
+
if (!navBar.contains(elem)) // ...if it's not already present...
|
|
1784
|
+
try { navBar.querySelector('a').parentNode.before(elem) } // ...at top of sidebar
|
|
1785
|
+
catch (err) { console.error(err) }
|
|
1786
|
+
})
|
|
1787
|
+
})
|
|
1788
|
+
)
|
|
1798
1789
|
|
|
1799
|
-
this.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true })
|
|
1790
|
+
this.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true })
|
|
1800
1791
|
},
|
|
1801
1792
|
|
|
1802
|
-
append(
|
|
1793
|
+
append(elem, attrs = {}) {
|
|
1803
1794
|
// element = 'button' | 'dropdown' REQUIRED (no default value)
|
|
1804
1795
|
// attrs = { ... }
|
|
1805
1796
|
// attrs for 'button': 'icon' = src string, 'label' = string, 'onclick' = function
|
|
1806
1797
|
// attrs for 'dropdown': 'items' = [ { text: string, value: string }, ... ] array of objects
|
|
1807
1798
|
// where 'text' is the displayed text of the option and 'value' is the value of the option
|
|
1808
|
-
const
|
|
1809
|
-
if (!
|
|
1810
|
-
return console.error('🤖 chatgpt.js >> Please supply a valid string element name!')
|
|
1811
|
-
|
|
1812
|
-
if (!
|
|
1813
|
-
return console.error(`🤖 chatgpt.js >> Invalid element! Valid
|
|
1814
|
-
|
|
1815
|
-
const
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
if (
|
|
1819
|
-
|
|
1799
|
+
const validElems = ['button', 'dropdown']
|
|
1800
|
+
if (!elem || typeof elem != 'string') // Element not passed or invalid type
|
|
1801
|
+
return console.error('🤖 chatgpt.js >> Please supply a valid string element name!')
|
|
1802
|
+
elem = elem.toLowerCase()
|
|
1803
|
+
if (!validElems.includes(elem)) // Element not in list
|
|
1804
|
+
return console.error(`🤖 chatgpt.js >> Invalid element! Valid elems are [${validElems}]`)
|
|
1805
|
+
|
|
1806
|
+
const newElem = document.createElement(elem == 'dropdown' ? 'select' : elem)
|
|
1807
|
+
newElem.id = Math.floor(chatgpt.randomFloat() * 1000000) + Date.now() // Add random id to the element
|
|
1808
|
+
|
|
1809
|
+
if (elem == 'button') {
|
|
1810
|
+
newElem.textContent = attrs?.label && typeof attrs.label == 'string'
|
|
1820
1811
|
? attrs.label
|
|
1821
|
-
: 'chatgpt.js button'
|
|
1822
|
-
|
|
1823
|
-
const icon = document.createElement('img');
|
|
1812
|
+
: 'chatgpt.js button'
|
|
1813
|
+
const icon = document.createElement('img')
|
|
1824
1814
|
icon.src = attrs?.icon && typeof attrs.icon == 'string' // Can also be base64 encoded image string
|
|
1825
1815
|
? attrs.icon // Add icon to button element if given, else default one
|
|
1826
|
-
:
|
|
1827
|
-
icon.width = 18
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
newElement.onclick = attrs?.onclick && typeof attrs.onclick == 'function'
|
|
1831
|
-
? attrs.onclick
|
|
1832
|
-
: function() {};
|
|
1816
|
+
: `${chatgpt.endpoints.assets}/starters/chrome/extension/icons/icon128.png`
|
|
1817
|
+
icon.width = 18
|
|
1818
|
+
newElem.firstChild.before(icon)
|
|
1819
|
+
newElem.onclick = attrs?.onclick && typeof attrs.onclick == 'function' ? attrs.onclick : function(){}
|
|
1833
1820
|
}
|
|
1834
1821
|
|
|
1835
|
-
else if (
|
|
1822
|
+
else if (elem == 'dropdown') {
|
|
1836
1823
|
if (!attrs?.items || // There no are options to add
|
|
1837
1824
|
!Array.isArray(attrs.items) || // It's not an array
|
|
1838
1825
|
!attrs.items.length) // The array is empty
|
|
1839
|
-
attrs.items = [{ text: '🤖 chatgpt.js option', value: 'chatgpt.js option value' }]
|
|
1826
|
+
attrs.items = [{ text: '🤖 chatgpt.js option', value: 'chatgpt.js option value' }] // Set default dropdown entry
|
|
1840
1827
|
|
|
1841
1828
|
if (!attrs.items.every(el => typeof el == 'object')) // The entries of the array are not objects
|
|
1842
|
-
return console.error('\'items\' must be an array of objects!')
|
|
1829
|
+
return console.error('\'items\' must be an array of objects!')
|
|
1843
1830
|
|
|
1844
1831
|
attrs.items.forEach(item => {
|
|
1845
|
-
const optionElement = document.createElement('option')
|
|
1846
|
-
optionElement.textContent = item?.text
|
|
1847
|
-
optionElement.value = item?.value
|
|
1848
|
-
|
|
1849
|
-
})
|
|
1832
|
+
const optionElement = document.createElement('option')
|
|
1833
|
+
optionElement.textContent = item?.text
|
|
1834
|
+
optionElement.value = item?.value
|
|
1835
|
+
newElem.add(optionElement)
|
|
1836
|
+
})
|
|
1850
1837
|
}
|
|
1851
1838
|
|
|
1852
1839
|
|
|
1853
|
-
// Fix for blank background on dropdown
|
|
1854
|
-
if (
|
|
1840
|
+
// Fix for blank background on dropdown elems
|
|
1841
|
+
if (elem == 'dropdown') newElem.style.backgroundColor = 'var(--gray-900, rgb(32,33,35))'
|
|
1855
1842
|
|
|
1856
|
-
this.
|
|
1857
|
-
this.activateObserver()
|
|
1858
|
-
document.body.setAttribute('data-chatgptjs', 'observer-trigger')
|
|
1843
|
+
this.elems.push(newElem)
|
|
1844
|
+
this.activateObserver()
|
|
1845
|
+
document.body.setAttribute('data-chatgptjs', 'observer-trigger') // add attribute to trigger the observer
|
|
1859
1846
|
|
|
1860
|
-
return
|
|
1847
|
+
return newElem.id // Return the element id
|
|
1861
1848
|
},
|
|
1862
1849
|
|
|
1863
1850
|
exists() { return !!chatgpt.getNewChatLink(); },
|
|
1864
|
-
hide() { this.isOn() ? this.toggle() : console.info('Sidebar already hidden!')
|
|
1865
|
-
show() { this.isOff() ? this.toggle() : console.info('Sidebar already shown!')
|
|
1866
|
-
isOff() { return !this.isOn()
|
|
1851
|
+
hide() { this.isOn() ? this.toggle() : console.info('Sidebar already hidden!') },
|
|
1852
|
+
show() { this.isOff() ? this.toggle() : console.info('Sidebar already shown!') },
|
|
1853
|
+
isOff() { return !this.isOn() },
|
|
1867
1854
|
isOn() {
|
|
1868
1855
|
const sidebar = (() => {
|
|
1869
|
-
return chatgpt.sidebar.exists() ? document.querySelector(
|
|
1870
|
-
if (!sidebar) { console.error('Sidebar element not found!'); return false
|
|
1856
|
+
return chatgpt.sidebar.exists() ? document.querySelector(chatgpt.selectors.sidebar) : null })()
|
|
1857
|
+
if (!sidebar) { console.error('Sidebar element not found!'); return false }
|
|
1871
1858
|
else return chatgpt.browser.isMobile() ?
|
|
1872
1859
|
document.documentElement.style.overflow == 'hidden'
|
|
1873
|
-
: sidebar.style.visibility != 'hidden' && sidebar.style.width != '0px'
|
|
1860
|
+
: sidebar.style.visibility != 'hidden' && sidebar.style.width != '0px'
|
|
1874
1861
|
},
|
|
1875
1862
|
|
|
1876
1863
|
toggle() {
|
|
1877
|
-
const sidebarToggle = document.querySelector(
|
|
1878
|
-
if (!sidebarToggle) console.error('Sidebar toggle not found!')
|
|
1879
|
-
sidebarToggle.click()
|
|
1864
|
+
const sidebarToggle = document.querySelector(chatgpt.selectors.btns.sidebar)
|
|
1865
|
+
if (!sidebarToggle) console.error('Sidebar toggle not found!')
|
|
1866
|
+
sidebarToggle.click()
|
|
1880
1867
|
},
|
|
1881
1868
|
|
|
1882
1869
|
async isLoaded(timeout = 5000) {
|
|
@@ -1892,18 +1879,18 @@ const chatgpt = {
|
|
|
1892
1879
|
}
|
|
1893
1880
|
},
|
|
1894
1881
|
|
|
1895
|
-
startNewChat() { try { chatgpt.getNewChatBtn().click()
|
|
1882
|
+
startNewChat() { try { chatgpt.getNewChatBtn().click() } catch (err) { console.error(err.message) }},
|
|
1896
1883
|
stop() { chatgpt.response.stopGenerating(); },
|
|
1897
1884
|
|
|
1898
1885
|
async suggest(ideaType, details) {
|
|
1899
1886
|
if (!ideaType) return console.error('ideaType (1st argument) not supplied'
|
|
1900
|
-
+
|
|
1901
|
-
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i]
|
|
1902
|
-
return console.error(`Argument ${ i + 1 } must be a string.`)
|
|
1903
|
-
chatgpt.send('Suggest some names. ' + ( details || '' ))
|
|
1904
|
-
console.info(`Creating ${ ideaType }...`)
|
|
1905
|
-
await chatgpt.isIdle()
|
|
1906
|
-
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
1887
|
+
+ `(e.g. 'gifts', 'names', 'recipes', etc.)`)
|
|
1888
|
+
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] != 'string')
|
|
1889
|
+
return console.error(`Argument ${ i + 1 } must be a string.`)
|
|
1890
|
+
chatgpt.send('Suggest some names. ' + ( details || '' ))
|
|
1891
|
+
console.info(`Creating ${ ideaType }...`)
|
|
1892
|
+
await chatgpt.isIdle()
|
|
1893
|
+
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
1907
1894
|
},
|
|
1908
1895
|
|
|
1909
1896
|
speak(msg, { voice = 2, pitch = 2, speed = 1.1, onend } = {} ) {
|
|
@@ -1934,72 +1921,72 @@ const chatgpt = {
|
|
|
1934
1921
|
},
|
|
1935
1922
|
|
|
1936
1923
|
async summarize(text) {
|
|
1937
|
-
if (!text) return console.error('Text (1st) argument not supplied. Pass some text!')
|
|
1938
|
-
if (typeof text
|
|
1939
|
-
chatgpt.send('Summarize the following text:\n\n' + text)
|
|
1940
|
-
console.info('Summarizing text...')
|
|
1941
|
-
await chatgpt.isIdle()
|
|
1942
|
-
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
1924
|
+
if (!text) return console.error('Text (1st) argument not supplied. Pass some text!')
|
|
1925
|
+
if (typeof text != 'string') return console.error('Text argument must be a string!')
|
|
1926
|
+
chatgpt.send('Summarize the following text:\n\n' + text)
|
|
1927
|
+
console.info('Summarizing text...')
|
|
1928
|
+
await chatgpt.isIdle()
|
|
1929
|
+
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
1943
1930
|
},
|
|
1944
1931
|
|
|
1945
1932
|
toggleScheme() { chatgpt.settings.scheme.toggle(); },
|
|
1946
1933
|
|
|
1947
1934
|
async translate(text, outputLang) {
|
|
1948
|
-
if (!text) return console.error('Text (1st) argument not supplied. Pass some text!')
|
|
1949
|
-
if (!outputLang) return console.error('outputLang (2nd) argument not supplied. Pass a language!')
|
|
1950
|
-
for (let i = 0; i < arguments.length; i++) if (typeof arguments[i]
|
|
1951
|
-
return console.error(`Argument ${ i + 1 } must be a string!`)
|
|
1952
|
-
chatgpt.send(
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest');
|
|
1935
|
+
if (!text) return console.error('Text (1st) argument not supplied. Pass some text!')
|
|
1936
|
+
if (!outputLang) return console.error('outputLang (2nd) argument not supplied. Pass a language!')
|
|
1937
|
+
for (let i = 0 ; i < arguments.length ; i++) if (typeof arguments[i] != 'string')
|
|
1938
|
+
return console.error(`Argument ${ i + 1 } must be a string!`)
|
|
1939
|
+
chatgpt.send(`Translate the following text to ${outputLang}. Only reply with the translation.\n\n${text}`)
|
|
1940
|
+
console.info('Translating text...')
|
|
1941
|
+
await chatgpt.isIdle()
|
|
1942
|
+
return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest')
|
|
1957
1943
|
},
|
|
1958
1944
|
|
|
1959
|
-
unminify() { chatgpt.code.unminify()
|
|
1945
|
+
unminify() { chatgpt.code.unminify() },
|
|
1960
1946
|
|
|
1961
1947
|
uuidv4() {
|
|
1962
1948
|
try {
|
|
1963
1949
|
// use native secure uuid generator when available
|
|
1964
|
-
return crypto.randomUUID()
|
|
1950
|
+
return crypto.randomUUID()
|
|
1965
1951
|
} catch(_e) {
|
|
1966
|
-
let d = new Date().getTime()
|
|
1952
|
+
let d = new Date().getTime() // get current timestamp in ms (to ensure UUID uniqueness)
|
|
1967
1953
|
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
1968
1954
|
const r = ( // generate random nibble
|
|
1969
|
-
( d + (window.crypto.getRandomValues(new Uint32Array(1))[0] / (Math.pow(2, 32) - 1))*16)%16 | 0 )
|
|
1970
|
-
d = Math.floor(d/16)
|
|
1955
|
+
( d + (window.crypto.getRandomValues(new Uint32Array(1))[0] / (Math.pow(2, 32) - 1))*16)%16 | 0 )
|
|
1956
|
+
d = Math.floor(d/16) // correspond each UUID digit to unique 4-bit chunks of timestamp
|
|
1971
1957
|
return ( c == 'x' ? r : (r&0x3|0x8) ).toString(16); // generate random hexadecimal digit
|
|
1972
|
-
})
|
|
1973
|
-
return uuid
|
|
1958
|
+
})
|
|
1959
|
+
return uuid
|
|
1974
1960
|
}
|
|
1975
1961
|
},
|
|
1976
1962
|
|
|
1977
|
-
writeCode() { chatgpt.code.write()
|
|
1978
|
-
}
|
|
1963
|
+
writeCode() { chatgpt.code.write() }
|
|
1964
|
+
}
|
|
1979
1965
|
|
|
1980
|
-
chatgpt.scheme = { ...chatgpt.settings.scheme }
|
|
1966
|
+
chatgpt.scheme = { ...chatgpt.settings.scheme } // copy `chatgpt.settings.scheme` methods into `chatgpt.scheme`
|
|
1981
1967
|
|
|
1982
1968
|
// Create chatgpt.[actions]Button(identifier) functions
|
|
1983
|
-
const cjsBtnActions = ['click', 'get'], cjsTargetTypes = [ 'button', 'link', 'div', 'response' ]
|
|
1969
|
+
const cjsBtnActions = ['click', 'get'], cjsTargetTypes = [ 'button', 'link', 'div', 'response' ]
|
|
1984
1970
|
for (const btnAction of cjsBtnActions) {
|
|
1985
|
-
chatgpt[btnAction
|
|
1986
|
-
const
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
for (const
|
|
1991
|
-
if (
|
|
1992
|
-
return
|
|
1993
|
-
for (const navLink of document.querySelectorAll(
|
|
1994
|
-
if (navLink.textContent.toLowerCase().includes(
|
|
1995
|
-
return navLink
|
|
1996
|
-
|
|
1997
|
-
|
|
1971
|
+
chatgpt[`${btnAction}Button`] = function handleButton(btnIdentifier) {
|
|
1972
|
+
const btn = /^[.#]/.test(btnIdentifier) ? document.querySelector(btnIdentifier)
|
|
1973
|
+
: /send/i.test(btnIdentifier) ? document.querySelector(chatgpt.selectors.btns.send)
|
|
1974
|
+
: /scroll/i.test(btnIdentifier) ? document.querySelector(chatgpt.selectors.btns.scroll)
|
|
1975
|
+
: (function() { // get via text content
|
|
1976
|
+
for (const btn of document.querySelectorAll('button'))
|
|
1977
|
+
if (btn.textContent.toLowerCase().includes(btnIdentifier.toLowerCase()))
|
|
1978
|
+
return btn
|
|
1979
|
+
for (const navLink of document.querySelectorAll(chatgpt.selectors.links.sidebarItem))
|
|
1980
|
+
if (navLink.textContent.toLowerCase().includes(btnIdentifier.toLowerCase()))
|
|
1981
|
+
return navLink
|
|
1982
|
+
})()
|
|
1983
|
+
if (btnAction == 'click') btn.click() ; else return btn
|
|
1984
|
+
}
|
|
1998
1985
|
}
|
|
1999
1986
|
|
|
2000
1987
|
// Create ALIAS functions
|
|
2001
1988
|
const cjsFuncAliases = [
|
|
2002
|
-
['actAs', '
|
|
1989
|
+
['actAs', 'act', 'become', 'persona', 'premadePrompt', 'preMadePrompt', 'prePrompt', 'rolePlay', 'rp'],
|
|
2003
1990
|
['activateAutoRefresh', 'activateAutoRefresher', 'activateRefresher', 'activateSessionRefresher',
|
|
2004
1991
|
'autoRefresh', 'autoRefresher', 'autoRefreshSession', 'refresher', 'sessionRefresher'],
|
|
2005
1992
|
['continue', 'continueChat', 'continueGenerating', 'continueResponse'],
|
|
@@ -2015,8 +2002,9 @@ const cjsFuncAliases = [
|
|
|
2015
2002
|
['getScrollToBottomButton', 'getScrollButton'],
|
|
2016
2003
|
['getStopButton', 'getStopGeneratingButton'],
|
|
2017
2004
|
['getTextarea', 'getTextArea', 'getChatbar', 'getChatBar', 'getChatbox', 'getChatBox'],
|
|
2018
|
-
['
|
|
2019
|
-
['
|
|
2005
|
+
['getVoiceButton', 'getVoiceModeButton'],
|
|
2006
|
+
['isFullScreen', 'isFullscreen'],
|
|
2007
|
+
['isTempChat', 'isIncognito', 'isIncognitoMode', 'isTempChatMode'],
|
|
2020
2008
|
['minify', 'codeMinify', 'minifyCode'],
|
|
2021
2009
|
['new', 'newChat', 'startNewChat'],
|
|
2022
2010
|
['obfuscate', 'codeObfuscate', 'obfuscateCode'],
|
|
@@ -2053,6 +2041,8 @@ const cjsFuncSynonyms = [
|
|
|
2053
2041
|
['execute', 'interpret', 'interpreter', 'run'],
|
|
2054
2042
|
['firefox', 'ff'],
|
|
2055
2043
|
['generating', 'generation'],
|
|
2044
|
+
['login', 'logIn', 'logOn', 'signIn', 'signOn'],
|
|
2045
|
+
['logout', 'logOut', 'logOff', 'signOff', 'signOut'],
|
|
2056
2046
|
['message', 'msg'],
|
|
2057
2047
|
['minify', 'uglify'],
|
|
2058
2048
|
['refactor', 'rewrite'],
|
|
@@ -2062,6 +2052,7 @@ const cjsFuncSynonyms = [
|
|
|
2062
2052
|
['sentiment', 'attitude', 'emotion', 'feeling', 'opinion', 'perception'],
|
|
2063
2053
|
['speak', 'play', 'say', 'speech', 'talk', 'tts'],
|
|
2064
2054
|
['summarize', 'tldr'],
|
|
2055
|
+
['temp', 'temporary'],
|
|
2065
2056
|
['typing', 'generating'],
|
|
2066
2057
|
['unminify', 'beautify', 'prettify', 'prettyPrint']
|
|
2067
2058
|
];
|
|
@@ -2105,16 +2096,16 @@ function toCamelCase(words) {
|
|
|
2105
2096
|
return words.map((word, idx) => idx == 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)).join('') }
|
|
2106
2097
|
|
|
2107
2098
|
// Prefix console logs w/ '🤖 chatgpt.js >> '
|
|
2108
|
-
const consolePrefix = '🤖 chatgpt.js >> ', ogError = console.error, ogInfo = console.info
|
|
2099
|
+
const consolePrefix = '🤖 chatgpt.js >> ', ogError = console.error, ogInfo = console.info
|
|
2109
2100
|
console.error = (...args) => {
|
|
2110
|
-
if (!args[0].startsWith(consolePrefix)) ogError(consolePrefix + args[0], ...args.slice(1))
|
|
2111
|
-
else ogError(...args)
|
|
2112
|
-
}
|
|
2101
|
+
if (!args[0].startsWith(consolePrefix)) ogError(consolePrefix + args[0], ...args.slice(1))
|
|
2102
|
+
else ogError(...args)
|
|
2103
|
+
}
|
|
2113
2104
|
console.info = (msg) => {
|
|
2114
2105
|
if (!msg.startsWith(consolePrefix)) ogInfo(consolePrefix + msg);
|
|
2115
|
-
else ogInfo(msg)
|
|
2116
|
-
}
|
|
2106
|
+
else ogInfo(msg)
|
|
2107
|
+
}
|
|
2117
2108
|
|
|
2118
2109
|
// Export chatgpt object
|
|
2119
|
-
try { window.chatgpt = chatgpt
|
|
2120
|
-
try { module.exports = chatgpt
|
|
2110
|
+
try { window.chatgpt = chatgpt } catch (err) {} // for Greasemonkey
|
|
2111
|
+
try { module.exports = chatgpt } catch (err) {} // for CommonJS
|