// ==UserScript==
// @name Slack Conversation Scraper & Custom Buttons
// @version 2.7.1
// @description Combines conversation scraper and custom message buttons with smooth UI and arrow key toggle animation.
// @author Mahmudul Hasan Shawon
// @icon https://www.slack.com/favicon.ico
// @match https://app.slack.com/client/*
// @license MIT
// @grant none
// @namespace https://gf.qytechs.cn/users/1392874
// ==/UserScript==
(function () {
'use strict';
const buttons = [
{ id: 'goodMorningButton', text: 'GM!', message: 'Good morning!' },
{ id: 'okButton', text: 'Ok', message: 'Ok' },
{ id: 'scrapeConversationButton', text: '📋 Scrape All', message: '' },
{ id: 'copyConversationButton', text: '✨ Guess Reply', message: '' },
];
function waitForSlackInterface() {
const checkInterval = setInterval(() => {
const textBox = document.querySelector('[data-message-input="true"] .ql-editor');
if (textBox) {
clearInterval(checkInterval);
addControlBox();
}
}, 1000);
}
function addControlBox() {
const controlBox = document.createElement('div');
Object.assign(controlBox.style, {
position: 'fixed',
bottom: '35px',
right: '100px',
width: '200px',
padding: '15px',
backgroundColor: '#ffffff',
borderRadius: '12px',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.2)',
fontFamily: 'Arial, sans-serif',
zIndex: '9999',
transition: 'transform 0.3s ease-in-out, opacity 0.3s ease-in-out',
transform: 'translateY(0)',
opacity: '1',
});
controlBox.id = 'slackControlBox';
const toggleButton = document.createElement('div');
Object.assign(toggleButton.style, {
position: 'fixed',
bottom: '46px',
right: '120px',
width: '36px',
height: '36px',
backgroundColor: '#C3FF93',
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
zIndex: '10000',
});
toggleButton.innerHTML = `<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 12h18M12 3v18" stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
toggleButton.title = 'Hide/Show Control Box';
toggleButton.addEventListener('click', () => toggleControlBox(controlBox));
document.body.appendChild(toggleButton);
const sideBySideContainer = document.createElement('div');
Object.assign(sideBySideContainer.style, {
display: 'flex',
justifyContent: 'space-between',
gap: '10px',
marginBottom: '10px',
});
const goodMorningButton = createButton(buttons[0]);
const okButton = createButton(buttons[1]);
goodMorningButton.style.flex = '1';
okButton.style.flex = '1';
sideBySideContainer.appendChild(goodMorningButton);
sideBySideContainer.appendChild(okButton);
controlBox.appendChild(sideBySideContainer);
buttons.slice(2).forEach((btn) => {
const button = createButton(btn);
controlBox.appendChild(button);
});
const inputField = createInputField();
controlBox.appendChild(inputField);
document.body.appendChild(controlBox);
// Add keyboard toggle functionality
document.addEventListener('keydown', (e) => handleKeyPress(e, controlBox));
}
function toggleControlBox(controlBox) {
const isHidden = controlBox.style.opacity === '0';
controlBox.style.opacity = isHidden ? '1' : '0';
controlBox.style.transform = isHidden ? 'translateY(0)' : 'translateY(20px)';
}
function handleKeyPress(event, controlBox) {
if (event.key === 'ArrowDown') {
controlBox.style.opacity = '0';
controlBox.style.transform = 'translateY(20px)';
} else if (event.key === 'ArrowUp') {
controlBox.style.opacity = '1';
controlBox.style.transform = 'translateY(0)';
}
}
function createButton({ id, text, message }) {
const button = document.createElement('button');
Object.assign(button.style, {
display: 'block',
width: '100%',
marginBottom: '10px',
padding: '8px 0',
backgroundColor: '#f3f3f3',
color: '#333',
fontSize: '14px',
border: 'none',
borderRadius: '8px',
cursor: 'pointer',
transition: 'background-color 0.2s ease-in-out',
});
button.id = id;
button.textContent = text;
button.addEventListener('mouseenter', () => {
button.style.backgroundColor = '#e0e0e0';
});
button.addEventListener('mouseleave', () => {
button.style.backgroundColor = '#f3f3f3';
});
button.addEventListener('click', () => {
if (id === 'copyConversationButton') copyMessages();
else if (id === 'scrapeConversationButton') scrapeConversation();
else sendMessage(message);
});
return button;
}
function createInputField() {
const input = document.createElement('input');
Object.assign(input.style, {
width: '65%',
padding: '8px',
fontSize: '12px',
border: '1px solid #ccc',
borderRadius: '8px',
textAlign: 'center',
boxSizing: 'border-box',
});
input.id = 'messageCountInputField';
input.type = 'number';
input.placeholder = 'Number of messages';
return input;
}
function scrapeConversation() {
const messageBlocks = Array.from(document.querySelectorAll('.c-message_kit__background'));
let conversation = '', lastSender = null;
messageBlocks.forEach((block) => {
const sender = block.querySelector('.c-message__sender_button')?.textContent.trim() || lastSender;
if (sender) lastSender = sender;
const messageText = Array.from(block.querySelectorAll('.p-rich_text_section'))
.map((el) => el.textContent.trim())
.join(' ').trim();
if (messageText) {
conversation += `${lastSender}: ${messageText}\n\n`;
}
});
if (conversation.trim()) {
copyToClipboard(conversation, 'All conversations copied!');
} else {
showPopUp('No conversation found.');
}
}
function copyMessages() {
const numberOfMessages = document.getElementById('messageCountInputField').value || 2;
const messageBlocks = Array.from(document.querySelectorAll('.c-message_kit__background')).slice(-numberOfMessages);
let conversation = '', lastSender = null;
messageBlocks.forEach((block) => {
const sender = block.querySelector('.c-message__sender_button')?.textContent.trim() || lastSender;
if (sender) lastSender = sender;
const messageText = Array.from(block.querySelectorAll('.p-rich_text_section'))
.map((el) => el.textContent.trim())
.join(' ').trim();
if (messageText) {
conversation += `${lastSender}: ${messageText}\n\n`;
}
});
if (conversation.trim()) {
const formatted = `${conversation}Guess reply:`;
copyToClipboard(formatted, `Last ${numberOfMessages} messages copied!`);
} else {
showPopUp(`Unable to copy ${numberOfMessages} messages.`);
}
}
function copyToClipboard(text, message) {
navigator.clipboard.writeText(text)
.then(() => showPopUp(message))
.catch(() => showPopUp('Failed to copy.'));
}
function sendMessage(message) {
const textBox = document.querySelector('[data-message-input="true"] .ql-editor');
const sendButton = document.querySelector('[data-qa="texty_send_button"]');
if (textBox) {
textBox.focus();
document.execCommand('insertText', false, message);
setTimeout(() => sendButton?.click(), 500);
} else {
showPopUp('Message box not found.');
}
}
function showPopUp(message) {
const popUp = document.createElement('div');
Object.assign(popUp.style, {
position: 'fixed',
bottom: '245px',
right: '100px',
backgroundColor: '#A294F9',
color: '#FFFFFF',
padding: '10px 15px',
borderRadius: '16px 16px 0px 16px',
fontSize: '14px',
boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.3)',
animation: 'fadeInOut 3s ease-in-out',
zIndex: '9999',
});
popUp.textContent = message;
document.body.appendChild(popUp);
setTimeout(() => popUp.remove(), 3000);
}
const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerText = `
@keyframes fadeInOut {
0% {
opacity: 0;
transform: translateY(20px);
}
10% {
opacity: 1;
transform: translateY(0);
}
90% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(20px);
}
}
`;
document.head.appendChild(styleSheet);
waitForSlackInterface();
})();