// ==UserScript==
// @name 掘金抽奖
// @namespace http://tampermonkey.net/
// @version 1.2.5
// @description 掘金抽奖 签到 免费抽奖 5连抽 10连抽 可视化抽奖 petite-vue
// @author 无仙
// @match https://juejin.cn/*
// @icon https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web//static/favicons/favicon-32x32.png
// @require https://unpkg.com/petite-vue
// ==/UserScript==
(async function () {
'use strict';
const { createApp } = PetiteVue; // 不会吧不会吧,不会还有人不知道petite-vue吧
const root = document.createElement('div');
root.class = 'wx_draw_wrap';
root.innerHTML = `
<div v-show="!popup" class="wx_draw" @click="open">掘金抽奖</div>
<div v-if="popup" class="wx_popup">
<div class="wx_mask" @click="popup = false"></div>
<div class="wx_main">
<div class="wx_header">
<div>掘金抽奖</div>
<div class="wx_score">当前矿石:{{ score }}</div>
</div>
<div class="wx_body">
<div class="wx_options">
<div @click="check_in" v-if="check_status === -1 || check_status === false">签到</div>
<div @click="get_free" v-else>签到成功</div>
<div @click="draw(5)">5连抽</div>
<div @click="draw(10)">10连抽</div>
<div @click="draw(undefined)">梭哈抽奖</div>
</div>
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<thead>
<tr>
<th>奖品图片</th>
<th>奖品名称</th>
<th>中奖次数</th>
</tr>
</thead>
<tbody>
<tr v-for="item in award">
<td><img :src="item.lottery_image"/></td>
<td>{{ item.lottery_name }}</td>
<td>{{ item.times }}</td>
</tr>
</tbody>
</table>
<div class="wx_loading" v-if="loading">
<svg class="circular" viewBox="25 25 50 50">
<circle class="path" cx="50" cy="50" r="20" fill="none" />
</svg>
</div>
</div>
<div class="wx_footer">
<div class="wx_confirm wx_btn" @click="popup = false">关闭</div>
</div>
</div>
</div>
`;
// 查询奖品列表
const res = await fetch('https://api.juejin.cn/growth_api/v1/lottery_config/get', {
headers: {
cookie: document.cookie
},
method: 'GET',
credentials: 'include'
}).then((res) => res.json());
const award = (res.data && res.data.lottery ? res.data.lottery : []).map((item) => ({ ...item, times: 0 }));
const { free_count, point_cost } = res.data; // 剩余免费抽奖次数,单次抽奖消耗数
document.body.appendChild(root); // 插入DOM
// petite-vue init初始化
createApp({
award,
popup: false,
loading: false,
score: 0,
free_count,
check_status: -1,
async open() {
const res = await fetch('https://api.juejin.cn/growth_api/v1/get_cur_point', {
headers: {
cookie: document.cookie
},
method: 'GET',
credentials: 'include'
}).then((res) => res.json());
this.score = res.data; // 当前分数
this.popup = true;
(this.check_status === -1 || this.check_status === false) && this.get_status();
},
async draw(times, is_not_free = true) {
if (this.loading || times === 0) return;
// const is_not_free = !(this.free_count && times === 1);
if (is_not_free && this.score < point_cost * (times || 1)) return alert('分都不够想啥呢?');
let i = 0;
const drawFn = async () => {
if ((is_not_free && this.score < point_cost) || i === times) {
this.free_count = 0;
this.loading = false;
this.open();
console.log(`${times ? times + '连抽' : '梭哈'}结束!`);
return;
}
const result = await fetch('https://api.juejin.cn/growth_api/v1/lottery/draw', {
headers: {
cookie: document.cookie
},
method: 'POST',
credentials: 'include'
}).then((res) => res.json());
if (result.err_no !== 0) {
console.log(result.err_msg);
this.loading = false;
this.open();
return;
}
i++;
is_not_free && (this.score -= point_cost);
if (result.data.lottery_type === 1) this.score += 66;
const item = this.award.find((item) => item.lottery_id === result.data.lottery_id);
item.times++;
console.log(`抽到:${result.data.lottery_name}`);
drawFn();
};
console.log(`开始${times ? times + '连抽' : '梭哈'}!`);
this.loading = true;
this.award.forEach((item) => {
item.times = 0;
});
try {
drawFn();
} catch (error) {
this.loading = false;
console.error(error);
}
},
async check_in() {
if (this.check_status) {
this.get_free(); // 免费抽奖
return;
}
// 签到
const check_in = await fetch('https://api.juejin.cn/growth_api/v1/check_in', {
headers: {
cookie: document.cookie
},
method: 'POST',
credentials: 'include'
}).then((res) => res.json());
if (check_in.err_no !== 0) {
alert('签到失败!');
this.check_status = false;
return;
}
this.check_status = true;
this.score = check_in.data.sum_point;
this.get_free(); // 免费抽奖
},
async get_status() {
// 查询签到状态
const today_status = await fetch('https://api.juejin.cn/growth_api/v1/get_today_status', {
headers: {
cookie: document.cookie
},
method: 'GET',
credentials: 'include'
}).then((res) => res.json());
this.check_status = today_status.data;
},
async get_free() {
// 查询是否有免费抽奖次数
const res = await fetch('https://api.juejin.cn/growth_api/v1/lottery_config/get', {
headers: {
cookie: document.cookie
},
method: 'GET',
credentials: 'include'
}).then((res) => res.json());
this.free_count = res.data.free_count;
if (res.data.free_count) {
// 有免费抽奖次数
this.draw(res.data.free_count, false);
}
}
}).mount();
// 处理样式
const style = `
.wx_draw_wrap {
box-sizing: border-box;
position: fixed;
top: 50%;
left: 0px;
z-index: 888888;
margin-top: -20px;
}
.wx_draw {
box-sizing: border-box;
position: fixed;
top: 50%;
left: 0px;
z-index: 888888;
width: 40px;
height: 40px;
line-height: 16px;
font-size: 12px;
padding: 4px;
background-color: rgb(232, 243, 255);
border: 1px solid rgb(232, 243, 255);
color: rgb(30, 128, 255);
text-align: center;
overflow: hidden;
cursor: pointer;
}
.wx_popup {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 999999;
}
.wx_mask {
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
}
.wx_main {
--width: 460px;
position: absolute;
left: 50%;
top: 50%;
width: var(--width);
transform: translate(-50%, -50%);
background: #fff;
border-radius: 4px;
}
.wx_main .wx_header {
height: 40px;
line-height: 40px;
font-size: 16px;
padding: 0 16px;
border-bottom: 1px solid #999;
display: flex;
align-items: center;
justify-content: space-between;
color: #000;
font-weight: 400;
}
.wx_score {
font-size: 12px;
font-size: #00a100;
}
.wx_main .wx_body {
padding: 16px;
border-bottom: 1px solid #999;
position: relative;
}
.wx_main .wx_options {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.wx_main .wx_options div {
width: 80px;
text-align: center;
height: 24px;
line-height: 24px;
background-color: rgb(232, 243, 255);
border: 1px solid #c9d4e3;
color: rgb(30, 128, 255);
cursor: pointer;
border-radius: 2px;
}
.wx_main .wx_body p {
margin: 0 0 8px;
}
.wx_body table {
width: 100%;
text-align: center;
border-left: 1px solid #ccc;
border-top: 1px solid #ccc;
}
.wx_body table th,
.wx_body table td {
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
line-height: 20px;
}
.wx_body table th {
line-height: 28px;
}
.wx_main .wx_body img {
vertical-align: middle;
width: 40px;
height: 40px;
}
.wx_main .wx_footer {
padding: 12px 16px;
text-align: right;
}
.wx_btn {
display: inline-block;
width: 48px;
cursor: pointer;
text-align: center;
height: 20px;
line-height: 20px;
background-color: rgb(232, 243, 255);
border: 1px solid #c9d4e3;
color: rgb(30, 128, 255);
border-radius: 2px;
}
.wx_loading {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 9999999;
background: rgba(0,0,0,0.65);
}
.wx_loading .circular {
height: 42px;
width: 42px;
-webkit-animation: loading-rotate 2s linear infinite;
animation: loading-rotate 2s linear infinite;
position: absolute;
left: 50%;
top: 50%;
margin-top: -21px;
margin-left: -21px;
}
.wx_loading .path {
-webkit-animation: loading-dash 1.5s ease-in-out infinite;
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: #409eff;
stroke-linecap: round;
}
@keyframes loading-rotate {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
`;
const styleEl = document.createElement('style');
styleEl.textContent = style;
document.head.appendChild(styleEl);
})();