// ==UserScript== // @name 腾讯会议智能操作 & 自动关闭 // @namespace https://meeting.tencent.com/ // @version 1.0.1 // @icon https://meeting.tencent.com/favicon.ico // @description 打开腾讯会议链接后:进行中→加入会议;未开始→添加到会议列表;操作后倒计时关闭页面(可取消) // @author bo Sea // @match https://meeting.tencent.com/dm/* // @match https://meeting.weixin.qq.com/dm/* // @grant window.close // @run-at document-idle // @license MIT // ==/UserScript== (function () { 'use strict'; const THRESHOLD = 5 * 60; const CLOSE_SEC = 5; let cancelFlag = false; let closeTimer = null; function injectStyle() { if (document.getElementById('tm-style')) return; const s = document.createElement('style'); s.id = 'tm-style'; s.textContent = ` @keyframes tm-in { from { transform: translateX(120%); opacity: 0 } to { transform: translateX(0); opacity: 1 } } #tm-box { position: fixed; top: 16px; right: 16px; z-index: 999999; width: 310px; border-radius: 12px; overflow: hidden; box-shadow: 0 8px 32px rgba(0,0,0,.2); font-family: system-ui, -apple-system, sans-serif; animation: tm-in .3s ease-out; } .tm-header { padding: 14px 18px; color: #fff; display: flex; align-items: center; gap: 10px; } .tm-header .tm-icon { font-size: 20px } .tm-header .tm-title { font-weight: 600; font-size: 15px } .tm-body { padding: 14px 18px; background: #fff; font-size: 13px; line-height: 1.8; color: #333; } .tm-subject { font-weight: 600 } .tm-code { color: #999; font-size: 12px } .tm-hint { color: #999; font-size: 12px; margin-top: 4px } .tm-status { margin-top: 6px } .tm-countdown { font-size: 20px; font-weight: 700; margin-top: 8px } .tm-bar { margin-top: 12px; padding: 8px 12px; background: #f5f5f5; border-radius: 6px; display: flex; align-items: center; justify-content: space-between; } .tm-bar .tm-sec { color: #e53935; font-weight: bold } .tm-cancel-btn { padding: 4px 14px; font-size: 12px; border: 1px solid #e53935; color: #e53935; border-radius: 4px; background: #fff; cursor: pointer; } .tm-cancel-btn:hover { background: #fce4ec } .tm-muted { color: #999 } .tm-green { color: #0f9d58 } .tm-amber { color: #f9a825 } `; document.head.appendChild(s); } function getData() { const el = id => document.getElementById(id); const txt = id => (el(id)?.textContent || '').trim(); let start = null; const dm = txt('tm-meeting-start-date').match(/(\d{4})\s*年\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日/); const tm = txt('tm-meeting-start-time').match(/(\d{1,2}):(\d{2})/); if (dm && tm) start = new Date(+dm[1], dm[2] - 1, +dm[3], +tm[1], +tm[2]); return { state: txt('tm-meeting-state'), subject: txt('tm-meeting-subject') || '腾讯会议', code: txt('tm-meeting-code'), duration: txt('tm-meeting-duration'), dateStr: txt('tm-meeting-start-date'), timeStr: txt('tm-meeting-start-time'), start, }; } function toast(bgColor, icon, title, bodyHTML) { injectStyle(); let box = document.getElementById('tm-box'); if (!box) { box = document.createElement('div'); box.id = 'tm-box'; document.body.appendChild(box); } box.innerHTML = `
${icon} ${title}
${bodyHTML}
`; } function closeBarHTML() { return `
⏱ ${CLOSE_SEC} 秒后自动关闭
`; } function doCancel() { if (cancelFlag) return; cancelFlag = true; clearInterval(closeTimer); const bar = document.getElementById('tm-bar'); if (bar) bar.innerHTML = '已取消自动关闭'; } function startClose() { cancelFlag = false; let n = CLOSE_SEC; document.getElementById('tm-cancel')?.addEventListener('click', doCancel); document.addEventListener('keydown', e => { if (e.key === 'Escape') doCancel(); }, { once: true }); closeTimer = setInterval(() => { if (cancelFlag) return clearInterval(closeTimer); document.getElementById('tm-sec').textContent = --n; if (n <= 0) { clearInterval(closeTimer); doClose(); } }, 1000); } function doClose() { window.close(); setTimeout(() => { if (!document.hidden) history.back(); }, 300); } document.addEventListener('visibilitychange', () => { if (document.hidden && !cancelFlag) { clearInterval(closeTimer); doClose(); } }); function getDiffSec(start) { if (!start) return null; return Math.floor((start.getTime() - Date.now()) / 1000); } function fmtDuration(ms) { const h = Math.floor(ms / 3600000); const m = Math.floor((ms % 3600000) / 60000); return h > 0 ? `${h}小时${m}分钟` : `${m}分钟`; } function codeHTML(code) { return code ? `
会议号 ${code}
` : ''; } function doJoin() { const btns = document.querySelectorAll('[id="mpJoinBtnCtrl"]'); const btn = btns[1] || btns[0]; if (btn) { btn.click(); return '加入会议'; } return null; } function doCollect() { const el = document.getElementById('collect-meeting'); if (el) { el.click(); return true; } return false; } function main() { const d = getData(); const diffSec = getDiffSec(d.start); if (d.state === '已结束') { toast('#9e9e9e', '⚫', '会议已结束', `
${d.subject}
${d.dateStr} ${d.timeStr}
${closeBarHTML()} `); startClose(); return; } if (d.state === '进行中' || d.state === '会议中' || diffSec === null || diffSec <= THRESHOLD) { const result = doJoin(); toast(result ? '#0f9d58' : '#f57c00', result ? '🟢' : '⚠️', result ? '正在加入会议' : '加入失败', `
${d.subject}
${codeHTML(d.code)}
${result ? `✅ 已点击"${result}"` : '未找到加入按钮'}
${closeBarHTML()} `); startClose(); return; } const ok = doCollect(); toast(ok ? '#f9a825' : '#f57c00', ok ? '📋' : '⚠️', ok ? '会议未开始' : '收藏失败', `
${d.subject}
${codeHTML(d.code)}
开始:${d.dateStr} ${d.timeStr}
${ok ? `
时长:${d.duration}
` : ''} ${ok ? `
${fmtDuration(diffSec * 1000)}
` : ''}
${ok ? '✅ 已添加到会议列表' : '未找到添加按钮'}
${closeBarHTML()} `); if (ok) { const el = document.getElementById('tm-cd'); const t = setInterval(() => { const left = getDiffSec(d.start); if (left <= 0) { clearInterval(t); el.textContent = '已到时间'; el.className = 'tm-countdown tm-green'; return; } el.textContent = fmtDuration(left * 1000); }, 1000); } startClose(); } document.readyState === 'complete' ? main() : window.addEventListener('load', main); })();