Slack Conversation Scraper & Custom Buttons

Combines conversation scraper and custom message buttons with a modern smooth UI inside a single box.

当前为 2024-12-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Slack Conversation Scraper & Custom Buttons
  3. // @version 2.4
  4. // @description Combines conversation scraper and custom message buttons with a modern smooth UI inside a single box.
  5. // @author Mahmudul Hasan Shawon
  6. // @icon https://www.slack.com/favicon.ico
  7. // @match https://app.slack.com/client/*
  8. // @grant none
  9. // @namespace https://greasyfork.org/users/1392874
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14.  
  15. const buttons = [
  16. { id: 'goodMorningButton', text: 'GM!', message: 'Good morning!' },
  17. { id: 'okButton', text: 'Ok', message: 'Ok' },
  18. { id: 'scrapeConversationButton', text: '📋 Scrape All', message: '' },
  19. { id: 'copyConversationButton', text: '✨ Guess Reply', message: '' },
  20. ];
  21.  
  22. // Start script after Slack interface loads
  23. function waitForSlackInterface() {
  24. const checkInterval = setInterval(() => {
  25. const textBox = document.querySelector('[data-message-input="true"] .ql-editor');
  26. if (textBox) {
  27. clearInterval(checkInterval);
  28. addControlBox();
  29. }
  30. }, 1000);
  31. }
  32.  
  33. // Add modern control box containing buttons and input field
  34. function addControlBox() {
  35. const controlBox = document.createElement('div');
  36. Object.assign(controlBox.style, {
  37. position: 'fixed', bottom: '30px', right: '100px',
  38. width: '200px', padding: '15px', backgroundColor: '#ffffff',
  39. borderRadius: '12px', boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
  40. fontFamily: 'Arial, sans-serif', zIndex: '9999',
  41. transition: 'transform 0.3s ease-in-out'
  42. });
  43. controlBox.id = 'slackControlBox';
  44.  
  45. // Hover animation
  46. controlBox.addEventListener('mouseenter', () => {
  47. controlBox.style.transform = 'scale(1.02)';
  48. });
  49. controlBox.addEventListener('mouseleave', () => {
  50. controlBox.style.transform = 'scale(1)';
  51. });
  52.  
  53. // Container for first two buttons (side by side)
  54. const sideBySideContainer = document.createElement('div');
  55. Object.assign(sideBySideContainer.style, {
  56. display: 'flex', justifyContent: 'space-between', gap: '10px', marginBottom: '10px'
  57. });
  58.  
  59. // Add "GM!" and "Ok" buttons to the side-by-side container
  60. const goodMorningButton = createButton(buttons[0]); // GM!
  61. const okButton = createButton(buttons[1]); // Ok
  62. goodMorningButton.style.flex = '1'; // Make them equally wide
  63. okButton.style.flex = '1';
  64.  
  65. sideBySideContainer.appendChild(goodMorningButton);
  66. sideBySideContainer.appendChild(okButton);
  67. controlBox.appendChild(sideBySideContainer);
  68.  
  69. // Add other buttons
  70. buttons.slice(2).forEach(btn => {
  71. const button = createButton(btn);
  72. controlBox.appendChild(button);
  73. });
  74.  
  75. // Add input field
  76. const inputField = createInputField();
  77. controlBox.appendChild(inputField);
  78.  
  79. document.body.appendChild(controlBox);
  80. }
  81.  
  82. // Create button elements
  83. function createButton({ id, text, message }) {
  84. const button = document.createElement('button');
  85. Object.assign(button.style, {
  86. display: 'block', width: '100%', marginBottom: '10px',
  87. padding: '8px 0', backgroundColor: '#f3f3f3', color: '#333',
  88. fontSize: '14px', border: 'none', borderRadius: '8px',
  89. cursor: 'pointer', transition: 'background-color 0.2s ease-in-out'
  90. });
  91. button.id = id;
  92. button.textContent = text;
  93. button.addEventListener('mouseenter', () => {button.style.backgroundColor = '#e0e0e0'; });
  94. button.addEventListener('mouseleave', () => {button.style.backgroundColor = '#f3f3f3'; });
  95.  
  96. button.addEventListener('click', () => {
  97. if (id === 'copyConversationButton') copyMessages();
  98. else if (id === 'scrapeConversationButton') scrapeConversation();
  99. else sendMessage(message);
  100. });
  101. return button;
  102. }
  103.  
  104. // Create input field for custom message count
  105. function createInputField() {
  106. const input = document.createElement('input');
  107. Object.assign(input.style, {
  108. width: '100%', padding: '8px', fontSize: '14px',
  109. border: '1px solid #ccc', borderRadius: '8px',
  110. textAlign: 'center', boxSizing: 'border-box'
  111. });
  112. input.id = 'messageCountInputField';
  113. input.type = 'number';
  114. input.placeholder = 'Number of messages';
  115. return input;
  116. }
  117.  
  118.  
  119.  
  120.  
  121.  
  122. // Scrape all Slack conversations
  123. function scrapeConversation() {
  124. const messageBlocks = Array.from(document.querySelectorAll('.c-message_kit__background'));
  125. let conversation = '', lastSender = null;
  126.  
  127. messageBlocks.forEach(block => {
  128. // Extract sender name or use the last known sender
  129. const sender = block.querySelector('.c-message__sender_button')?.textContent.trim() || lastSender;
  130. if (sender) lastSender = sender;
  131.  
  132. // Extract message text
  133. const messageText = Array.from(block.querySelectorAll('.p-rich_text_section'))
  134. .map(el => el.textContent.trim())
  135. .join(' ').trim();
  136.  
  137. // Append the message in the desired format
  138. if (messageText) {
  139. conversation += `${lastSender}: ${messageText}\n\n`;
  140. console.log(`${lastSender}: ${messageText}`); // Log sender and message
  141. }
  142. });
  143.  
  144. if (conversation.trim()) {
  145. copyToClipboard(conversation, 'All conversations copied!');
  146. } else {
  147. showPopUp('No conversation found.');
  148. }
  149. }
  150.  
  151.  
  152. // Copy last X messages with Guess Reply
  153. function copyMessages() {
  154. const numberOfMessages = document.getElementById('messageCountInputField').value || 2;
  155. const messages = Array.from(document.querySelectorAll('.c-message_kit__blocks'))
  156. .slice(-numberOfMessages)
  157. .map(container => {
  158. const sender = container.closest('.c-message_kit__background')
  159. ?.querySelector('.c-message__sender_button')?.textContent.trim();
  160. const messageText = container.querySelector('.p-rich_text_section')?.textContent.trim();
  161. return sender && messageText ? `\n${sender}: ${messageText}` : null;
  162. })
  163. .filter(Boolean);
  164.  
  165. if (!messages.length) {
  166. showPopUp(`Unable to copy ${numberOfMessages} messages.`);
  167. return;
  168. }
  169.  
  170. const formatted = `${messages.join('\n')}\n\nGuess reply:`;
  171. copyToClipboard(formatted, `Last ${numberOfMessages} messages copied!`);
  172. }
  173.  
  174.  
  175.  
  176.  
  177.  
  178. // Copy text to clipboard and show notification
  179. function copyToClipboard(text, message) {
  180. navigator.clipboard.writeText(text)
  181. .then(() => showPopUp(message))
  182. .catch(() => showPopUp('Failed to copy.'));
  183. }
  184.  
  185. // Send predefined message
  186. function sendMessage(message) {
  187. const textBox = document.querySelector('[data-message-input="true"] .ql-editor');
  188. const sendButton = document.querySelector('[data-qa="texty_send_button"]');
  189.  
  190. if (textBox) {
  191. textBox.focus();
  192. document.execCommand('insertText', false, message);
  193. setTimeout(() => sendButton?.click(), 500);
  194. } else {
  195. showPopUp('Message box not found.');
  196. }
  197. }
  198.  
  199.  
  200.  
  201. // Smooth animated pop-up notification
  202. function showPopUp(message) {
  203. const popUp = document.createElement('div');
  204. Object.assign(popUp.style, {
  205. position: 'fixed', bottom: '245px', right: '100px',
  206. backgroundColor: '#A294F9', color: '#FFFFFF',
  207. padding: '10px 15px', borderRadius: '16px 16px 0px 16px', fontSize: '14px',
  208. boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.3)',
  209. animation: 'fadeInOut 3s ease-in-out', zIndex: '9999'
  210. });
  211. popUp.textContent = message;
  212. document.body.appendChild(popUp);
  213.  
  214. // Remove the pop-up after animation completes
  215. setTimeout(() => popUp.remove(), 3000);
  216. }
  217.  
  218. // Add CSS animation for fadeInOut
  219. const styleSheet = document.createElement('style');
  220. styleSheet.type = 'text/css';
  221. styleSheet.innerText = `
  222. @keyframes fadeInOut {
  223. 0% {
  224. opacity: 0;
  225. transform: translateY(20px);
  226. }
  227. 10% {
  228. opacity: 1;
  229. transform: translateY(0);
  230. }
  231. 90% {
  232. opacity: 1;
  233. transform: translateY(0);
  234. }
  235. 100% {
  236. opacity: 0;
  237. transform: translateY(20px);
  238. }
  239. }
  240. `;
  241. document.head.appendChild(styleSheet);
  242.  
  243. // Start script
  244. waitForSlackInterface();
  245. })();