Skip to content

Instantly share code, notes, and snippets.

@bibendi
Created March 1, 2026 11:06
Show Gist options
  • Select an option

  • Save bibendi/5b542e2edb05f0701cc8c90221921a55 to your computer and use it in GitHub Desktop.

Select an option

Save bibendi/5b542e2edb05f0701cc8c90221921a55 to your computer and use it in GitHub Desktop.
Tampermonkey Mattermost Thread Extractor
// ==UserScript==
// @name Mattermost Thread Extractor (Sbermarket) - Dual Mode
// @namespace http://tampermonkey.net/
// @version 2.4
// @description Копирует тред в буфер из любого режима просмотра (список тредов или правая панель)
// @author You
// @match https://mattermost.sbermarket.tech/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// SVG иконка копирования
const copyIcon = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: middle;">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>`;
const checkIcon = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: middle;">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>`;
function extractThread() {
// Ищем контейнер треда - пробуем разные варианты для разных режимов
let container = document.getElementById('thread-pane-container') ||
document.getElementById('rhsContainer') ||
document.querySelector('.sidebar--right');
if (!container) {
alert('Откройте тред (панель с тредом должна быть видна)');
return;
}
// Ищем посты
const posts = container.querySelectorAll('[data-testid="rhsPostView"], .post--thread, .post');
if (posts.length === 0) {
alert('Сообщения не найдены. Прокрутите тред вверх для загрузки всех сообщений.');
return;
}
let output = `=== Mattermost Thread Export ===\n`;
output += `URL: ${window.location.href}\n`;
output += `Всего сообщений: ${posts.length}\n`;
output += `Дата: ${new Date().toLocaleString('ru-RU')}\n\n`;
posts.forEach((post, idx) => {
const authorEl = post.querySelector('.user-popover');
const author = authorEl ? authorEl.textContent.trim() : 'Unknown';
const timeEl = post.querySelector('time.post__time');
const time = timeEl ? timeEl.textContent.trim() : '';
const textEl = post.querySelector('.post-message__text');
const text = textEl ? textEl.innerText.trim() : '[нет текста]';
const marker = idx === 0 ? '[НАЧАЛО ТРЕДА] ' : '';
output += `${marker}[${time}] ${author}:\n`;
output += `${text}\n`;
output += `${'-'.repeat(40)}\n\n`;
});
navigator.clipboard.writeText(output).then(() => {
// Визуальная обратная связь на всех кнопках копирования
document.querySelectorAll('.mm-copy-btn').forEach(btn => {
const originalHTML = btn.innerHTML;
btn.innerHTML = checkIcon;
btn.style.color = '#28a745';
setTimeout(() => {
btn.innerHTML = copyIcon;
btn.style.color = '';
}, 2000);
});
showNotification(`Скопировано ${posts.length} сообщений`);
}).catch(err => {
console.error('Ошибка:', err);
alert('Не удалось скопировать в буфер обмена');
});
}
function showNotification(msg) {
const div = document.createElement('div');
div.textContent = msg;
div.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #28a745;
color: white;
padding: 10px 16px;
border-radius: 4px;
font-family: sans-serif;
font-size: 13px;
z-index: 10000;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
`;
document.body.appendChild(div);
setTimeout(() => div.remove(), 2500);
}
function createCopyButton() {
const btn = document.createElement('button');
btn.className = 'mm-copy-btn btn btn-icon btn-sm';
btn.type = 'button';
btn.setAttribute('aria-label', 'Копировать тред');
btn.title = 'Копировать тред в буфер обмена';
btn.innerHTML = copyIcon;
btn.style.marginLeft = '4px';
btn.addEventListener('click', extractThread);
return btn;
}
function addButtonToHeaders() {
// Режим 1: Список тредов (ThreadPane)
const threadPaneHeader = document.querySelector('.ThreadPane___header');
if (threadPaneHeader && !threadPaneHeader.querySelector('.mm-copy-btn')) {
const menuWrapper = threadPaneHeader.querySelector('.MenuWrapper');
if (menuWrapper) {
threadPaneHeader.insertBefore(createCopyButton(), menuWrapper);
}
}
// Режим 2: Правая панель (sidebar--right__header)
const sidebarHeader = document.querySelector('.sidebar--right__header');
if (sidebarHeader && !sidebarHeader.querySelector('.mm-copy-btn')) {
const controls = sidebarHeader.querySelector('.controls');
if (controls) {
// Вставляем перед кнопкой закрытия (rhsCloseButton)
const closeBtn = document.getElementById('rhsCloseButton');
if (closeBtn) {
controls.insertBefore(createCopyButton(), closeBtn);
} else {
// Или просто в конец controls, если кнопка закрытия не найдена
controls.appendChild(createCopyButton());
}
}
}
}
// Проверяем каждую секунду (Mattermont - SPA, элементы появляются динамически)
setInterval(addButtonToHeaders, 1000);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment