抽奖工具新增了个抽奖模板文案复制

方便复制抽奖贴文案,填写抽奖主题即可

抽奖主题:

:trophy: 奖品详情:

:three_o_clock: 活动时间:

开始时间:
截止时间:

:memo: 参与方式:

在本帖下回复任意内容

:magnifying_glass_tilted_left: 抽奖规则:

每位用户仅允许参与一次。
使用官方抽奖工具随机抽取中奖者。

:warning: 注意事项:

本活动将在活动截止时间后关闭回帖,以确保公正性。
中奖者将在活动结束后24小时内在本帖公布,并通过私信通知领奖方式。
所有规则及抽奖结果由活动发起人和论坛管理团队最终解释。

期待您的积极参与,祝您好运!如有任何疑问,欢迎随时联系抽奖发起人。

油猴源码
// ==UserScript==
// @name         LINUX DO 抽奖助手
// @namespace    https://linux.do/
// @version      0.1.2
// @description  在 LINUX DO 论坛帖子页面添加抽奖功能
// @author       linux.do
// @match        https://linux.do/t/*
// @grant        GM_xmlhttpRequest
// @connect      connect.linux.do
// ==/UserScript==

(function() {
    'use strict';

    const VERSION = '0.1.2';

    // 添加抽奖按钮样式
    const style = document.createElement('style');
    style.textContent = `
        .d-header-icons .lottery-btn,
        .header-icons .lottery-btn {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 34px;
            height: 34px;
            border-radius: 50%;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            cursor: pointer;
            font-size: 16px;
            box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
            transition: transform 0.2s, box-shadow 0.2s;
            margin: 0 0 0 5px;
            padding: 0;
            vertical-align: middle;
        }
        .d-header-icons .lottery-btn:hover,
        .header-icons .lottery-btn:hover {
            transform: scale(1.08);
            box-shadow: 0 3px 10px rgba(102, 126, 234, 0.5);
        }
        /* 备用固定定位样式 */
        .lottery-btn-fixed {
            position: fixed;
            right: 70px;
            top: 10px;
            z-index: 9999;
            width: 34px;
            height: 34px;
            border-radius: 50%;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            cursor: pointer;
            font-size: 16px;
            box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
        }
        .lottery-modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 10000;
            justify-content: center;
            align-items: center;
        }
        .lottery-modal.show { display: flex; }
        .lottery-dialog {
            background: var(--secondary, #fff);
            color: var(--primary, #333);
            border-radius: 12px;
            padding: 24px;
            width: 90%;
            max-width: 500px;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
        }
        .lottery-dialog h2 {
            margin: 0 0 20px;
            font-size: 1.5rem;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .lottery-dialog label {
            display: block;
            margin: 12px 0 6px;
            font-weight: 600;
        }
        .lottery-dialog input {
            width: 100%;
            padding: 10px 12px;
            border: 1px solid var(--primary-low, #ddd);
            border-radius: 6px;
            font-size: 1rem;
            background: var(--secondary, #fff);
            color: var(--primary, #333);
        }
        .lottery-dialog input:focus {
            outline: none;
            border-color: #667eea;
            box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
        }
        .lottery-actions {
            display: flex;
            gap: 12px;
            margin-top: 20px;
        }
        .lottery-actions button {
            flex: 1;
            padding: 12px;
            border: none;
            border-radius: 6px;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: opacity 0.2s;
        }
        .lottery-actions button:hover { opacity: 0.9; }
        .lottery-actions button:disabled { opacity: 0.5; cursor: not-allowed; }
        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        .btn-secondary {
            background: var(--primary-low, #e5e5e5);
            color: var(--primary, #333);
        }
        .lottery-result {
            margin-top: 16px;
            padding: 12px;
            background: var(--primary-very-low, #f5f5f5);
            border-radius: 8px;
            font-family: monospace;
            font-size: 0.85rem;
            white-space: pre-wrap;
            max-height: 300px;
            overflow-y: auto;
        }
        .lottery-status {
            margin-top: 12px;
            padding: 8px 12px;
            border-radius: 6px;
            font-size: 0.9rem;
        }
        .lottery-status.info { background: #e0f2fe; color: #0369a1; }
        .lottery-status.error { background: #fee2e2; color: #dc2626; }
        .lottery-status.success { background: #d1fae5; color: #059669; }
    `;
    document.head.appendChild(style);

    // 创建抽奖按钮
    const btn = document.createElement('button');
    btn.className = 'lottery-btn';
    btn.innerHTML = '🎲';
    btn.title = '抽奖';

    // 插入到头部导航栏右侧(头像旁边)
    function insertButton() {
        const headerIcons = document.querySelector('.d-header-icons');
        if (headerIcons) {
            const wrapper = document.createElement('li');
            wrapper.className = 'header-dropdown-toggle lottery-wrapper';
            wrapper.style.cssText = 'display:flex;align-items:center;';
            wrapper.appendChild(btn);
            headerIcons.appendChild(wrapper);
        } else {
            btn.className = 'lottery-btn-fixed';
            document.body.appendChild(btn);
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', insertButton);
    } else {
        setTimeout(insertButton, 500);
    }

    // 创建弹窗
    const modal = document.createElement('div');
    modal.className = 'lottery-modal';
    modal.innerHTML = `
        <div class="lottery-dialog">
            <h2>🎲 LINUX DO 抽奖</h2>
            <div id="lottery-topic-info"></div>

            <div class="lottery-actions" style="margin-top: 10px;">
                <button class="btn-secondary" id="lottery-copy-template">📝 复制文案模板</button>
            </div>

            <label for="lottery-count">中奖人数</label>
            <input type="number" id="lottery-count" min="1" value="1" placeholder="请输入中奖人数">

            <label for="lottery-last-floor">最后楼层 (可选)</label>
            <input type="number" id="lottery-last-floor" min="1" placeholder="留空表示全部楼层参与">

            <label for="lottery-required-text">回复必须包含的文本 (可选)</label>
            <input type="text" id="lottery-required-text" placeholder="只抽取包含指定文本的回复">

            <div id="lottery-status" class="lottery-status info" style="display:none"></div>

            <div class="lottery-actions">
                <button class="btn-secondary" id="lottery-cancel">取消</button>
                <button class="btn-primary" id="lottery-start">开始抽奖</button>
            </div>

            <div id="lottery-result" class="lottery-result" style="display:none"></div>

            <div class="lottery-actions" id="lottery-copy-wrap" style="display:none">
                <button class="btn-primary" id="lottery-copy">📋 复制结果</button>
            </div>
        </div>
    `;
    document.body.appendChild(modal);

    // 存储中奖用户信息,用于复制时生成 @ 列表
    let winnersInfo = [];
    let currentTopic = null; // 缓存当前 topic 信息(用于模板自动填标题)

    // 事件绑定
    btn.onclick = () => {
        modal.classList.add('show');
        checkTopic();
    };
    modal.onclick = (e) => { if (e.target === modal) modal.classList.remove('show'); };

    document.getElementById('lottery-cancel').onclick = () => modal.classList.remove('show');
    document.getElementById('lottery-start').onclick = startLottery;
    document.getElementById('lottery-copy').onclick = copyResult;
    document.getElementById('lottery-copy-template').onclick = copyTemplate;

    function showStatus(msg, type = 'info') {
        const el = document.getElementById('lottery-status');
        el.textContent = msg;
        el.className = `lottery-status ${type}`;
        el.style.display = 'block';
    }

    function hideStatus() {
        document.getElementById('lottery-status').style.display = 'none';
    }

    // 获取帖子ID
    function getTopicId() {
        const match = location.pathname.match(/\/t\/(?:topic\/)?(\d+)/);
        return match ? match[1] : null;
    }

    // 检查帖子状态
    async function checkTopic() {
        const topicId = getTopicId();
        if (!topicId) {
            showStatus('无法识别帖子ID', 'error');
            return;
        }

        try {
            const resp = await fetch(`/t/${topicId}.json`);
            const data = await resp.json();
            currentTopic = data;

            const info = document.getElementById('lottery-topic-info');
            info.innerHTML = `
                <p><strong>帖子:</strong>${data.title}</p>
                <p><strong>作者:</strong>${data.details.created_by.username}</p>
                <p><strong>状态:</strong>${data.closed ? '✅ 已关闭' : data.archived ? '✅ 已存档' : '❌ 未关闭'}</p>
            `;

            if (!data.closed && !data.archived) {
                showStatus('帖子尚未关闭或存档,无法抽奖', 'error');
                document.getElementById('lottery-start').disabled = true;
            } else {
                hideStatus();
                document.getElementById('lottery-start').disabled = false;
            }
        } catch (e) {
            showStatus('获取帖子信息失败: ' + e.message, 'error');
        }
    }

    // MD5 实现
    function md5(string) {
        function md5cycle(x, k) {
            var a = x[0], b = x[1], c = x[2], d = x[3];
            a = ff(a, b, c, d, k[0], 7, -680876936); d = ff(d, a, b, c, k[1], 12, -389564586);
            c = ff(c, d, a, b, k[2], 17, 606105819); b = ff(b, c, d, a, k[3], 22, -1044525330);
            a = ff(a, b, c, d, k[4], 7, -176418897); d = ff(d, a, b, c, k[5], 12, 1200080426);
            c = ff(c, d, a, b, k[6], 17, -1473231341); b = ff(b, c, d, a, k[7], 22, -45705983);
            a = ff(a, b, c, d, k[8], 7, 1770035416); d = ff(d, a, b, c, k[9], 12, -1958414417);
            c = ff(c, d, a, b, k[10], 17, -42063); b = ff(b, c, d, a, k[11], 22, -1990404162);
            a = ff(a, b, c, d, k[12], 7, 1804603682); d = ff(d, a, b, c, k[13], 12, -40341101);
            c = ff(c, d, a, b, k[14], 17, -1502002290); b = ff(b, c, d, a, k[15], 22, 1236535329);
            a = gg(a, b, c, d, k[1], 5, -165796510); d = gg(d, a, b, c, k[6], 9, -1069501632);
            c = gg(c, d, a, b, k[11], 14, 643717713); b = gg(b, c, d, a, k[0], 20, -373897302);
            a = gg(a, b, c, d, k[5], 5, -701558691); d = gg(d, a, b, c, k[10], 9, 38016083);
            c = gg(c, d, a, b, k[15], 14, -660478335); b = gg(b, c, d, a, k[4], 20, -405537848);
            a = gg(a, b, c, d, k[9], 5, 568446438); d = gg(d, a, b, c, k[14], 9, -1019803690);
            c = gg(c, d, a, b, k[3], 14, -187363961); b = gg(b, c, d, a, k[8], 20, 1163531501);
            a = gg(a, b, c, d, k[13], 5, -1444681467); d = gg(d, a, b, c, k[2], 9, -51403784);
            c = gg(c, d, a, b, k[7], 14, 1735328473); b = gg(b, c, d, a, k[12], 20, -1926607734);
            a = hh(a, b, c, d, k[5], 4, -378558); d = hh(d, a, b, c, k[8], 11, -2022574463);
            c = hh(c, d, a, b, k[11], 16, 1839030562); b = hh(b, c, d, a, k[14], 23, -35309556);
            a = hh(a, b, c, d, k[1], 4, -1530992060); d = hh(d, a, b, c, k[4], 11, 1272893353);
            c = hh(c, d, a, b, k[7], 16, -155497632); b = hh(b, c, d, a, k[10], 23, -1094730640);
            a = hh(a, b, c, d, k[13], 4, 681279174); d = hh(d, a, b, c, k[0], 11, -358537222);
            c = hh(c, d, a, b, k[3], 16, -722521979); b = hh(b, c, d, a, k[6], 23, 76029189);
            a = hh(a, b, c, d, k[9], 4, -640364487); d = hh(d, a, b, c, k[12], 11, -421815835);
            c = hh(c, d, a, b, k[15], 16, 530742520); b = hh(b, c, d, a, k[2], 23, -995338651);
            a = ii(a, b, c, d, k[0], 6, -198630844); d = ii(d, a, b, c, k[7], 10, 1126891415);
            c = ii(c, d, a, b, k[14], 15, -1416354905); b = ii(b, c, d, a, k[5], 21, -57434055);
            a = ii(a, b, c, d, k[12], 6, 1700485571); d = ii(d, a, b, c, k[3], 10, -1894986606);
            c = ii(c, d, a, b, k[10], 15, -1051523); b = ii(b, c, d, a, k[1], 21, -2054922799);
            a = ii(a, b, c, d, k[8], 6, 1873313359); d = ii(d, a, b, c, k[15], 10, -30611744);
            c = ii(c, d, a, b, k[6], 15, -1560198380); b = ii(b, c, d, a, k[13], 21, 1309151649);
            a = ii(a, b, c, d, k[4], 6, -145523070); d = ii(d, a, b, c, k[11], 10, -1120210379);
            c = ii(c, d, a, b, k[2], 15, 718787259); b = ii(b, c, d, a, k[9], 21, -343485551);
            x[0] = add32(a, x[0]); x[1] = add32(b, x[1]); x[2] = add32(c, x[2]); x[3] = add32(d, x[3]);
        }
        function cmn(q, a, b, x, s, t) { a = add32(add32(a, q), add32(x, t)); return add32((a << s) | (a >>> (32 - s)), b); }
        function ff(a, b, c, d, x, s, t) { return cmn((b & c) | ((~b) & d), a, b, x, s, t); }
        function gg(a, b, c, d, x, s, t) { return cmn((b & d) | (c & (~d)), a, b, x, s, t); }
        function hh(a, b, c, d, x, s, t) { return cmn(b ^ c ^ d, a, b, x, s, t); }
        function ii(a, b, c, d, x, s, t) { return cmn(c ^ (b | (~d)), a, b, x, s, t); }
        function md51(s) {
            var n = s.length, state = [1732584193, -271733879, -1732584194, 271733878], i;
            for (i = 64; i <= s.length; i += 64) md5cycle(state, md5blk(s.substring(i - 64, i)));
            s = s.substring(i - 64);
            var tail = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
            for (i = 0; i < s.length; i++) tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
            tail[i >> 2] |= 0x80 << ((i % 4) << 3);
            if (i > 55) { md5cycle(state, tail); for (i = 0; i < 16; i++) tail[i] = 0; }
            tail[14] = n * 8;
            md5cycle(state, tail);
            return state;
        }
        function md5blk(s) {
            var md5blks = [], i;
            for (i = 0; i < 64; i += 4) md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
            return md5blks;
        }
        var hex_chr = '0123456789abcdef'.split('');
        function rhex(n) { var s = '', j = 0; for (; j < 4; j++) s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F]; return s; }
        function hex(x) { for (var i = 0; i < x.length; i++) x[i] = rhex(x[i]); return x.join(''); }
        function add32(a, b) { return (a + b) & 0xFFFFFFFF; }
        return hex(md51(string));
    }

    // SHA 哈希
    async function sha(algo, data) {
        const buf = await crypto.subtle.digest(algo, new TextEncoder().encode(data));
        return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');
    }

    // 伪随机数生成器
    class SeededRandom {
        constructor(seed) {
            this.seed = 0;
            for (let i = 0; i < seed.length; i++) {
                this.seed = ((this.seed << 5) - this.seed) + seed.charCodeAt(i);
                this.seed = this.seed & 0x7fffffff;
            }
        }
        next() {
            this.seed = (this.seed * 1103515245 + 12345) & 0x7fffffff;
            return this.seed / 0x7fffffff;
        }
        choice(arr) {
            return arr[Math.floor(this.next() * arr.length)];
        }
    }

    // 格式化日期
    function formatDate(isoString) {
        const d = new Date(isoString);
        const pad = n => n.toString().padStart(2, '0');
        return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
    }

    // 获取帖子内容
    async function fetchPostContent(postId) {
        const resp = await fetch(`/posts/${postId}.json`);
        if (!resp.ok) throw new Error(`获取帖子内容失败: ${resp.status}`);
        const data = await resp.json();
        return data.raw || '';
    }

    // 开始抽奖
    async function startLottery() {
        const topicId = getTopicId();
        const winnersCount = parseInt(document.getElementById('lottery-count').value) || 1;
        const lastFloor = document.getElementById('lottery-last-floor').value ? parseInt(document.getElementById('lottery-last-floor').value) : null;
        const requiredText = document.getElementById('lottery-required-text').value.trim() || null;

        const startBtn = document.getElementById('lottery-start');
        startBtn.disabled = true;
        startBtn.textContent = '抽奖中...';

        try {
            // 获取帖子信息
            showStatus('正在获取帖子信息...', 'info');
            const topicResp = await fetch(`/t/${topicId}.json`);
            const topic = await topicResp.json();
            currentTopic = topic;

            // 获取有效楼层
            showStatus('正在获取有效楼层...', 'info');
            const postsData = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: `https://connect.linux.do/api/topic/${topicId}/valid_post_number`,
                    onload: (resp) => {
                        if (resp.status === 200) {
                            try {
                                const data = JSON.parse(resp.responseText);
                                resolve(data);
                            } catch (e) {
                                reject(new Error('API返回数据格式错误'));
                            }
                        } else if (resp.status === 404) {
                            reject(new Error('该帖子不在抽奖支持的版块,或帖子不存在'));
                        } else if (resp.status === 403) {
                            reject(new Error('无权访问,请确保已登录'));
                        } else {
                            reject(new Error(`API请求失败: ${resp.status} - ${resp.responseText}`));
                        }
                    },
                    onerror: () => reject(new Error('网络请求失败,请检查网络连接'))
                });
            });

            let numbers = postsData.rows || [];
            let ids = postsData.ids || [];
            let created = postsData.created || [];

            if (!numbers.length) {
                if (postsData.error) throw new Error(`API错误: ${postsData.error}`);
                if (postsData.success === true) throw new Error('该帖子没有有效的参与楼层(可能原因:没有回复、回复已被删除、或回复者不符合条件)');
                throw new Error('该帖不符合抽奖条件(可能原因:版块不支持抽奖、帖子未关闭)');
            }

            // 截取楼层
            if (lastFloor) {
                const cutIndex = numbers.findIndex(n => n > lastFloor);
                if (cutIndex > 0) {
                    numbers = numbers.slice(0, cutIndex);
                    ids = ids.slice(0, cutIndex);
                    created = created.slice(0, cutIndex);
                }
            }

            // 过滤包含指定文本的回复
            if (requiredText) {
                showStatus(`正在检查回复内容是否包含: ${requiredText} (0/${ids.length})`, 'info');
                const filteredNumbers = [];
                const filteredIds = [];
                const filteredCreated = [];

                for (let i = 0; i < ids.length; i++) {
                    showStatus(`正在检查回复内容是否包含: ${requiredText} (${i + 1}/${ids.length})`, 'info');
                    const content = await fetchPostContent(ids[i]);
                    if (content.includes(requiredText)) {
                        filteredNumbers.push(numbers[i]);
                        filteredIds.push(ids[i]);
                        filteredCreated.push(created[i]);
                    }
                }

                numbers = filteredNumbers;
                ids = filteredIds;
                created = filteredCreated;

                if (!numbers.length) throw new Error(`没有找到包含 "${requiredText}" 的有效回复`);
            }

            const totalFloors = numbers.length;
            const count = Math.min(winnersCount, totalFloors);

            // 生成种子
            showStatus('正在计算抽奖结果...', 'info');
            const content = [
                count, topicId, topic.details.created_by.username, topic.created_at,
                ids.join(','), numbers.join(','), created.join(',')
            ].join('|');

            const md5Hash = md5(content);
            const sha1Hash = await sha('SHA-1', content);
            const sha512Hash = await sha('SHA-512', content);
            const seed = await sha('SHA-256', md5Hash + sha1Hash + sha512Hash);

            // 抽奖
            const rng = new SeededRandom(seed);
            const available = [...numbers];
            const winners = [];
            for (let i = 0; i < count; i++) {
                const winner = rng.choice(available);
                available.splice(available.indexOf(winner), 1);
                winners.push(winner);
            }

            // 获取中奖楼层对应的用户名
            showStatus('正在获取中奖用户信息...', 'info');
            winnersInfo = [];
            for (const floor of winners) {
                const floorIndex = numbers.indexOf(floor);
                const postId = ids[floorIndex];
                try {
                    const postResp = await fetch(`/posts/${postId}.json`);
                    const postData = await postResp.json();
                    winnersInfo.push({ floor, username: postData.username, postId });
                } catch (e) {
                    winnersInfo.push({ floor, username: '未知用户', postId });
                }
            }

            // 格式化结果
            const divider = '='.repeat(80);
            const divider2 = '-'.repeat(80);
            const baseUrl = `https://linux.do/t/topic/${topicId}`;

            let result = `${divider}
${'LINUX DO 抽奖结果 - ' + VERSION}
${divider}
帖子链接: ${baseUrl}
帖子标题: ${topic.title}
帖子作者: ${topic.details.created_by.username}
发帖时间: ${formatDate(topic.created_at)}
${divider2}
抽奖时间: ${formatDate(new Date().toISOString())}
参与楼层: ${numbers[0]} - ${numbers[numbers.length - 1]} 楼
有效楼层: ${totalFloors} 楼
中奖数量: ${count} 个
最终种子: ${seed}
${divider2}
恭喜以下楼层中奖:
${divider2}
`;
            winners.forEach((floor, i) => {
                const idx = (i + 1).toString().padStart(3, ' ');
                result += `[${idx}] ${floor.toString().padStart(4, ' ')} 楼,楼层链接: ${baseUrl}/${floor}\n`;
            });

            result += `${divider}
注: 楼层顺序即为抽奖顺序
${divider}`;

            // 显示结果
            showStatus('抽奖完成!', 'success');
            document.getElementById('lottery-result').textContent = result;
            document.getElementById('lottery-result').style.display = 'block';
            document.getElementById('lottery-copy-wrap').style.display = 'flex';

        } catch (e) {
            showStatus('抽奖失败: ' + e.message, 'error');
        } finally {
            startBtn.disabled = false;
            startBtn.textContent = '开始抽奖';
        }
    }

    // 复制结果(带 @ 中奖用户)
    function copyResult() {
        const result = document.getElementById('lottery-result').textContent;
        const topicId = getTopicId();
        const baseUrl = `https://linux.do/t/topic/${topicId}`;

        let atList = '\n以下为中奖佬友及对应楼层:\n\n';
        winnersInfo.forEach(info => {
            atList += `* @${info.username} - [${info.floor} 楼](${baseUrl}/${info.floor})\n`;
        });

        const fullResult = '```text\n' + result + '\n```' + atList;

        navigator.clipboard.writeText(fullResult).then(() => {
            const b = document.getElementById('lottery-copy');
            b.textContent = '✅ 已复制';
            setTimeout(() => b.textContent = '📋 复制结果', 2000);
        }).catch((e) => {
            showStatus('复制结果失败: ' + (e?.message || e), 'error');
        });
    }

    // 复制抽奖帖子文案模板(自动填充主题=当前帖子标题)
    function copyTemplate() {
        const title = (currentTopic && currentTopic.title) ? currentTopic.title : '';

        const template =
`抽奖主题:

:trophy: 奖品详情:

:three_o_clock: 活动时间:

开始时间:
截止时间:

:memo: 参与方式:

在本帖下回复任意内容

:magnifying_glass_tilted_left: 抽奖规则:

每位用户仅允许参与一次。
使用官方抽奖工具随机抽取中奖者。

:warning: 注意事项:

本活动将在活动截止时间后关闭回帖,以确保公正性。
中奖者将在活动结束后24小时内在本帖公布,并通过私信通知领奖方式。
所有规则及抽奖结果由活动发起人和论坛管理团队最终解释。

期待您的积极参与,祝您好运!如有任何疑问,欢迎随时联系抽奖发起人。`;

        navigator.clipboard.writeText(template).then(() => {
            const b = document.getElementById('lottery-copy-template');
            const old = b.textContent;
            b.textContent = '✅ 已复制模板';
            setTimeout(() => b.textContent = old, 2000);
        }).catch((e) => {
            showStatus('复制模板失败: ' + (e?.message || e), 'error');
        });
    }

})();
7 个赞

这是什么???

1 个赞

抽奖工具

1 个赞

谢谢分享

[!success]-谢谢分享
抽奖脚本不是很需要,有没有自动中奖脚本:tieba_087:

感谢大佬

感谢分享