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