飞书妙计自动复制转换后的文字到剪切板

Extract title and subtitle from Feishu Meeting Minutes, format and copy to clipboard with centered non-blocking alerts and retry mechanism

  1. // ==UserScript==
  2. // @name 飞书妙计自动复制转换后的文字到剪切板
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.6
  5. // @description Extract title and subtitle from Feishu Meeting Minutes, format and copy to clipboard with centered non-blocking alerts and retry mechanism
  6. // @author You
  7. // @match https://*.feishu.cn/minutes/obcn*
  8. // @grant GM_setClipboard
  9. // @license GPLv3
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. function notify(message) {
  16. const notificationBoxId = 'tm-notification-box';
  17. let box = document.getElementById(notificationBoxId);
  18. if (!box) {
  19. box = document.createElement('div');
  20. box.id = notificationBoxId;
  21. Object.assign(box.style, {
  22. position: 'fixed',
  23. top: '50%',
  24. left: '50%',
  25. transform: 'translate(-50%, -50%)',
  26. backgroundColor: 'lightgrey',
  27. padding: '20px',
  28. zIndex: 10000,
  29. borderRadius: '10px',
  30. boxShadow: '0 4px 8px rgba(0,0,0,0.5)',
  31. textAlign: 'center',
  32. maxWidth: '80%',
  33. wordWrap: 'break-word'
  34. });
  35. document.body.appendChild(box);
  36. }
  37. box.style.display = 'block';
  38. box.textContent = message;
  39. setTimeout(() => {
  40. box.style.display = 'none';
  41. }, 3000);
  42. }
  43.  
  44. function copyToClipboard(text) {
  45. try {
  46. GM_setClipboard(text);
  47. notify('已复制到剪切板');
  48. } catch (e) {
  49. notify('复制失败,请手动复制');
  50. }
  51. }
  52.  
  53. function processText(inputText) {
  54. // 替换中文全角标点的","和"。"为换行符
  55. inputText = inputText.replace(/,/g, "\n").replace(/。/g, "\n");
  56.  
  57. // 在中文全角标点的"?"和"!"后加上换行符
  58. inputText = inputText.replace(/?/g, "?\n").replace(/!/g, "!\n");
  59.  
  60. // 将所有的\r换行符替换成\n换行符
  61. inputText = inputText.replace(/\r/g, "\n");
  62.  
  63. // 将连续的\n\n换行符替换成一个\n换行符
  64. while (inputText.includes("\n\n")) {
  65. inputText = inputText.replace(/\n\n/g, "\n");
  66. }
  67.  
  68. return inputText;
  69. }
  70.  
  71. function extractAndCopy(triesLeft = 3) {
  72. if (triesLeft === 0) {
  73. notify('查找失败,已放弃');
  74. return;
  75. }
  76.  
  77. notify('正在查找信息...');
  78. const titleXpath = "//div[@class='larkw-web-header-caption-head-title-edit']//span";
  79. const textXpath = "//div[@class='subtitle-comp']/div[@id='subtitle-scroll-container']";
  80. const titleResults = document.evaluate(titleXpath, document, null, XPathResult.ANY_TYPE, null);
  81. const textResults = document.evaluate(textXpath, document, null, XPathResult.ANY_TYPE, null);
  82. const titleElement = titleResults.iterateNext();
  83. const textElement = textResults.iterateNext();
  84.  
  85. if (titleElement && textElement) {
  86. const titleContent = titleElement.textContent || "";
  87. const textContent = textElement.textContent || "";
  88. if (titleContent && textContent) {
  89. notify('查找成功');
  90. const processedText = processText(textContent); // 调用处理文本函数
  91. copyToClipboard(`#### ${titleContent}\n${processedText}\n\n`);
  92. } else {
  93. notify('查找成功,但是没有找到完整的内容');
  94. }
  95. } else {
  96. notify(`查找失败,尝试剩余次数:${triesLeft - 1}`);
  97. setTimeout(() => extractAndCopy(triesLeft - 1), 1000);
  98. }
  99. }
  100.  
  101. if (document.readyState === "complete" || document.readyState === "interactive") {
  102. setTimeout(extractAndCopy, 1000);
  103. } else {
  104. document.addEventListener("DOMContentLoaded", () => setTimeout(extractAndCopy, 1000));
  105. }
  106. })();
  107. // ==UserScript==
  108. // @name 飞书妙计自动复制转换后的文字到剪切板
  109. // @namespace http://tampermonkey.net/
  110. // @version 0.6
  111. // @description Extract title and subtitle from Feishu Meeting Minutes, format and copy to clipboard with centered non-blocking alerts and retry mechanism
  112. // @author You
  113. // @match https://*.feishu.cn/minutes/obcn*
  114. // @grant GM_setClipboard
  115. // ==/UserScript==
  116.  
  117. (function() {
  118. 'use strict';
  119.  
  120. function notify(message) {
  121. const notificationBoxId = 'tm-notification-box';
  122. let box = document.getElementById(notificationBoxId);
  123. if (!box) {
  124. box = document.createElement('div');
  125. box.id = notificationBoxId;
  126. Object.assign(box.style, {
  127. position: 'fixed',
  128. top: '50%',
  129. left: '50%',
  130. transform: 'translate(-50%, -50%)',
  131. backgroundColor: 'lightgrey',
  132. padding: '20px',
  133. zIndex: 10000,
  134. borderRadius: '10px',
  135. boxShadow: '0 4px 8px rgba(0,0,0,0.5)',
  136. textAlign: 'center',
  137. maxWidth: '80%',
  138. wordWrap: 'break-word'
  139. });
  140. document.body.appendChild(box);
  141. }
  142. box.style.display = 'block';
  143. box.textContent = message;
  144. setTimeout(() => {
  145. box.style.display = 'none';
  146. }, 3000);
  147. }
  148.  
  149. function copyToClipboard(text) {
  150. try {
  151. GM_setClipboard(text);
  152. notify('已复制到剪切板');
  153. } catch (e) {
  154. notify('复制失败,请手动复制');
  155. }
  156. }
  157.  
  158. function processText(inputText) {
  159. // 删除所有的“嗯”
  160. inputText = inputText.replace(/嗯/g, "");
  161. // 删除所有的“唉”
  162. inputText = inputText.replace(/唉/g, "");
  163.  
  164. // 替换中文全角标点的","和"。"为换行符
  165. inputText = inputText.replace(/,/g, "\n").replace(/。/g, "\n");
  166.  
  167. // 在中文全角标点的"?"和"!"后加上换行符
  168. inputText = inputText.replace(/?/g, "?\n").replace(/!/g, "!\n");
  169.  
  170. // 将所有的\r换行符替换成\n换行符
  171. inputText = inputText.replace(/\r/g, "\n");
  172.  
  173. // 将连续的\n\n换行符替换成一个\n换行符
  174. while (inputText.includes("\n\n")) {
  175. inputText = inputText.replace(/\n\n/g, "\n");
  176. }
  177.  
  178. return inputText;
  179. }
  180.  
  181. function extractAndCopy(triesLeft = 3) {
  182. if (triesLeft === 0) {
  183. notify('查找失败,已放弃');
  184. return;
  185. }
  186.  
  187. notify('正在查找信息...');
  188. const titleXpath = "//div[@class='larkw-web-header-caption-head-title-edit']//span";
  189. const textXpath = "//div[@class='subtitle-comp']/div[@id='subtitle-scroll-container']";
  190. const titleResults = document.evaluate(titleXpath, document, null, XPathResult.ANY_TYPE, null);
  191. const textResults = document.evaluate(textXpath, document, null, XPathResult.ANY_TYPE, null);
  192. const titleElement = titleResults.iterateNext();
  193. const textElement = textResults.iterateNext();
  194.  
  195. if (titleElement && textElement) {
  196. const titleContent = titleElement.textContent || "";
  197. const textContent = textElement.textContent || "";
  198. if (titleContent && textContent) {
  199. notify('查找成功');
  200. const processedText = processText(textContent); // 调用处理文本函数
  201. copyToClipboard(`#### ${titleContent}\n${processedText}\n\n`);
  202. } else {
  203. notify('查找成功,但是没有找到完整的内容');
  204. }
  205. } else {
  206. notify(`查找失败,尝试剩余次数:${triesLeft - 1}`);
  207. setTimeout(() => extractAndCopy(triesLeft - 1), 1000);
  208. }
  209. }
  210.  
  211. if (document.readyState === "complete" || document.readyState === "interactive") {
  212. setTimeout(extractAndCopy, 1000);
  213. } else {
  214. document.addEventListener("DOMContentLoaded", () => setTimeout(extractAndCopy, 1000));
  215. }
  216. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址