Minimal ChatGPT Message Logger + Injector + Submitter

Listen for postMessage from parent, log to console, enter it into the chat input, and submit

  1. // ==UserScript==
  2. // @name Minimal ChatGPT Message Logger + Injector + Submitter
  3. // @description Listen for postMessage from parent, log to console, enter it into the chat input, and submit
  4. // @match https://chatgpt.com/*
  5. // @version 0.0.1.20250526031750
  6. // @namespace https://greasyfork.org/users/1435046
  7. // ==/UserScript==
  8.  
  9. (function () {
  10. 'use strict';
  11.  
  12. window.addEventListener('message', function (event) {
  13.  
  14. // Handle search button clicks
  15. if (event.data && event.data.type === 'searchButtonClicked') {
  16. const searchBtn1 = document.querySelector('[data-testid="composer-button-search"]');
  17.  
  18. const searchButton2 = document.getElementById('system-hint-button');
  19.  
  20. if (searchBtn1) {
  21. searchBtn1.click();
  22. console.log
  23. return;
  24. }
  25.  
  26. if (searchButton2) {
  27. searchButton2.focus(); // Sometimes needed to simulate a real user
  28. searchButton2.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }));
  29.  
  30. for (const div of document.querySelectorAll('[id^="radix-"] > div > div')) {
  31. if (div.textContent.trim() === 'Search the web') {
  32. div.click();
  33. break;
  34. }
  35. }
  36.  
  37. };
  38. return;
  39. }
  40.  
  41. if (event.data && event.data.type === 'reasonButtonClicked') {
  42. const reasonBtn1 = document.querySelector('[data-testid="composer-button-reason"]');
  43.  
  44. const reasonButton2 = document.getElementById('system-hint-button');
  45.  
  46. if (reasonBtn1) reasonBtn1.click();
  47.  
  48. if (reasonButton2) {
  49. reasonButton2.focus(); // Sometimes needed to simulate a real user
  50. reasonButton2.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true }));
  51.  
  52. for (const div of document.querySelectorAll('[id^="radix-"] > div > div')) {
  53. if (div.textContent.trim() === 'Think for longer') {
  54. div.click();
  55. break;
  56. }
  57. }
  58.  
  59. }
  60. return;
  61. }
  62.  
  63. if (event.data && event.data.type === 'newChatButtonClicked') {
  64. // Click the "New chat" anchor link
  65. const newChatLink = document.querySelector('a[aria-label="New chat"][href="/"]');
  66. if (newChatLink) newChatLink.click();
  67. return;
  68. }
  69.  
  70. if (event.data.type !== 'prompt') return;
  71.  
  72. // Locate ProseMirror composer
  73. const composer = document.querySelector('.ProseMirror');
  74. if (!composer) return;
  75.  
  76. console.log('Injecting message:', event.data.content);
  77.  
  78. // Focus and inject the text
  79. composer.focus();
  80.  
  81. const lines = event.data.content.split('\n');
  82. const html = lines
  83. .map(line => `<p>${line
  84. .replace(/&/g, '&amp;')
  85. .replace(/</g, '&lt;')
  86. .replace(/>/g, '&gt;')}</p>`)
  87. .join('');
  88. composer.innerHTML = html;
  89.  
  90. composer.dispatchEvent(new InputEvent('input', { bubbles: true }));
  91.  
  92. // Observe DOM changes until the send button is enabled
  93. const observer = new MutationObserver(function (mutations, obs) {
  94. const sendBtn = document.querySelector('[data-testid="send-button"]');
  95. if (sendBtn && !sendBtn.disabled) {
  96. obs.disconnect();
  97. console.log('Submitting message');
  98. sendBtn.click();
  99. }
  100. });
  101.  
  102. observer.observe(document, {
  103. childList: true,
  104. subtree: true,
  105. attributes: true,
  106. attributeFilter: ['disabled']
  107. });
  108. });
  109. })();