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