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 = `https://www.v2ex.com/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 = `https://www.v2ex.com/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
![]( https://i.v2ex.co/VlpwI4y5.jpeg)
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://v2ex.240801.xyz/?tid=12345&num=10&seed=12345&maxFloor=11(大佬别打,刚装上访问不了可能 cf 路由没设置好)这里的参数说明为 tid 为帖子 id num 为中奖人数 seed 为随机数种子 maxFloor 为最高楼层
ps 甜度不用担心,今年我们县的都甜。现摘现发没有清洗打腊,只要老家不下雨一般下单第二天就发货。室温 20 度以内还是很耐放的两三个星期没啥问题。有个卖橙子的微信群想加的加我微信 console.log(atob('enF3MjAwOA=='))拉群
为啥这次的代码块没有高亮。。。代码看起来多,是因为ai 写的取回复用户数没用v站的api,下面是去除相关爬虫代码后的代码:https://gist.github.com/zzerding/d80d25149a74c0dd96516d949e9e52b7
好了不摸鱼了领导催进度了
301
cs5791393 15 小时 31 分钟前
重在参与!
|
302
zhaojiejoe 15 小时 30 分钟前
分子
|
303
xujunjie 15 小时 26 分钟前
支持
|
304
570956418 15 小时 24 分钟前
参与一下
|
305
cjlsky 15 小时 21 分钟前
回购的第二年
|
306
robot2134 15 小时 21 分钟前 via Android
+1
|
307
defunct9 15 小时 17 分钟前
分子
|
308
dropdatabase 15 小时 14 分钟前
1
|
309
s609926202 15 小时 7 分钟前
+1
|
310
JerryV2 15 小时 6 分钟前
+1
|
311
xier9213 15 小时 3 分钟前
分母+1
|
312
ecee 15 小时 3 分钟前
+1
|
313
bomjack 15 小时 2 分钟前
重在参与
|
314
kzmdididi 15 小时 2 分钟前
大麦
|
315
vimfaith 14 小时 58 分钟前
重在参与!
|
316
PeterPig 14 小时 58 分钟前 via iPhone
+1
|
317
vimfaith 14 小时 58 分钟前
重在参与
|
318
nellace 14 小时 57 分钟前
重在参与!
|
319
cqq626432432 14 小时 56 分钟前
重在参与!
|
320
ma836323493 14 小时 55 分钟前
做个分子
|
321
jaylengao 14 小时 54 分钟前
重在参与
|
322
wxd21020 14 小时 53 分钟前
支持
|
323
hnbcinfo 14 小时 52 分钟前
大家好,我是分母。重在掺和,拉低各位中奖率就是我的目标
|
324
myxvhgiile 14 小时 51 分钟前
参与一下
|
325
wangofjian 14 小时 51 分钟前
凑个分子
|
326
ip7x12v5 14 小时 48 分钟前
试试
|
327
seeyourface 14 小时 47 分钟前
参与一下
|
328
qwas 14 小时 46 分钟前
橙不橙子不重要,看到队伍占个位
|
329
meteor957 14 小时 46 分钟前
分母+1
|
330
markchen88 14 小时 46 分钟前
参与
|
331
raydied 14 小时 45 分钟前
分子分母都+1
|
332
paullige 14 小时 44 分钟前
感谢
|
333
keaidian 14 小时 43 分钟前
幸运之子!
|
334
lilicheng 14 小时 43 分钟前
那就让我尝尝吧
|
335
jykid 14 小时 42 分钟前
那让我来试试
|
336
SkYouth 14 小时 41 分钟前
重在参与。
|
337
sdsd995 14 小时 40 分钟前
支持一下,参与试试
|
338
rawburuser 14 小时 39 分钟前
重在参与
|
339
aswqazx 14 小时 37 分钟前
+1
|
340
alanoy 14 小时 36 分钟前
重在参与!
|
341
wanwan 14 小时 34 分钟前 via Android
看看运气
|
342
Mianmiss 14 小时 34 分钟前
+1 重在参与
|
343
Mianmiss 14 小时 34 分钟前
参与下
|
344
luo123qiu 14 小时 33 分钟前
分母+1
|
345
iamperiod331www 14 小时 31 分钟前
参与一下
|
346
lin98 14 小时 31 分钟前
试试
|
347
fantasy55 14 小时 30 分钟前
分子++
|
348
kissing 14 小时 29 分钟前
支持一下!
|
349
a33291 14 小时 29 分钟前
祝 op 大卖
|
350
gongxuanzhang 14 小时 27 分钟前
今年运气还可以
|
351
walkeronway 14 小时 26 分钟前
支持支持
|
352
boom520 14 小时 26 分钟前
上次买过,味道 ok 的
|
353
xing666 14 小时 22 分钟前
重在参与
|
354
Incarna 14 小时 22 分钟前
支持,要是中了的话觉得好吃给家人买点
|
355
wlliam19 14 小时 19 分钟前
想吃
|
356
clieujv 14 小时 18 分钟前
我相信我能中
|
357
wujinglun 14 小时 16 分钟前
参与一下
|
358
sumarker 14 小时 15 分钟前
分子
|
359
jark006 14 小时 14 分钟前
分母来了
|
360
pegasusz 14 小时 10 分钟前
反正中不了,拉低下中奖率吧
|
361
bluelotus0739 14 小时 9 分钟前
赣南脐橙真的很好吃!!!
|
362
Jieyue 14 小时 9 分钟前
分子
|
363
njl12138 14 小时 7 分钟前
好家伙,第一次抽奖,我来试试
|
364
qwereee 14 小时 7 分钟前
+1
|
365
qingshengwen 14 小时 6 分钟前
分子+1
|
366
BelovedOne 14 小时 5 分钟前
重在参与!
|
367
ae86 14 小时 5 分钟前
分母+1
|
368
mrzhoudjq 14 小时 3 分钟前
参与一下
|
369
yxb9511 14 小时 1 分钟前
冲冲冲,每年都吃赣南脐橙
|
370
purewhite0 13 小时 59 分钟前
参与一下
|
371
mxT52CRuqR6o5 13 小时 59 分钟前
分母
|
372
waising 13 小时 56 分钟前
分母
|
373
lipanzjzs1 13 小时 51 分钟前
分母来啦
|
374
AmosWu 13 小时 51 分钟前
+1
|
375
veyron888 13 小时 51 分钟前
预祝大麦
|
376
hallostr 13 小时 49 分钟前
重在参与,爱吃脐橙
|
377
lddtech 13 小时 48 分钟前
分子
|
378
jy02919061 13 小时 47 分钟前 via Android
来了啦啦啦啦啦了
|
379
ZX16815 13 小时 47 分钟前
分子
|
380
cubicoo 13 小时 47 分钟前 via Android
参与一下
|
381
CaptainAmerica 13 小时 45 分钟前
支持一下
|
382
hahasong 13 小时 40 分钟前
我来看看能不能吃上
|
383
Rekkles 13 小时 39 分钟前
分子分母
|
384
wulv 13 小时 39 分钟前
参与一下
|
385
richamon 13 小时 38 分钟前
参与一下
|
386
vaynecv 13 小时 34 分钟前
分子
|
387
NilError 13 小时 33 分钟前
分母加一
|
388
ddonano 13 小时 33 分钟前
参与一下
|
389
yuxi521 13 小时 32 分钟前
1111
|
390
liuxiaojiu 13 小时 30 分钟前
试一下
|
391
zzerd OP @Feedmo https://gist.github.com/zzerding/d80d25149a74c0dd96516d949e9e52b7 看这兄弟,这有注释。晚上我有空用 V2EX api 重写 getAllUser
|
392
yveJohn 13 小时 30 分钟前
重在参与
|
393
lvxing888 13 小时 28 分钟前
支持一下
|
394
luistrong 13 小时 26 分钟前
分子
|
395
voya 13 小时 26 分钟前
生意兴隆
|
396
Yafun 13 小时 25 分钟前
重在参与!!
|
397
zhijietalk 13 小时 22 分钟前
试试
|
398
zgsi 13 小时 22 分钟前
代码一言难尽
|
399
norcm 13 小时 21 分钟前
分子!~
|
400
allendavis 13 小时 15 分钟前
拉低概率?
|