Skip to content

Commit

Permalink
2.0.0 (#26)
Browse files Browse the repository at this point in the history
* feat: 新增puppeteer 解决计算加密参数问题

* chore: 新增提示 & README修改

* fix: 处理秒杀重定向
  • Loading branch information
meooxx authored Sep 13, 2021
1 parent f446d2c commit 592feda
Show file tree
Hide file tree
Showing 12 changed files with 2,036 additions and 1,577 deletions.
43 changes: 22 additions & 21 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true,
},
extends: ['airbnb-base'],
parserOptions: {
ecmaVersion: 12,
},
rules: {
'no-shadow': 0,
'no-nested-ternary': 0,
eqeqeq: 0,
'no-await-in-loop': 0,
'comma-dangle': 0,
'no-use-before-define': 0,
'arrow-parens': 0,
'no-plusplus': 0,
'no-console': 0,
},
plugins: ['prettier'],
env: {
browser: true,
commonjs: true,
es2021: true
},
extends: ['airbnb-base'],
parserOptions: {
ecmaVersion: 12
},
rules: {
'no-shadow': 0,
'no-nested-ternary': 0,
eqeqeq: 0,
'no-await-in-loop': 0,
'comma-dangle': 0,
'no-use-before-define': 0,
'arrow-parens': 0,
'no-plusplus': 0,
'no-console': 0,
'no-tabs': 0,
indent: ['error', 'tab', 2]
}
};
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ qrcode.png
.vscode
config.go.json
test.js
coverage
coverage
screenshot*
3 changes: 2 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
registry=https://registry.npm.taobao.org/
registry=https://registry.npm.taobao.org/
PUPPETEER_DOWNLOAD_HOST=https://storage.googleapis.com.cnpmjs.org
86 changes: 44 additions & 42 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# 买 jd 预约抢购商品(原 jd_by_mask)

### 此代码受 https://github.com/tychxn/jd-assistan 启发完成
TG Group

[来吧加入电报组😊](https://t.me/joinchat/I5uwLB2vr6pruLYG)
TG Group

Issues帮助
[来吧加入电报组 😊](https://t.me/joinchat/I5uwLB2vr6pruLYG)

Issues 帮助
https://github.com/xxxXXX95/yuyue/issues/8

Master

https://github.com/xxxXXX95/yuyue/tree/master


## 支持预约-抢购-提交订单流程的商品

狗东预约抢购模式下又设置了如下俩种抢购流程, 现在程序内部实现了自动区分俩种流程。区分依据参照下文`代码分析`处贴的俩个 js 文件

1. 到时间直接抢购, 也叫秒杀(例如以前的口罩, 类似茅台(茅台现在限定App中抢购了😄))
1. 到时间直接抢购, 也叫秒杀(例如以前的口罩, 类似茅台(茅台现在限定 App 中抢购了 😄))
2. 到时间先添加购物车 -> 到购物车提交订单(普通显卡预约抢购类)
3. 原价到时间变价

Expand All @@ -29,11 +29,11 @@ https://github.com/xxxXXX95/yuyue/tree/master
4. 因为狗东有红包的话, 会自动勾选使用红包.为了抢购成功和快速, 配置文件中`最好填写 6 位支付密码`(保存密码的`config.js` **`不会从你本地上传到任何地方,请放心!!`**)
5. 已知 `windows` 系统自带终端打印出来的二维码错位, 请更换终端或者手动打开自动生成在本目录下`qrcode.png`文件, 进行扫码
6.`jd_by_mask` 改名 `jd_yuyue`.之前使用错别字防止搜索且买口罩也不符合现在仓库内容, 所以改名 `jd_yuyue`
7.`jd_yuyue` 改名 `yuyue`, 被x东邮件通知下架. 避免出问题, 规避搜索
7.`jd_yuyue` 改名 `yuyue`, 被 x 东邮件通知下架. 避免出问题, 规避搜索

TG Group
TG Group

[来吧加入电报组😊](https://t.me/joinchat/I5uwLB2vr6pruLYG)
[来吧加入电报组 😊](https://t.me/joinchat/I5uwLB2vr6pruLYG)

## Quick Start

Expand All @@ -42,28 +42,28 @@ TG Group
```js
// 最简单只加 eid, areaId 和 fp
const config = {
// 支持自定义 UA, 非必填
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
// 是否微信公众号推送抢购结果, 非必填
messenger: false, // true, false
// key 值在 http://sc.ftqq.com/3.version, 查看申请使用的方法. 把key粘贴至这里, 微信关注网站中的公众号即可.
sckey: '', // string, messenger 是 true 则为必填, 不填写不影响抢购流程

// 必填,
// 最新:用浏览器(最好chrome)打开本地文件夹下
// `get_eid_fp.html`文件, 将网页中`eid`,`fp` 填入配置中
// 其他方式: 或者电脑打开 jd 登陆页面, devtools, 选择
// `Elements`, 搜索 eid 和 sessionId(即fp),
// 在搜索到的input元素上面 value 属性中的值复制过来
eid: '', // string, 必填
fp: '', // string, 必填
// 6位支付密码如 '123456' 最好填上.如果当前账号有红包之类的则必填
// 只用于使用红包, 优惠券等. 不用于支付订单
pwd: '',
// *现在必须设置areaId*
// areaId 获取在第5步骤说明
areaId: '',
// 支持自定义 UA, 非必填
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
// 是否微信公众号推送抢购结果, 非必填
messenger: false, // true, false
// key 值在 http://sc.ftqq.com/3.version, 查看申请使用的方法. 把key粘贴至这里, 微信关注网站中的公众号即可.
sckey: '', // string, messenger 是 true 则为必填, 不填写不影响抢购流程

// 必填,
// 最新:用浏览器(最好chrome)打开本地文件夹下
// `get_eid_fp.html`文件, 将网页中`eid`,`fp` 填入配置中
// 其他方式: 或者电脑打开 jd 登陆页面, devtools, 选择
// `Elements`, 搜索 eid 和 sessionId(即fp),
// 在搜索到的input元素上面 value 属性中的值复制过来
eid: '', // string, 必填
fp: '', // string, 必填
// 6位支付密码如 '123456' 最好填上.如果当前账号有红包之类的则必填
// 只用于使用红包, 优惠券等. 不用于支付订单
pwd: '',
// *现在必须设置areaId*
// areaId 获取在第5步骤说明
areaId: ''
};
module.exports = config;
```
Expand All @@ -72,7 +72,7 @@ module.exports = config;

3 在`本项目目录`(不是 nodejs 安装目录)下执行 `npm install --production` 或者 `yarn --prod`(不熟悉的, 就直接使用 npm 效果一样的) 安装依赖, 已经默认配置使用 `taobao` 镜像地址下载依赖包

4 配置`tasks-pool.js`(以前在*index.js* 中, 现在迁移出来了) 中设置日期 `date`, `forceKO`, `areadId`(如果已在 `config.js` 中设置了`areadId`, 这里无需再设置) 和要抢购的 `skuId`, 可选参数 新增 `submitTimes` 不填默认 10, `maxWaitingMS` 不填默认 800
4 配置`tasks-pool.js`(以前在*index.js* 中, 现在迁移出来了) 中设置日期 `date`, `forceKO`, `areadId`(如果已在 `config.js` 中设置了`areadId`, 这里无需再设置) 和要抢购的 `skuId`, 可选参数 新增 `submitTimes` 不填默认 10, `maxWaitingMS` 不填默认 800
格式如下

```js
Expand All @@ -96,14 +96,14 @@ const dd3 = new Date(2020, 2, 3, 21, 0, 0, 400).getTime();
// 需要更改 年/月/日 时:分:秒.毫秒
// skuId 获取方法, 打开任意一个商品详情页如 `https://item.jd.com/100011521400.html`, 则 `100011521400` 就是其skuId
exports.pool = [
// 1 *现在必须设置areaId*
// 如果上面 `config.js`中已经配置过了, 此处可以不用再配置`areaId`, areaId 获取在第 5 步骤说明
// 2 forceKO: true/false (废弃不用)
// 3 此脚本也可在非预约抢购流程中的秒杀(到时间变价那种)使用,为防止提交原价订单, 可配置提交订单重试次数 推荐 2-3 次(不明白的忽略)
// 4 如果抢购过程中出现 `请求结算页面超过xxx ms`, 请增大maxWaitingMS, 建议1000-2000之间, 不设置默认 800
{ skuId: '100011621642', date: dd1, areaId: `2_2825_51936` },
// 1 *现在必须设置areaId*
// 如果上面 `config.js`中已经配置过了, 此处可以不用再配置`areaId`, areaId 获取在第 5 步骤说明
// 2 forceKO: true/false (废弃不用)
// 3 此脚本也可在非预约抢购流程中的秒杀(到时间变价那种)使用,为防止提交原价订单, 可配置提交订单重试次数 推荐 2-3 次(不明白的忽略)
// 4 如果抢购过程中出现 `请求结算页面超过xxx ms`, 请增大maxWaitingMS, 建议1000-2000之间, 不设置默认 800
{ skuId: '100011621642', date: dd1, areaId: `2_2825_51936` }
// skuId: '10022991959725',areaId: `2_2825_51936`, date: dd1, submitTimes: 3, maxWaitingMS: 1000
// 不同时间的sku, 复制上述项修改值, 填写在下方
// 不同时间的sku, 复制上述项修改值, 填写在下方
];
// 设置要强制扫码登录(没搞懂使用场景的忽略此配置)
// 说明: 因为 x 东, 24小时就要重新登录, 防止运行时登录状态有效
Expand All @@ -117,7 +117,9 @@ exports.forceLogin = false;

6 本项目目录下执行 `node index`

7 扫描终端中的二维码登录, 24 小时之内重启不需要再次扫码登录, `cookie` 串会保留在本地文件 `cookie.json` 中. 过期的话必须重新扫码(代码自动校验)
7 可以扫描终端中的二维码登录, 24 小时之内重启不需要再次扫码登录, `cookie` 串会保留在本地文件 `cookie.json` 中. 过期的话必须重新扫码(代码自动校验), 或者你已经从别处获取到了`cookie`, 请至`cookie.json`(没有则新建一个)粘贴, 格式`"key1=value1;key2=value2;"`

8 新增`pupetter` windows 下面 280M, linux 或者 mac 下面 200M 不到

## Todo

Expand Down Expand Up @@ -156,9 +158,9 @@ exports.forceLogin = false;

- 订单<img src="https://user-images.githubusercontent.com/13815865/77068877-56728700-6a22-11ea-8102-925cc25a4b92.png" />

- 成功案例太多了, tg群很多人都抢到了, 尤其日用品, 如日用纸, 和0.01的水果
更多案例(☝️加群): [issues/2](https://github.com/meooxx/jd_by_mask/issues/2)
- 成功案例太多了, tg 群很多人都抢到了, 尤其日用品, 如日用纸, 和 0.01 的水果

更多案例(☝️ 加群): [issues/2](https://github.com/meooxx/jd_by_mask/issues/2)

## Advanced(废弃)

Expand Down
168 changes: 91 additions & 77 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,93 +5,107 @@ const dayjs = require('./dayjs.min.js');

const filePath = path.join(__dirname, 'config.js');
try {
fs.accessSync(filePath, fs.constants.F_OK);
fs.accessSync(filePath, fs.constants.F_OK);
} catch (e) {
console.log(e);
console.log('请按 readme中, 在本目录下新建 config.js 并填写内容');
process.exit();
console.log(e);
console.log('请按 readme中, 在本目录下新建 config.js 并填写内容');
process.exit();
}

const { login, submitOrderProcess } = require('./jobs');
const { login, submitOrderProcess, helper } = require('./jobs');
const { pool, forceLogin } = require('./tasks-pool');
const config = require('./config');

if (!config.eid || !config.fp) {
console.log('请在 config.js 按备注填写 `eid` 和 `fp`');
process.exit();
console.log('请在 config.js 按备注填写 `eid` 和 `fp`');
process.exit();
}

if (cluster.isWorker) {
const setupLoginWork = item => {
console.log('progress.worker:login', process.pid);
if (item.forceLogin) {
console.warn(
'已开启强制扫码登录, 如果接下里24小时内频繁重启, 重启最好关闭了'
);
}
return login(item.forceLogin);
};
const setupWork = item => {
const {
date,
skuId,
areaId = config.areaId,
forceKO = false,
...rest
} = item;
if (!areaId) {
console.log('no areaId!请确认填写了正确到areaId');
process.exit(1);
}
const expectedDate = new Date(item.date);
// 任务进程
console.log(
'process.worker:',
process.pid,
'时间:',
dayjs(expectedDate).format('YYYY-MM-DD HH:mm:ss.SSS'),
'sku',
item.skuId
);
// 补足 2_xxx_xxx -> 2_xxx_xxx_0
const area = areaId.split('_').length === 3 ? `${areaId}_0` : areaId;
submitOrderProcess(date, skuId, area, { forceKO, ...rest });
};
process.on('unhandledRejection', reason => {
console.log(reason);
});
cluster.worker.on('message', async job => {
if (job.type === 'login') {
await setupLoginWork(job);
cluster.worker.send({ doneWork: 'login' });
}
if (job.type === 'task') {
setupWork(job);
}
});
const setupLoginWork = item => {
if (item.forceLogin) {
console.warn(
'已开启强制扫码登录, 如果接下里24小时内频繁重启, 重启最好关闭了'
);
}
return login(item.forceLogin);
};
const setupWork = item => {
const {
date,
skuId,
areaId = config.areaId,
forceKO = false,
...rest
} = item;
if (!areaId) {
console.log('no areaId!请确认填写了正确到areaId');
process.exit(1);
}
const expectedDate = new Date(item.date);
// 任务进程
console.log(
'process.worker:',
process.pid,
'待抢购时间:',
dayjs(expectedDate).format('YYYY-MM-DD HH:mm:ss.SSS'),
'sku',
item.skuId
);
process.on('exit', () => {
process.send({
doneWork: 'cookie',
cookies: helper.reqTools.cookiejar.toJSON()
});
});
// 补足 2_xxx_xxx -> 2_xxx_xxx_0
const area = areaId.split('_').length === 3 ? `${areaId}_0` : areaId;
submitOrderProcess(date, skuId, area, { forceKO, ...rest });
};
process.on('unhandledRejection', reason => {
console.log(reason);
});
cluster.worker.on('message', async job => {
if (job.type === 'login') {
console.log('progress.worker:login', process.pid);
await setupLoginWork(job);
cluster.worker.send({ doneWork: 'login' });
console.log('登录完成');
process.exit();
}
if (job.type === 'task') {
setupWork(job);
}
});
} else {
// 使用独立进程登陆
// forcelogin, 强制登陆一次
cluster.fork().send({ type: 'login', forceLogin });
cluster.on('message', (_, message) => {
// 登陆流程
if (message.doneWork === 'login') {
// eslint-disable-next-line global-require
require('./codeInfo');
// 登陆完成后
for (let i = 0; i < pool.length; i++) {
const item = pool[i];
cluster.fork().send({ ...item, type: 'task' });
}
}
// fork 秒杀流程
if (message.doneWork === 'forkKO') {
for (let i = 0; i < message.items.length; i++) {
const item = message.items[i];
cluster.fork().send({ ...item, type: 'task', forceKO: true });
}
}
});
// 使用独立进程登陆
// forcelogin, 强制登陆一次
const cookies = [];
let isWritingFile = false;
cluster.fork().send({ type: 'login', forceLogin });
cluster.on('message', (_, message) => {
// 登陆流程
if (message.doneWork === 'login') {
// eslint-disable-next-line global-require
require('./codeInfo');
// 登陆完成后
for (let i = 0; i < pool.length; i++) {
const item = pool[i];
cluster.fork().send({ ...item, type: 'task' });
}
}
// fork 秒杀流程
if (message.doneWork === 'forkKO') {
for (let i = 0; i < message.items.length; i++) {
const item = message.items[i];
cluster.fork().send({ ...item, type: 'task', forceKO: true });
}
}
//
if (message.doneWork === 'cookie') {
helper.saveJson(message.cookies);
}
});

console.log('process.master:', process.pid);
console.log('process.master:', process.pid);
}
Loading

0 comments on commit 592feda

Please sign in to comment.