CB Mod Assist

Helper script for mods on chaturbate.com that lets you save predefined messages and send them to chat with two clicks

  1. // ==UserScript==
  2. // @name CB Mod Assist
  3. // @author TheKrucible
  4. // @namespace https://sorte.ninja
  5. // @version 1.2
  6. // @include https://chaturbate.com/*
  7. // @grant GM.registerMenuCommand
  8. // @description Helper script for mods on chaturbate.com that lets you save predefined messages and send them to chat with two clicks
  9. // ==/UserScript==
  10.  
  11. const host = document.createElement('div');
  12. host.setAttribute('class', 'cbma');
  13. document.body.appendChild(host);
  14.  
  15. const KEY = 'cbma-text';
  16. const EDITOR_ID = 'cbma-textarea';
  17. const STYLES = `
  18. .cbma .editor {
  19. position: fixed;
  20. left: 20vw;
  21. top: 10vh;
  22. width: 60vw;
  23. background-color: white;
  24. padding: 20px;
  25. box-sizing: border-box;
  26. }
  27. .cbma .editor textarea {
  28. width: 100%;
  29. height: 60vh;
  30. box-sizing: border-box;
  31. resize: none;
  32. }
  33. .cbma .speak {
  34. position: absolute;
  35. right: 50px;
  36. bottom: 0px;
  37. width: fit-content;
  38. max-width: 50vw;
  39. background-color: white;
  40. padding: 20px;
  41. box-sizing: border-box;
  42. }
  43. .cbma .speak ul {
  44. margin: 0;
  45. padding: 0;
  46. list-style: none;
  47. }
  48. .cbma .speak ul li {
  49. cursor: pointer;
  50. }
  51. .cbma .speak ul li.off {
  52. font-style: italic;
  53. color: gray;
  54. }
  55. .cbma .speak ul li:hover {
  56. text-decoration: underline;
  57. }
  58. .cbma .buttonbar {
  59. margin-top: 20px;
  60. text-align: right;
  61. }
  62. button.cbma-speak {
  63. position: absolute;
  64. right: 75px;
  65. top: calc(50% - 10px);
  66. background-color: green;
  67. color: white;
  68. border: none;
  69. border-radius: 4px;
  70. padding: 3px 5px;
  71. text-transform: uppercase;
  72. }
  73. `;
  74.  
  75. function showEditor() {
  76. const text = window.localStorage.getItem(KEY) || '';
  77. host.innerHTML = `
  78. <div class="editor">
  79. <h1>CB Mod Assist Editor</h1>
  80. <p>
  81. Enter lines you want to speak in chat here. One line = one chat item. Empty lines are ignored.<br>
  82. Lines starting with one or more spaces are shown "grayed out" in select box, but can still be spoken.<br>
  83. Do multi-line speaks with two plus signs: 'This will ++ send three lines ++ to chat'.<br>
  84. Make model specific sections with a line '# modelname'. Everything following that line will only show in that models room.
  85. </p>
  86. <textarea id="${EDITOR_ID}">${text}</textarea>
  87. <div class="buttonbar">
  88. <button id="cbma-close">Save and close</button>
  89. </div>
  90. </div>
  91. `;
  92. document.getElementById('cbma-close').addEventListener('click', closeEditor);
  93. }
  94.  
  95. function closeEditor() {
  96. const text = document.getElementById(EDITOR_ID).value;
  97. window.localStorage.setItem(KEY, text);
  98. host.innerHTML = '';
  99. }
  100.  
  101. function showSpeak() {
  102. const items = (window.localStorage.getItem(KEY) || '')
  103. .split('\n')
  104. .filter(i => i.trim() !== '');
  105.  
  106. if (items.length === 0) {
  107. host.innerHTML = `
  108. <div class="speak">
  109. <h1>CB Mod Assist</h1>
  110. <p>No lines entered. Please click 'Edit' below to enter lines.</p>
  111. <div class="buttonbar">
  112. <button id="cbma-edit">Edit</button>
  113. <button id="cbma-close">Cancel</button>
  114. </div>
  115. </div>
  116. `;
  117. }
  118. else {
  119. host.innerHTML = `
  120. <div class="speak">
  121. <h1>CB Mod Assist</h1>
  122. <p>Click line below to speak in chat. Shift+click to copy to chat box without sending.</p>
  123. <ul id="cbma-list"></ul>
  124. <div class="buttonbar">
  125. <button id="cbma-edit">Edit</button>
  126. <button id="cbma-close">Cancel</button>
  127. </div>
  128. </div>
  129. `;
  130. const list = document.getElementById('cbma-list');
  131. let model = '';
  132. items.forEach(item => {
  133. if (item.startsWith('#')) {
  134. model = '/' + item.substring(1).trim() + '/'
  135. }
  136. else if (window.location.pathname.startsWith(model)) {
  137. const li = document.createElement('li');
  138. if (item.startsWith(' ')) {
  139. li.setAttribute('class', 'off');
  140. }
  141. li.textContent = item.trim();
  142. list.appendChild(li);
  143. }
  144. });
  145. list.addEventListener('click', speak);
  146. }
  147. document.getElementById('cbma-edit').addEventListener('click', showEditor);
  148. document.getElementById('cbma-close').addEventListener('click', cancelSpeak);
  149. }
  150.  
  151. function speak(event) {
  152. const speach = event.target.textContent.split('++').map(x => x.trim()).filter(x => !!x);
  153. const input = document.querySelector('.inputDiv .chat-input-field');
  154. const button = input.parentElement.parentElement.querySelector('.SendButton');
  155. if (!input || !button) {
  156. return;
  157. }
  158.  
  159. if (event.shiftKey) {
  160. input.innerText = event.target.textContent;
  161. return;
  162. }
  163.  
  164. if (speach.length > 0) {
  165. doSpeak(speach, input, button);
  166. }
  167.  
  168. cancelSpeak();
  169. }
  170.  
  171. function doSpeak(lines, input, button) {
  172. const line = lines.shift();
  173. input.innerText = line;
  174. button.click();
  175.  
  176. if (lines.length > 0) {
  177. window.setTimeout(() => doSpeak(lines, input, button), 300);
  178. }
  179. }
  180.  
  181. function cancelSpeak() {
  182. host.innerHTML = '';
  183. }
  184.  
  185. GM.registerMenuCommand('CB Mod Assist - Edit', showEditor);
  186. GM.registerMenuCommand('CB Mod Assist - Speak', showSpeak);
  187.  
  188. function addGlobalStyle(css) {
  189. const head = document.getElementsByTagName('head')[0];
  190. if (!head) {
  191. return;
  192. }
  193. const style = document.createElement('style');
  194. style.type = 'text/css';
  195. style.innerHTML = css;
  196. head.appendChild(style);
  197. }
  198.  
  199. function init() {
  200. addGlobalStyle(STYLES);
  201. const input = document.querySelector('div.inputDiv');
  202. if (input) {
  203. const button = document.createElement('button');
  204. button.setAttribute('class', 'cbma-speak');
  205. button.innerHTML = 'Assist';
  206. input.appendChild(button);
  207. const textarea = input.querySelector('.chat-input-field');
  208. textarea.style.width = (textarea.clientWidth - 60) + 'px';
  209. button.addEventListener('click', showSpeak);
  210. }
  211. }
  212.  
  213. window.addEventListener('load', () => window.setTimeout(init, 2000));