抽奖规则和去年一样取上证指数和最后指定日期最高楼。上一年的帖子/996196
今年取 2024-11-22 上证指数收盘价作为随机数种子,有效抽奖用户为 2024-11-22 帖子回复最高楼层前的所有所有人
去年的抽奖代码没有去重所有回复多个的用户中的概率高一点点,今年我用 ai 糊了一下。代码如下
const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'; async function handleRequest(tid, num, seedParam, maxFloor) { if (!tid || isNaN(num)) { console.error('请提供有效的帖子 ID 和抽奖人数'); return; } console.log(`Fetching OP for TID: ${tid}`); const op = await getOp(tid); console.log(`OP: ${op}`); console.log(`Fetching all users for TID: ${tid}`); const uniqueUserList = await getAllUser(tid, maxFloor); console.log(`Unique Users: ${uniqueUserList.join(', ')}`); const filteredUserList = uniqueUserList.filter(user => user !== op); console.log(`Filtered Users: ${filteredUserList.join(', ')}`); const userCount = filteredUserList.length; console.log(`User Count: ${userCount}`); const seed = seedParam !== null ? seedParam : userCount.toString(); console.log(`Seed: ${seed}`); console.log(`Max Floor: ${maxFloor}`); const combinedSeed = `${seed}-${maxFloor}`; console.log(`Combined Seed: ${combinedSeed}`); const shuffledList = shuffle(filteredUserList, combinedSeed); console.log(`Shuffled Users: ${shuffledList.join(', ')}`); const result = `符合条件的用户共有 ${filteredUserList.length} 人 抽奖结果: ${shuffledList.slice(0, num).join('\n')}`; console.log(result); } async function getOp(tid) { const url = `amp/t/${tid}`; console.log(`Fetching OP URL: ${url}`); const respOnse= await fetch(url, { headers: { 'User-Agent': UA } }); if (!response.ok) { console.log(`Failed to fetch OP: ${response.status} ${response.statusText}`); return null; } const text = await response.text(); const match = /<div class="topic_author">[\s\S]*?<amp-img[^>]+alt="([^"]+)"[\s\S]*?<\/div>/.exec(text); console.log(`OP Match: ${match ? match[1] : null}`); return match ? match[1].trim() : null; } async function getUserList(url, startIndex, endIndex) { console.log(`Fetching User List URL: ${url}`); const respOnse= await fetch(url, { headers: { 'User-Agent': UA } }); if (!response.ok) { console.log(`Failed to fetch User List: ${response.status} ${response.statusText}`); return []; } const text = await response.text(); const replyItemRegex = /<div class="reply_item">([\s\S]*?)<\/div>/g; let replyItemMatch; const users = []; let currentIndex = startIndex || 0; while ((replyItemMatch = replyItemRegex.exec(text)) !== null) { if (endIndex !== undefined && currentIndex >= endIndex) { break; } const replyItem = replyItemMatch[1]; const altRegex = /<amp-img[^>]+alt="([^"]+)"[^>]*>/g; const altMatch = altRegex.exec(replyItem); if (altMatch) { users.push(altMatch[1]); } currentIndex++; } console.log(`Matches: ${users.join(', ')}`); return users; } async function getAllPages(tid) { const url = `amp/t/${tid}`; console.log(`Fetching All Pages URL: ${url}`); const respOnse= await fetch(url, { headers: { 'User-Agent': UA } }); if (!response.ok) { console.log(`Failed to fetch All Pages: ${response.status} ${response.statusText}`); return []; } const text = await response.text(); const pageCountMatch = /\u5171 (\d+) \u9875/.exec(text); const pageCount = pageCountMatch ? parseInt(pageCountMatch[1], 10) : 1; console.log(`Page Count: ${pageCount}`); const pages = []; for (let i = 1; i <= pageCount; i++) { pages.push(`${url}/${i}`); } return pages; } async function getAllUser(tid, maxFloor) { const pages = await getAllPages(tid); console.log(`Pages: ${pages.join(', ')}`); let allUsers = []; let currentFloor = 0; for (let page of pages) { const startIndex = currentFloor; const endIndex = maxFloor !== undefined ? maxFloor : undefined; const users = await getUserList(page, startIndex, endIndex); allUsers = allUsers.concat(users); currentFloor += users.length; if (endIndex !== undefined && currentFloor >= endIndex) { break; } } const uniqueUsers = []; const seen = new Set(); for (const user of allUsers) { if (!seen.has(user)) { uniqueUsers.push(user); seen.add(user); } } console.log(`Unique Users: ${uniqueUsers.join(', ')}`); return uniqueUsers; } function shuffle(array, seed) { const rng = lcg(seed); console.log(`Shuffling with Seed: ${seed}`); for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(rng() * (i + 1)); console.log(`Shuffling: i=${i}, j=${j}`); if (array[i] !== undefined && array[j] !== undefined) { [array[i], array[j]] = [array[j], array[i]]; } else { console.log(`Error: array[i] or array[j] is undefined. i=${i}, j=${j}, array[i]=${array[i]}, array[j]=${array[j]}`); } } return array; } function lcg(seed) { let state = hashCode(seed); console.log(`LCG State: ${state}`); const a = 1664525; const c = 1013904223; const m = Math.pow(2, 32); return function() { state = (a * state + c) % m; console.log(`LCG Next: ${state / m}`); return state / m; } } function hashCode(str) { l  et hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash |= 0; } return Math.abs(hash); } // Example usage: handleRequest('123456', 3, seed123, 10); 这里的 '123456' 是帖子 ID ,3 是抽奖人数,'seed123' 是种子(可选),10 是最大楼层(可选)。 在 V2EX 网站里用浏览器 consele 运行就可以 为了方便 v 友进行类似抽奖我糊了一个 cloudflare work 来使用直接请求 https://2ex.240801.xyz/?tid=12345&num=10&seed=12345&maxFloor=11(大佬别打,刚装上访问不了可能 cf 路由没设置好)这里的参数说明为 tid 为帖子 id num 为中奖人数 seed 为随机数种子 maxFloor 为最高楼层
下面是橙子链接请大家按需购买

ps 甜度不用担心,今年我们县的都甜。现摘现发没有清洗打腊,只要老家不下雨一般下单第二天就发货。室温 20 度以内还是很耐放的两三个星期没啥问题。有个卖橙子的微信群想加的加我微信 console.log(atob('enF3MjAwOA=='))拉群


