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
好了不摸鱼了领导催进度了
看到515楼我知道这个正则表达式取用户非常不可靠(但是当前代码好像没有注入成功),所以改用v2ex api。 使用方式
101
xianxiancr2 4 天前
分子+1
|
102
guogews 4 天前
争当分子
|
103
bcys 4 天前
重在参与
|
104
ashrkq 4 天前
重在参与
|
105
spemoon 4 天前
祝大卖
|
106
ropon 4 天前
中奖
|
107
coderth 4 天前
重在参与~
|
108
yunmeng 4 天前
重在参与
|
109
loren0912 4 天前
|
110
benzalus 4 天前
好奇,op 选邮政快递的原因是网点问题么
|
111
BALLACK 4 天前
支持
|
112
xianchen926 4 天前
江西人想吃老家的橙子了🍊
|
113
pigfloyd 4 天前
试试
|
114
alct 4 天前 via iPhone
支持一下支持一下
|
115
immit 4 天前
分母,试试
|
116
WANHOO 4 天前
重在参与!
|
117
4912941 4 天前
分子
|
118
jimdra 4 天前
牛啊
|
119
yangJunKing 4 天前
分子
|
120
GeekHao 4 天前
+1
|
121
Alison1113 4 天前
好吃吗?想问问,想买
|
122
Latin 4 天前
永远不中体
|
123
fengfisher3 4 天前
虽然每次都抽不中,所以,我在别的地方买了。
|
124
Maxwells8 4 天前
码选之人
|
125
MondaySmile 4 天前
分母
|
126
starlin 4 天前
重在参与
|
127
Jason0803 4 天前
分子
|
128
coderzhangsan 4 天前
支持
|
129
xhadmin 4 天前
重在参与!
|
130
liyinhe 4 天前
分母加一,重在参与!
|
131
supersadmin 4 天前
+1
|
132
sanshiliu 4 天前
做个分母
|
133
null00 4 天前
重在参与
|
134
duandage0812 4 天前
重在参与~
|
135
juzidong 4 天前
分子
|
136
ldyisbest 4 天前
支持
|
137
mingliao 4 天前
冲
|
138
ala2008 4 天前
前女友家也有,哭
|
139
a2519862329 4 天前
+1
|
140
kylinC 4 天前
+1
|
141
srddpzbx 4 天前
|
142
yoyo989899 4 天前
重在参与!
|
143
yngby 4 天前
重在参与
|
144
imch1n 4 天前
文字支持一下
|
145
Isaaccccccc 4 天前
支持一下
|
146
kunkunzhang 4 天前
你们都是分母,我是分子
|
147
c4dfan 4 天前
隔壁省的来凑个分母。。。。
|
148
shinelamla 4 天前
说不定中了呢!
|
149
nicholaswan 4 天前
参与
|
150
jrlee1204 4 天前
参与一个
|
151
incubus 4 天前
参与一下
|
152
ivanchou 4 天前
当分母 祝楼主大卖
|
153
uniqueFlynn 4 天前
已买,抽个玩玩
|
154
Danmen123 4 天前
试试
|
155
chenliangngng 4 天前
拉低概率
|
156
plusliang 4 天前
重在参与!
|
157
7i587i789 4 天前
进行一个分母的当
|
158
Petercao008 4 天前
分母+1
|
159
Aimirr 4 天前
分母+1
|
160
whqijy 4 天前
分子+1
|
161
Dizzy226 4 天前
试试运气
|
162
xavi818 4 天前
支持一下
|
163
forevam 4 天前
尝试中奖 重在参与!
|
164
ylrshui 4 天前
看看运气+1
|
165
wwwtarzan 4 天前
分子
|
166
yahaoo 4 天前
1
|
167
Evovil 4 天前
参与参与
|
168
ammeto 4 天前
支持一下
|
169
whythings 4 天前
支持一下
|
170
zhangsimon 4 天前
我是分子,预祝大卖!
|
171
Lilithia 4 天前
让我尝尝!
|
172
xyqy 4 天前
重在参与!支持一下
|
173
zerowxxyf 4 天前
参与
|
174
WayTooExplore 4 天前
参与
|
175
o0OoO0o 4 天前
分母
|
176
xiaopenyou 4 天前
支持一下
|
177
ABrother 4 天前
参与一下
|
178
Lexin914 4 天前
支持一下
|
179
youngzhaojia 4 天前
支持一下
|
180
willxiang 4 天前
重在参与!
|
181
qingchangaas 4 天前
支持一下
|
182
Moierby 4 天前
老板生意兴隆
|
183
nevermoreluo 4 天前
试试
|
184
zhangyq008 4 天前
重在参与
|
185
hack2012 4 天前
真的假的
|
186
t298 4 天前
我来组成头部
|
187
bananaplan 4 天前
我第一回评论吧
|
188
Picmen 4 天前
我来组成分子
|
189
huyoa 4 天前
天命分子!!!
|
190
lvyouchn 4 天前
支持一下
|
191
suwia 4 天前
重在参与!
|
192
RLinux 4 天前
+1
|
193
sb 4 天前
测试中奖
|
194
niuxinghua 4 天前
参与下
|
195
ily433664 4 天前
已经买了同事家的脐橙,今年的品质挺不错了
|
196
dishuibaby 4 天前
参与快乐
|
197
nanbobo 4 天前
重在参与!
|
198
erlkingrui999 4 天前
分母来也
|
199
webertyan 4 天前
参与!
|
200
rabitzn 4 天前
这个不错 参与一下
|