您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在Linux.do平台上进行抽奖,支持文章切换时自动更新,以表格形式展示结果,包含用户头像和参与时间,支持时间范围选择
当前为
// ==UserScript== // @name Linux.do 抽奖器 // @namespace http://linux.do/ // @version 1.0.1 // @description 在Linux.do平台上进行抽奖,支持文章切换时自动更新,以表格形式展示结果,包含用户头像和参与时间,支持时间范围选择 // @author PastKing // @match https://www.linux.do/t/topic/* // @match https://linux.do/t/topic/* // @grant none // @license MIT // @icon https://cdn.linux.do/uploads/default/optimized/1X/3a18b4b0da3e8cf96f7eea15241c3d251f28a39b_2_32x32.png // ==/UserScript== (function() { 'use strict'; let uiElements = null; // 创建UI元素 function createUI() { const container = document.createElement('div'); container.style.cssText = ` background-color: #ffffff; padding: 30px; border-radius: 10px; margin: 30px auto; text-align: center; max-width: 800px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); font-family: Arial, sans-serif; margin-bottom: 0 !important; `; const title = document.createElement('h2'); title.textContent = '🎉 Linux.do 抽奖器 - @PastKing'; title.style.cssText = ` color: #2c3e50; margin-bottom: 25px; font-weight: bold; `; const dateContainer = document.createElement('div'); dateContainer.style.cssText = ` display: flex; justify-content: center; align-items: center; margin-bottom: 25px; `; const startDateTimeInput = document.createElement('input'); startDateTimeInput.type = 'datetime-local'; startDateTimeInput.style.cssText = ` padding: 10px; margin: 0 10px; border: 1px solid #bdc3c7; border-radius: 5px; font-size: 14px; `; const endDateTimeInput = document.createElement('input'); endDateTimeInput.type = 'datetime-local'; endDateTimeInput.style.cssText = startDateTimeInput.style.cssText; dateContainer.appendChild(createLabel('开始时间:')); dateContainer.appendChild(startDateTimeInput); dateContainer.appendChild(createLabel('结束时间:')); dateContainer.appendChild(endDateTimeInput); const inputContainer = document.createElement('div'); inputContainer.style.cssText = ` display: flex; justify-content: center; align-items: center; margin-bottom: 25px; `; const input = document.createElement('input'); input.type = 'number'; input.min = '1'; input.placeholder = '抽取数量'; input.style.cssText = ` padding: 10px; margin-right: 15px; border: 1px solid #bdc3c7; border-radius: 5px; font-size: 14px; width: 120px; marginBottom: '0 !important' `; const button = document.createElement('button'); button.textContent = '开始抽奖'; button.style.cssText = ` padding: 10px 20px; background-color: #3498db; color: white; border: none; border-radius: 5px; font-size: 16px; cursor: pointer; transition: background-color 0.3s; `; button.onmouseover = () => button.style.backgroundColor = '#2980b9'; button.onmouseout = () => button.style.backgroundColor = '#3498db'; inputContainer.appendChild(input); inputContainer.appendChild(button); const result = document.createElement('div'); container.appendChild(title); container.appendChild(dateContainer); container.appendChild(inputContainer); container.appendChild(result); return { container, input, button, result, startDateTimeInput, endDateTimeInput }; } function createLabel(text) { const label = document.createElement('label'); label.textContent = text; label.style.cssText = ` font-size: 14px; color: #34495e; margin-right: 5px; `; return label; } // 格式化日期 function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleString('zh-CN', { year: 'numeric', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } // 获取候选人列表 async function getCandidateList(startDateTime, endDateTime) { const topicId = window.location.pathname.split('/')[3]; let candidateList = []; let nameList = new Set(); const start = startDateTime ? new Date(startDateTime) : null; const end = endDateTime ? new Date(endDateTime) : null; for (let i = 1; i < 1000; i++) { const response = await fetch(`/t/${topicId}.json?page=${i}`); if (response.status === 404) break; const result = await response.json(); const posts = result.post_stream.posts; const topicOwner = result.details.created_by.username; for (let post of posts) { const postDate = new Date(post.created_at); if ((start && postDate < start) || (end && postDate > end)) continue; const onlyName = post.username; if (!nameList.has(onlyName) && onlyName !== topicOwner) { const candidate = { only_name: onlyName, display_name: post.display_username, post_number: post.post_number, created_at: post.created_at, avatar: post.avatar_template.replace('{size}', '90') }; candidateList.push(candidate); nameList.add(onlyName); } } } return candidateList; } // 执行抽奖 async function performLottery(count, startDateTime, endDateTime) { const candidates = await getCandidateList(startDateTime, endDateTime); if (candidates.length === 0) { return { error: '在选定的时间范围内没有找到任何候选人。' }; } if (count > candidates.length) { return { error: `抽奖人数不能多于唯一发帖人数。当前只有 ${candidates.length} 个符合条件的候选人。` }; } const chosenPosts = []; const winners = new Set(); while (winners.size < count && candidates.length > 0) { const randomIndex = Math.floor(Math.random() * candidates.length); const winner = candidates.splice(randomIndex, 1)[0]; if (!winners.has(winner.only_name)) { winners.add(winner.only_name); chosenPosts.push(winner); } } return { winners: chosenPosts }; } // 显示抽奖结果 function displayResults(results) { uiElements.result.innerHTML = '<h3 style="color: #2c3e50; margin-bottom: 20px;">🏆 抽奖结果</h3>'; const table = document.createElement('table'); table.style.cssText = ` width: 100%; border-collapse: separate; border-spacing: 0; border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); `; const headerRow = table.insertRow(); ['序号', '头像', '用户名', '楼层', '参与时间'].forEach(text => { const th = document.createElement('th'); th.textContent = text; th.style.cssText = ` padding: 15px; background-color: #f2f2f2; color: #333; font-weight: bold; text-align: left; border-bottom: 2px solid #ddd; `; headerRow.appendChild(th); }); results.forEach((result, index) => { const row = table.insertRow(); row.style.backgroundColor = index % 2 === 0 ? '#ffffff' : '#f9f9f9'; const cellIndex = row.insertCell(); cellIndex.textContent = index + 1; cellIndex.style.cssText = ` padding: 12px 15px; text-align: center; font-weight: bold; color: #3498db; `; const cellAvatar = row.insertCell(); const avatar = document.createElement('img'); avatar.src = result.avatar.startsWith('http') ? result.avatar : `https://linux.do${result.avatar}`; avatar.style.cssText = ` width: 40px; height: 40px; border-radius: 50%; display: block; margin: 0 auto; border: 2px solid #3498db; `; cellAvatar.appendChild(avatar); cellAvatar.style.padding = '12px 15px'; const cellUsername = row.insertCell(); const userLink = document.createElement('a'); userLink.href = `https://linux.do/u/${encodeURIComponent(result.only_name)}/summary`; userLink.textContent = `@${result.only_name}`; userLink.target = '_blank'; userLink.style.cssText = ` text-decoration: none; color: #3498db; font-weight: bold; transition: color 0.3s; `; userLink.onmouseover = () => userLink.style.color = '#2980b9'; userLink.onmouseout = () => userLink.style.color = '#3498db'; cellUsername.appendChild(userLink); cellUsername.style.cssText = ` padding: 12px 15px; text-align: left; `; const cellNumber = row.insertCell(); cellNumber.textContent = `#${result.post_number}`; cellNumber.style.cssText = ` padding: 12px 15px; text-align: center; color: #7f8c8d; `; const cellTime = row.insertCell(); cellTime.textContent = formatDate(result.created_at); cellTime.style.cssText = ` padding: 12px 15px; text-align: center; color: #7f8c8d; `; }); uiElements.result.appendChild(table); } // 主函数 function main() { uiElements = createUI(); // 插入UI到指定位置 const targetElement = document.querySelector('#post_1 > div.row'); if (targetElement) { targetElement.parentNode.insertBefore(uiElements.container, targetElement.nextSibling); // 强制移除目标元素的 marginBottom function removeMarginBottom() { targetElement.style.setProperty('margin-bottom', '0', 'important'); const computedStyle = window.getComputedStyle(targetElement); if (computedStyle.getPropertyValue('margin-bottom') !== '0px') { targetElement.style.setProperty('margin-bottom', '-9px', 'important'); } } removeMarginBottom(); const observer = new MutationObserver(removeMarginBottom); observer.observe(targetElement, { attributes: true, attributeFilter: ['style'] }); setInterval(removeMarginBottom, 100); } else { console.error('无法找到目标插入位置'); return; } uiElements.button.addEventListener('click', async () => { const count = parseInt(uiElements.input.value); if (isNaN(count) || count < 1) { uiElements.result.innerHTML = '<p style="color: #e74c3c; font-weight: bold;">请输入有效的抽取数量。</p>'; return; } const startDateTime = uiElements.startDateTimeInput.value ? new Date(uiElements.startDateTimeInput.value) : null; const endDateTime = uiElements.endDateTimeInput.value ? new Date(uiElements.endDateTimeInput.value) : null; if (startDateTime && endDateTime && startDateTime > endDateTime) { uiElements.result.innerHTML = '<p style="color: #e74c3c; font-weight: bold;">开始时间不能晚于结束时间。</p>'; return; } uiElements.button.disabled = true; uiElements.button.textContent = '抽奖中...'; uiElements.button.style.backgroundColor = '#bdc3c7'; uiElements.result.innerHTML = '<p style="color: #3498db; font-weight: bold;">正在抽奖,请稍候...</p>'; const lotteryResults = await performLottery(count, startDateTime, endDateTime); if (lotteryResults.error) { uiElements.result.innerHTML = `<p style="color: #e74c3c; font-weight: bold;">${lotteryResults.error}</p>`; } else { displayResults(lotteryResults.winners); } uiElements.button.disabled = false; uiElements.button.textContent = '开始抽奖'; uiElements.button.style.backgroundColor = '#3498db'; }); } // 运行主函数 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址