Копирование из защищённого канала Телеграм
Оказалось, что в веб-версии Телеграма модератор канала может запретить копирование текста сообщений. Тривиальные способы, без привлечения браузерного “Inspect element”, не работают.

К счастью, для браузера Firefox есть дополнение GreaseMonkey. Наверняка, для Google Chrome тоже что-то подобное есть.

Подготовлен скрипт, который добавляет кнопку “Copy” рядом с каждым сообщением, после включения в GreaseMonkey.


Скрипт в свёрнутом блоке ниже:
// ==UserScript==
// @name Copy Content Blocks
// @namespace https://example.com
// @version 1.0
// @description Finds .content-inner blocks, adds a Copy button, and copies their text.
// @match https://web.telegram.org/*
// @grant none
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
/**
* Copy text to clipboard with a fallback.
*/
function copyTextToClipboard(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
// Modern async clipboard API
return navigator.clipboard.writeText(text);
} else {
// Fallback for older browsers
return new Promise((resolve, reject) => {
const temp = document.createElement('textarea');
temp.value = text;
document.body.appendChild(temp);
temp.select();
const success = document.execCommand('copy');
document.body.removeChild(temp);
success ? resolve() : reject(new Error('execCommand: copy failed.'));
});
}
}
/**
* Attach a "Copy" button to all .Message blocks that haven't been processed yet.
*/
function addCopyButtons() {
const messageBlocks = document.querySelectorAll('.Message:not([data-copybtn-attached])');
messageBlocks.forEach(block => {
block.setAttribute('data-copybtn-attached', 'true');
// Ensure the block is positioned so the absolutely placed button stays at the top-right corner
block.style.position = 'relative';
// Create the copy button
const copyBtn = document.createElement('button');
copyBtn.innerText = 'Copy';
// Style the button to float at the top-right, above other elements
copyBtn.style.zIndex = '999999';
copyBtn.style.pointerEvents = 'auto';
copyBtn.style.cursor = 'pointer';
// Clicking the button copies text content of the block
copyBtn.addEventListener('click', () => {
const textToCopy = block.textContent.trim();
copyTextToClipboard(textToCopy)
.then(() => {
console.log('Copied block content');
})
.catch(err => {
console.error('Failed to copy text:', err);
});
});
// Add the button to the block
block.prepend(copyBtn);
});
}
// Run once immediately
addCopyButtons();
// Use a MutationObserver for dynamically loaded content
const observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
if (mutation.addedNodes && mutation.addedNodes.length > 0) {
addCopyButtons();
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
})();