@kudoai/chatgpt.js 3.0.2 → 3.1.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.
@@ -85,9 +85,10 @@ const chatgpt = { // eslint-disable-line no-redeclare
85
85
  document.head.append(modalStyle);
86
86
  }
87
87
  modalStyle.innerText = ( // update prev/new style contents
88
+ '.no-mobile-tap-outline { outline: none ; -webkit-tap-highlight-color: transparent }'
88
89
 
89
90
  // Background styles
90
- '.chatgpt-modal {'
91
+ + '.chatgpt-modal {'
91
92
  + 'position: fixed ; top: 0 ; left: 0 ; width: 100% ; height: 100% ;' // expand to full view-port
92
93
  + 'background-color: rgba(67, 70, 72, 0) ;' // init dim bg but no opacity
93
94
  + 'transition: background-color 0.05s ease ;' // speed to transition in show alert routine
@@ -148,7 +149,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
148
149
 
149
150
  // Create/append buttons (if provided) to buttons div
150
151
  const modalButtons = document.createElement('div');
151
- modalButtons.classList.add('modal-buttons');
152
+ modalButtons.classList.add('modal-buttons', 'no-mobile-tap-outline');
152
153
  if (btns) { // are supplied
153
154
  if (!Array.isArray(btns)) btns = [btns]; // convert single button to array if necessary
154
155
  btns.forEach((buttonFn) => { // create title-cased labels + attach listeners
@@ -157,7 +158,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
157
158
  .replace(/[_-]\w/g, match => match.slice(1).toUpperCase()) // convert snake/kebab to camel case
158
159
  .replace(/([A-Z])/g, ' $1') // insert spaces
159
160
  .replace(/^\w/, firstChar => firstChar.toUpperCase()); // capitalize first letter
160
- button.addEventListener('click', () => { dismissAlert(); buttonFn(); });
161
+ button.onclick = () => { dismissAlert(); buttonFn(); };
161
162
  modalButtons.insertBefore(button, modalButtons.firstChild); // insert button to left
162
163
  });
163
164
  }
@@ -177,12 +178,11 @@ const chatgpt = { // eslint-disable-line no-redeclare
177
178
  const checkboxFn = checkbox, // assign the named function to checkboxFn
178
179
  checkboxInput = document.createElement('input');
179
180
  checkboxInput.type = 'checkbox';
180
- checkboxInput.addEventListener('change', checkboxFn);
181
+ checkboxInput.onchange = checkboxFn;
181
182
 
182
183
  // Create/show label
183
184
  const checkboxLabel = document.createElement('label');
184
- checkboxLabel.addEventListener('click', () => {
185
- checkboxInput.checked = !checkboxInput.checked; checkboxFn(); });
185
+ checkboxLabel.onclick = () => { checkboxInput.checked = !checkboxInput.checked; checkboxFn(); };
186
186
  checkboxLabel.textContent = checkboxFn.name.charAt(0).toUpperCase() // capitalize first char
187
187
  + checkboxFn.name.slice(1) // format remaining chars
188
188
  .replace(/([A-Z])/g, (match, letter) => ' ' + letter.toLowerCase()) // insert spaces, convert to lowercase
@@ -194,7 +194,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
194
194
 
195
195
  // Create close button
196
196
  const closeBtn = document.createElement('div');
197
- closeBtn.title = 'Close'; closeBtn.classList.add('modal-close-btn');
197
+ closeBtn.title = 'Close'; closeBtn.classList.add('modal-close-btn', 'no-mobile-tap-outline');
198
198
  const closeSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
199
199
  closeSVG.setAttribute('height', '10px');
200
200
  closeSVG.setAttribute('viewBox', '0 0 14 14');
@@ -247,8 +247,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
247
247
 
248
248
  // Add listeners to dismiss alert
249
249
  const dismissElems = [modalContainer, closeBtn, closeSVG, dismissBtn];
250
- dismissElems.forEach(elem => {
251
- elem.addEventListener('click', clickHandler); });
250
+ dismissElems.forEach(elem => elem.onclick = clickHandler);
252
251
  document.addEventListener('keydown', keyHandler);
253
252
 
254
253
  // Define alert dismisser
@@ -262,10 +261,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
262
261
  alertQueue = JSON.parse(localStorage.alertQueue);
263
262
  alertQueue.shift(); // + memory
264
263
  localStorage.alertQueue = JSON.stringify(alertQueue); // + storage
265
-
266
- // Remove all listeners to prevent memory leaks
267
- dismissElems.forEach(elem => { elem.removeEventListener('click', clickHandler); });
268
- document.removeEventListener('keydown', keyHandler);
264
+ document.removeEventListener('keydown', keyHandler); // prevent memory leaks
269
265
 
270
266
  // Check for pending alerts in queue
271
267
  if (alertQueue.length > 0) {
@@ -282,7 +278,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
282
278
  return modalContainer.id; // if assignment used
283
279
  },
284
280
 
285
- askAndGetReply: async function(query) {
281
+ async askAndGetReply(query) {
286
282
  chatgpt.send(query); await chatgpt.isIdle();
287
283
  return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest');
288
284
  },
@@ -300,8 +296,10 @@ const chatgpt = { // eslint-disable-line no-redeclare
300
296
  const randomDelay = Math.max(2, Math.floor(chatgpt.randomFloat() * 21 - 10)); // set random delay up to ±10 secs
301
297
  autoRefresh.isActive = setTimeout(() => {
302
298
  const manifestScript = document.querySelector('script[src*="_ssgManifest.js"]');
303
- document.querySelector('#refresh-frame').src = manifestScript.src + '?' + Date.now();
304
- console.log('↻ ChatGPT >> [' + autoRefresh.nowTimeStamp() + '] ChatGPT session refreshed');
299
+ if (manifestScript) {
300
+ document.querySelector('#refresh-frame').src = manifestScript.src + '?' + Date.now();
301
+ console.log('↻ ChatGPT >> [' + autoRefresh.nowTimeStamp() + '] ChatGPT session refreshed');
302
+ }
305
303
  scheduleRefreshes(interval);
306
304
  }, (interval + randomDelay) * 1000);
307
305
  };
@@ -309,8 +307,8 @@ const chatgpt = { // eslint-disable-line no-redeclare
309
307
  console.log('↻ ChatGPT >> [' + chatgpt.autoRefresh.nowTimeStamp() + '] Auto refresh activated');
310
308
 
311
309
  // Add listener to send beacons in Chromium to thwart auto-discards if Page Visibility API supported
312
- if (navigator.userAgent.includes('Chrome') && typeof document.hidden !== 'undefined') {
313
- document.addEventListener('visibilitychange', this.toggle.beacons); }
310
+ if (navigator.userAgent.includes('Chrome') && typeof document.hidden !== 'undefined')
311
+ document.addEventListener('visibilitychange', this.toggle.beacons);
314
312
  },
315
313
 
316
314
  deactivate() {
@@ -379,7 +377,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
379
377
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); }
380
378
  },
381
379
 
382
- clearChats: async function() { // back-end method
380
+ async clearChats() { // back-end method
383
381
  return new Promise((resolve, reject) => {
384
382
  chatgpt.getAccessToken().then(token => {
385
383
  const xhr = new XMLHttpRequest();
@@ -398,7 +396,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
398
396
  code: {
399
397
  // Tip: Use template literals for easier passing of code arguments. Ensure backticks and `$`s are escaped (using `\`)
400
398
 
401
- execute: async function(code) {
399
+ async execute(code) {
402
400
  if (!code) return console.error('Code argument not supplied. Pass some code!');
403
401
  if (typeof code !== 'string') return console.error('Code argument must be a string!');
404
402
  chatgpt.send('Display the output as if you were terminal:\n\n' + code);
@@ -412,7 +410,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
412
410
  return codeBlocks ? codeBlocks[codeBlocks.length - 1] : msg;
413
411
  },
414
412
 
415
- isIdle: async function() {
413
+ async isIdle() {
416
414
  await new Promise(resolve => { // when in conversation page
417
415
  (function checkConvoPage() {
418
416
  document.querySelector('div[data-message-author-role]') ? resolve(true)
@@ -421,26 +419,26 @@ const chatgpt = { // eslint-disable-line no-redeclare
421
419
  await new Promise(resolve => { // when reply starts generating
422
420
  (function checkReplyExists() {
423
421
  const msgDivs = document.querySelectorAll('div[data-message-author-role]');
424
- msgDivs[msgDivs.length - 1].dataset.messageAuthorRole == 'assistant' ? resolve(true)
422
+ msgDivs[msgDivs.length - 1]?.dataset.messageAuthorRole == 'assistant' ? resolve(true)
425
423
  : setTimeout(checkReplyExists, 200); })();
426
424
  });
427
425
  const lastReplyDiv = await new Promise(resolve => { // when code starts generating
428
426
  (function checkPreExists() {
429
427
  const replyDivs = document.querySelectorAll('div[data-message-author-role="assistant"]'),
430
428
  lastReplyDiv = replyDivs[replyDivs.length - 1];
431
- lastReplyDiv.querySelector('pre') ? resolve(lastReplyDiv)
429
+ lastReplyDiv?.querySelector('pre') ? resolve(lastReplyDiv)
432
430
  : setTimeout(checkPreExists, 200); })();
433
431
  });
434
432
  return Promise.race([
435
433
  new Promise(resolve => { // when code block not last child of reply div
436
434
  (function checkPreNotLast() {
437
- lastReplyDiv.querySelector('pre').nextElementSibling ? resolve(true)
435
+ lastReplyDiv?.querySelector('pre').nextElementSibling ? resolve(true)
438
436
  : setTimeout(checkPreNotLast, 200); })();
439
437
  }), chatgpt.isIdle() // ...or reply stopped generating
440
438
  ]);
441
439
  },
442
440
 
443
- minify: async function(code) {
441
+ async minify(code) {
444
442
  if (!code) return console.error('Code argument not supplied. Pass some code!');
445
443
  if (typeof code !== 'string') return console.error('Code argument must be a string!');
446
444
  chatgpt.send('Minify the following code:\n\n' + code);
@@ -449,7 +447,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
449
447
  return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'));
450
448
  },
451
449
 
452
- obfuscate: async function(code) {
450
+ async obfuscate(code) {
453
451
  if (!code) return console.error('Code argument not supplied. Pass some code!');
454
452
  if (typeof code !== 'string') return console.error('Code argument must be a string!');
455
453
  chatgpt.send('Obfuscate the following code:\n\n' + code);
@@ -458,7 +456,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
458
456
  return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'));
459
457
  },
460
458
 
461
- refactor: async function(code, objective) {
459
+ async refactor(code, objective) {
462
460
  if (!code) return console.error('Code (1st) argument not supplied. Pass some code!');
463
461
  for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] !== 'string')
464
462
  return console.error(`Argument ${ i + 1 } must be a string.`);
@@ -468,7 +466,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
468
466
  return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'));
469
467
  },
470
468
 
471
- review: async function(code) {
469
+ async review(code) {
472
470
  if (!code) return console.error('Code argument not supplied. Pass some code!');
473
471
  if (typeof code !== 'string') return console.error('Code argument must be a string!');
474
472
  chatgpt.send('Review the following code for me:\n\n' + code);
@@ -477,7 +475,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
477
475
  return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest');
478
476
  },
479
477
 
480
- unminify: async function(code) {
478
+ async unminify(code) {
481
479
  if (!code) return console.error('Code argument not supplied. Pass some code!');
482
480
  if (typeof code !== 'string') return console.error('Code argument must be a string!');
483
481
  chatgpt.send('Unminify the following code.:\n\n' + code);
@@ -486,7 +484,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
486
484
  return chatgpt.code.extract(await chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'));
487
485
  },
488
486
 
489
- write: async function(prompt, outputLang) {
487
+ async write(prompt, outputLang) {
490
488
  if (!prompt) return console.error('Prompt (1st) argument not supplied. Pass a prompt!');
491
489
  if (!outputLang) return console.error('outputLang (2nd) argument not supplied. Pass a language!');
492
490
  for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] !== 'string')
@@ -500,7 +498,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
500
498
 
501
499
  continue() { chatgpt.response.continue(); },
502
500
 
503
- detectLanguage: async function(text) {
501
+ async detectLanguage(text) {
504
502
  if (!text) return console.error('Text argument not supplied. Pass some text!');
505
503
  if (typeof text !== 'string') return console.error('Text argument must be a string!');
506
504
  chatgpt.send('Detect the language of the following text:\n\n' + text
@@ -512,7 +510,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
512
510
 
513
511
  executeCode() { chatgpt.code.execute(); },
514
512
 
515
- exportChat: async function(chatToGet, format) {
513
+ async exportChat(chatToGet, format) {
516
514
  // chatToGet = 'active' (default) | 'latest' | index|title|id of chat to get
517
515
  // format = 'html' (default) | 'md' | 'pdf' | 'text'
518
516
 
@@ -544,9 +542,9 @@ const chatgpt = { // eslint-disable-line no-redeclare
544
542
  const msgs = []; let isUserMsg = true;
545
543
  chatDivs.forEach((div) => {
546
544
  const sender = isUserMsg ? 'USER' : 'CHATGPT'; isUserMsg = !isUserMsg;
547
- let msg = Array.from(div.childNodes).map(node => node.innerText)
548
- .join('\n\n') // insert double line breaks between paragraphs
549
- .replace('Copy code', '');
545
+ const msg = Array.from(div.childNodes).map(node => node.innerText)
546
+ .join('\n\n') // insert double line breaks between paragraphs
547
+ .replace('Copy code', '');
550
548
  msgs.push(sender + ': ' + msg);
551
549
  });
552
550
  transcript = msgs.join('\n\n');
@@ -567,7 +565,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
567
565
  // Format filename after <title>
568
566
  const parser = new DOMParser(),
569
567
  parsedHtml = parser.parseFromString(htmlContent, 'text/html');
570
- filename = parsedHtml.querySelector('title').textContent + '.html';
568
+ filename = `${ parsedHtml.querySelector('title').textContent || 'ChatGPT conversation' }.html`;
571
569
 
572
570
  // Convert relative CSS paths to absolute ones
573
571
  const cssLinks = parsedHtml.querySelectorAll('link[rel="stylesheet"]');
@@ -864,15 +862,15 @@ const chatgpt = { // eslint-disable-line no-redeclare
864
862
  return formBtnSVG.parentNode.parentNode;
865
863
  }},
866
864
 
867
- getFooterDiv() { return document.querySelector('main form').parentNode.parentNode.nextElementSibling; },
865
+ getFooterDiv() { return document.querySelector('main form')?.parentNode.parentNode.nextElementSibling; },
868
866
  getHeaderDiv() { return document.querySelector('main .sticky'); },
869
867
  getLastPrompt() { return chatgpt.getChatData('active', 'msg', 'user', 'latest'); },
870
868
  getLastResponse() { return chatgpt.getChatData('active', 'msg', 'chatgpt', 'latest'); },
871
869
 
872
870
  getNewChatButton() {
873
871
  for (const navBtnSVG of document.querySelectorAll('nav button svg'))
874
- if (navBtnSVG.querySelector('path[d*="M15.673 3.913a3.121"], ' // pencil-on-pad icon
875
- + 'path[d*="M3.07 10.876C3.623"]')) // refresh icon if temp chat
872
+ if (navBtnSVG.querySelector('path[d^="M15.6729"], ' // pencil-on-pad icon
873
+ + 'path[d^="M3.06957"]')) // refresh icon if temp chat
876
874
  return navBtnSVG.parentNode;
877
875
  },
878
876
 
@@ -1134,7 +1132,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1134
1132
  const optionButtons = document.querySelectorAll('a[role="menuitem"]');
1135
1133
  let cssClasses;
1136
1134
 
1137
- for (let navLink of optionButtons)
1135
+ for (const navLink of optionButtons)
1138
1136
  if (navLink.textContent == 'Settings') {
1139
1137
  cssClasses = navLink.classList;
1140
1138
  break; }
@@ -1152,7 +1150,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1152
1150
  this.elements.push(newElement);
1153
1151
  const menuBtn = document.querySelector('nav button[id*="headless"]');
1154
1152
  if (!this.addedEvent) { // to prevent adding more than one event
1155
- menuBtn.addEventListener('click', () => { setTimeout(addElementsToMenu, 25); });
1153
+ menuBtn?.addEventListener('click', () => { setTimeout(addElementsToMenu, 25); });
1156
1154
  this.addedEvent = true; }
1157
1155
 
1158
1156
  return newElement.id; // Return the element id
@@ -1171,7 +1169,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1171
1169
 
1172
1170
  minify() { chatgpt.code.minify(); },
1173
1171
 
1174
- notify: async function(msg, position, notifDuration, shadow) {
1172
+ notify(msg, position, notifDuration, shadow) {
1175
1173
  notifDuration = notifDuration ? +notifDuration : 1.75; // sec duration to maintain notification visibility
1176
1174
  const fadeDuration = 0.35, // sec duration of fade-out
1177
1175
  vpYoffset = 23, vpXoffset = 27; // px offset from viewport border
@@ -1185,7 +1183,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1185
1183
 
1186
1184
  // Create/append close button
1187
1185
  const closeBtn = document.createElement('div');
1188
- closeBtn.title = 'Dismiss'; closeBtn.classList.add('notif-close-btn');
1186
+ closeBtn.title = 'Dismiss'; closeBtn.classList.add('notif-close-btn', 'no-mobile-tap-outline');
1189
1187
  const closeSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1190
1188
  closeSVG.setAttribute('height', '8px');
1191
1189
  closeSVG.setAttribute('viewBox', '0 0 14 14');
@@ -1215,6 +1213,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1215
1213
  }
1216
1214
  notifStyle.innerText = ( // update prev/new style contents
1217
1215
  '.chatgpt-notif {'
1216
+ + '.no-mobile-tap-outline { outline: none ; -webkit-tap-highlight-color: transparent }'
1218
1217
  + 'background-color: black ; padding: 10px 13px 10px 18px ; border-radius: 11px ; border: 1px solid #f5f5f7 ;' // bubble style
1219
1218
  + 'opacity: 0 ; position: fixed ; z-index: 9999 ; font-size: 1.8rem ; color: white ;' // visibility
1220
1219
  + '-webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none ; user-select: none ;'
@@ -1270,15 +1269,17 @@ const chatgpt = { // eslint-disable-line no-redeclare
1270
1269
  clearTimeout(dismissFuncTID);
1271
1270
  };
1272
1271
  const dismissFuncTID = setTimeout(dismissNotif, hideDelay * 1000); // maintain visibility for `hideDelay` secs, then dismiss
1273
- closeSVG.addEventListener('click', dismissNotif, { once: true }); // add to close button clicks
1272
+ closeSVG.onclick = dismissNotif; // add to close button clicks
1274
1273
 
1275
1274
  // Destroy notification
1276
- notificationDiv.addEventListener('animationend', () => {
1275
+ notificationDiv.onanimationend = () => {
1277
1276
  notificationDiv.remove(); // remove from DOM
1278
1277
  notifyProps = JSON.parse(localStorage.notifyProps);
1279
1278
  notifyProps.queue[notificationDiv.quadrant].shift(); // + memory
1280
1279
  localStorage.notifyProps = JSON.stringify(notifyProps); // + storage
1281
- }, { once: true });
1280
+ };
1281
+
1282
+ return notificationDiv;
1282
1283
  },
1283
1284
 
1284
1285
  obfuscate() { chatgpt.code.obfuscate(); },
@@ -1409,7 +1410,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1409
1410
  return node; // if assignment used
1410
1411
  },
1411
1412
 
1412
- resend: async function() { chatgpt.send(await chatgpt.getChatData('latest', 'msg', 'user', 'latest')); },
1413
+ async resend() { chatgpt.send(await chatgpt.getChatData('latest', 'msg', 'user', 'latest')); },
1413
1414
 
1414
1415
  response: {
1415
1416
  continue() { try { chatgpt.getContinueBtn().click(); } catch (err) { console.error(err.message); }},
@@ -1437,36 +1438,35 @@ const chatgpt = { // eslint-disable-line no-redeclare
1437
1438
  const responseDivs = document.querySelectorAll('div[data-testid*="conversation-turn"]:nth-child(odd)'),
1438
1439
  strPos = pos.toString().toLowerCase();
1439
1440
  let response = '';
1440
- if (responseDivs.length) {
1441
- if (/last|final/.test(strPos)) // get last response
1442
- response = responseDivs[responseDivs.length - 1].textContent;
1443
- else { // get nth response
1444
- const nthOfResponse = (
1445
-
1446
- // Calculate base number
1447
- Number.isInteger(pos) ? pos : // do nothing for integers
1448
- /^\d+/.test(strPos) ? /^\d+/.exec(strPos)[0] : // extract first digits for strings w/ them
1449
- ( // convert words to integers for digitless strings
1450
- /^(?:1|one|fir)(?:st)?$/.test(strPos) ? 1
1451
- : /^(?:2|tw(?:o|en|el(?:ve|f))|seco)(?:nd|t[yi])?(?:e?th)?$/.test(strPos) ? 2
1452
- : /^(?:3|th(?:ree|ir?))(?:rd|teen|t[yi])?(?:e?th)?$/.test(strPos) ? 3
1453
- : /^(?:4|fou?r)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 4
1454
- : /^(?:5|fi(?:ve|f))(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 5
1455
- : /^(?:6|six)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 6
1456
- : /^(?:7|seven)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 7
1457
- : /^(?:8|eight?)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 8
1458
- : /^(?:9|nine?)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 9
1459
- : /^(?:10|ten)(?:th)?$/.test(strPos) ? 10 : 1 )
1460
-
1461
- // Transform base number if suffixed
1462
- * ( /(ty|ieth)$/.test(strPos) ? 10 : 1 ) // x 10 if -ty/ieth
1463
- + ( /teen(th)?$/.test(strPos) ? 10 : 0 ) // + 10 if -teen/teenth
1464
-
1465
- );
1466
- response = responseDivs[nthOfResponse - 1].textContent;
1467
- }
1468
- response = response.replace(/^ChatGPT(?:ChatGPT)?/, ''); // strip sender name
1441
+ if (!responseDivs.length) return console.error('No conversation found!');
1442
+ if (/last|final/.test(strPos)) // get last response
1443
+ response = responseDivs[responseDivs.length - 1].textContent;
1444
+ else { // get nth response
1445
+ const nthOfResponse = (
1446
+
1447
+ // Calculate base number
1448
+ Number.isInteger(pos) ? pos : // do nothing for integers
1449
+ /^\d+/.test(strPos) ? /^\d+/.exec(strPos)[0] : // extract first digits for strings w/ them
1450
+ ( // convert words to integers for digitless strings
1451
+ /^(?:1|one|fir)(?:st)?$/.test(strPos) ? 1
1452
+ : /^(?:2|tw(?:o|en|el(?:ve|f))|seco)(?:nd|t[yi])?(?:e?th)?$/.test(strPos) ? 2
1453
+ : /^(?:3|th(?:ree|ir?))(?:rd|teen|t[yi])?(?:e?th)?$/.test(strPos) ? 3
1454
+ : /^(?:4|fou?r)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 4
1455
+ : /^(?:5|fi(?:ve|f))(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 5
1456
+ : /^(?:6|six)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 6
1457
+ : /^(?:7|seven)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 7
1458
+ : /^(?:8|eight?)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 8
1459
+ : /^(?:9|nine?)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 9
1460
+ : /^(?:10|ten)(?:th)?$/.test(strPos) ? 10 : 1 )
1461
+
1462
+ // Transform base number if suffixed
1463
+ * ( /(ty|ieth)$/.test(strPos) ? 10 : 1 ) // x 10 if -ty/ieth
1464
+ + ( /teen(th)?$/.test(strPos) ? 10 : 0 ) // + 10 if -teen/teenth
1465
+
1466
+ );
1467
+ response = responseDivs[nthOfResponse - 1].textContent;
1469
1468
  }
1469
+ response = response.replace(/^ChatGPT(?:ChatGPT)?/, ''); // strip sender name
1470
1470
  return response;
1471
1471
  },
1472
1472
 
@@ -1481,17 +1481,17 @@ const chatgpt = { // eslint-disable-line no-redeclare
1481
1481
  send(msg, method='') {
1482
1482
  for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] !== 'string')
1483
1483
  return console.error(`Argument ${ i + 1 } must be a string!`);
1484
- const textArea = document.querySelector('form textarea'),
1485
- sendBtn = chatgpt.getSendButton();
1484
+ const textArea = chatgpt.getChatBox();
1485
+ if (!textArea) return console.error('Chatbar element not found!');
1486
1486
  textArea.value = msg;
1487
1487
  textArea.dispatchEvent(new Event('input', { bubbles: true })); // enable send button
1488
-
1489
1488
  setTimeout(function delaySend() {
1489
+ const sendBtn = chatgpt.getSendButton();
1490
1490
  if (!sendBtn?.hasAttribute('disabled')) { // send msg
1491
1491
  method.toLowerCase() == 'click' || chatgpt.browser.isMobile() ? sendBtn.click()
1492
- : textArea.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 13, bubbles: true }));
1493
- } else setTimeout(delaySend, 25);
1494
- }, 25);
1492
+ : textArea.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
1493
+ } else setTimeout(delaySend, 222);
1494
+ }, 222);
1495
1495
  },
1496
1496
 
1497
1497
  sendInNewChat(msg) {
@@ -1531,7 +1531,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1531
1531
  }
1532
1532
  },
1533
1533
 
1534
- sentiment: async function(text, entity) {
1534
+ async sentiment(text, entity) {
1535
1535
  for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] !== 'string')
1536
1536
  return console.error(`Argument ${ i + 1 } must be a string.`);
1537
1537
  chatgpt.send('What is the sentiment of the following text'
@@ -1654,8 +1654,9 @@ const chatgpt = { // eslint-disable-line no-redeclare
1654
1654
  element.style.margin = '2px 0';
1655
1655
  });
1656
1656
 
1657
- const navBar = document.querySelector('nav');
1658
1657
  // Create MutationObserver instance
1658
+ const navBar = document.querySelector('nav');
1659
+ if (!navBar) return console.error('Sidebar element not found!');
1659
1660
  this.observer = new MutationObserver(mutations => {
1660
1661
  mutations.forEach(mutation => {
1661
1662
  if ((mutation.type == 'childList' && mutation.addedNodes.length) ||
@@ -1741,6 +1742,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1741
1742
  isOff() { return !this.isOn(); },
1742
1743
  isOn() {
1743
1744
  const sidebar = document.querySelector('body script + div > div');
1745
+ if (!sidebar) return console.error('Sidebar element not found!');
1744
1746
  return chatgpt.browser.isMobile() ?
1745
1747
  document.documentElement.style.overflow == 'hidden'
1746
1748
  : sidebar.style.visibility != 'hidden' && sidebar.style.width != '0px';
@@ -1750,12 +1752,12 @@ const chatgpt = { // eslint-disable-line no-redeclare
1750
1752
  const isMobileDevice = chatgpt.browser.isMobile(),
1751
1753
  navBtnSelector = isMobileDevice ? 'button' : 'nav button',
1752
1754
  isToggleBtn = isMobileDevice ? () => true // since 1st one is toggle
1753
- : btn => btn.querySelector('svg path[d*="M8.857 3h6.286c1.084"]');
1755
+ : btn => btn.querySelector('svg path[d^="M8.857"]');
1754
1756
  for (const btn of document.querySelectorAll(navBtnSelector))
1755
1757
  if (isToggleBtn(btn)) { btn.click(); return; }
1756
1758
  },
1757
1759
 
1758
- isLoaded: async function() {
1760
+ async isLoaded() {
1759
1761
  await chatgpt.isLoaded();
1760
1762
  return Promise.race([
1761
1763
  new Promise(resolve => {
@@ -1771,7 +1773,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1771
1773
  startNewChat() { try { chatgpt.getNewChatBtn().click(); } catch (err) { console.error(err.message); }},
1772
1774
  stop() { chatgpt.response.stopGenerating(); },
1773
1775
 
1774
- suggest: async function(ideaType, details) {
1776
+ async suggest(ideaType, details) {
1775
1777
  if (!ideaType) return console.error('ideaType (1st argument) not supplied'
1776
1778
  + '(e.g. \'gifts\', \'names\', \'recipes\', etc.)');
1777
1779
  for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] !== 'string')
@@ -1809,7 +1811,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1809
1811
  } catch (err) { console.error( err); }
1810
1812
  },
1811
1813
 
1812
- summarize: async function(text) {
1814
+ async summarize(text) {
1813
1815
  if (!text) return console.error('Text (1st) argument not supplied. Pass some text!');
1814
1816
  if (typeof text !== 'string') return console.error('Text argument must be a string!');
1815
1817
  chatgpt.send('Summarize the following text:\n\n' + text);
@@ -1820,7 +1822,7 @@ const chatgpt = { // eslint-disable-line no-redeclare
1820
1822
 
1821
1823
  toggleScheme() { chatgpt.settings.scheme.toggle(); },
1822
1824
 
1823
- translate: async function(text, outputLang) {
1825
+ async translate(text, outputLang) {
1824
1826
  if (!text) return console.error('Text (1st) argument not supplied. Pass some text!');
1825
1827
  if (!outputLang) return console.error('outputLang (2nd) argument not supplied. Pass a language!');
1826
1828
  for (let i = 0; i < arguments.length; i++) if (typeof arguments[i] !== 'string')
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "name": "ChatGPT Extension",
4
4
  "description": "A Chrome template to start using chatgpt.js like a boss!",
5
- "version": "2024.8.28",
5
+ "version": "2024.8.29",
6
6
  "author": "chatgpt.js",
7
7
  "icons": {
8
8
  "16": "icons/icon16.png",
@@ -3,13 +3,13 @@
3
3
  // @description A Greasemonkey template to start using chatgpt.js like a boss
4
4
  // @author chatgpt.js
5
5
  // @namespace https://chatgpt.js.org
6
- // @version 2024.8.28
6
+ // @version 2024.8.29
7
7
  // @license MIT
8
8
  // @match *://chatgpt.com/*
9
9
  // @match *://chat.openai.com/*
10
- // @icon https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js@3.0.2/starters/greasemonkey/media/images/icons/robot/icon48.png
11
- // @icon64 https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js@3.0.2/starters/greasemonkey/media/images/icons/robot/icon64.png
12
- // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.0.2/dist/chatgpt.min.js
10
+ // @icon https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js@3.1.0/starters/greasemonkey/media/images/icons/robot/icon48.png
11
+ // @icon64 https://cdn.jsdelivr.net/gh/KudoAI/chatgpt.js@3.1.0/starters/greasemonkey/media/images/icons/robot/icon64.png
12
+ // @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.1.0/dist/chatgpt.min.js
13
13
  // @grant GM_getValue
14
14
  // @grant GM_setValue
15
15
  // @noframes