// ==UserScript==
// @name 知乎想法抽奖工具
// @namespace http://tampermonkey.net/
// @version 0.1.2
// @description 一个知乎想法抽奖工具,支持鼓掌、转发等
// @author HuanCheng65
// @match https://www.zhihu.com/pin/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_setClipboard
// @resource mduiCss https://unpkg.zhimg.com/[email protected]/dist/css/mdui.min.css
// ==/UserScript==
(function () {
"use strict";
const STATE_LOADING = 0;
const STATE_READY = 1;
const STATE_LOTTERY = 2;
const MODE_LIKE = "0";
const MODE_REPIN = "1";
const MODE_LIKE_AND_REPIN = "2";
const MODE_LIKE_OR_REPIN = "3";
const RESULT_NOT_ENOUGH_PEOPLE = 0
const RESULT_SUCCESS = 1
const lottery = {
id: location.pathname.split("/").pop(),
repins: [],
likes: [],
result: {
type: 0,
text: "",
users: []
},
_loadFailed_: false,
_loadingText_: null,
_state_: null,
get loadingText() {
return this._loadingText_;
},
set loadingText(val) {
log(val);
this._loadingText_ = val;
updatePanel();
},
get state() {
return this._state_;
},
set state(val) {
this._state_ = val;
updatePanel();
},
get loadFailed() {
return this._loadFailed_;
},
set loadFailed(val) {
this._loadFailed_ = val;
updatePanel();
}
};
var $;
var fetchedAction = false;
function getRandom(start, end, fixed = 0) {
let differ = end - start
let random = Math.random()
return (start + differ * random).toFixed(fixed)
}
function dateFormat(fmt, date) {
let ret;
let opt = {
"Y+": date.getFullYear().toString(), // 年
"m+": (date.getMonth() + 1).toString(), // 月
"d+": date.getDate().toString(), // 日
"H+": date.getHours().toString(), // 时
"M+": date.getMinutes().toString(), // 分
"S+": date.getSeconds().toString(), // 秒
// 有其他格式化字符需求可以继续添加,必须转化成字符串
};
for (let k in opt) {
ret = new RegExp("(" + k + ")").exec(fmt);
if (ret) {
fmt = fmt.replace(
ret[1],
ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
);
}
}
return fmt;
}
function requireScript(url, onload) {
var script = document.createElement("script");
script.src = url;
if (typeof onload == "function") {
script.onload = onload;
}
document.documentElement.appendChild(script);
}
function init() {
$ = mdui.$;
GM_addStyle(`
#lottery {
position: fixed;
top: 32px;
left: 32px;
z-index: 999;
}
#lotteryPanel {
display: none;
}
#lottery.showPanel #openLotteryBtn {
display: none;
}
#lottery.showPanel #lotteryPanel {
display: block;
}
#lotteryPanel .mdui-card-header-title,
#lotteryPanel .mdui-card-header-subtitle {
margin-left: 0px;
margin-right: 48px;
}
.card-header-inner {
float: left;
}
#closeLotteryBtn {
float: right;
position: relative;
right: 0px;
}
#lotteryResultList {
max-height: 500px;
overflow: auto;
}
`);
$(`
<div id="lottery">
<button class="mdui-btn mdui-btn-raised mdui-ripple mdui-color-indigo" id="openLotteryBtn">抽奖</button>
<div class="mdui-card" id="lotteryPanel">
<div class="mdui-card-header">
<div class="card-header-inner">
<div class="mdui-card-header-title">知乎想法抽奖工具</div>
<div class="mdui-card-header-subtitle">by 幻了个城fly</div>
</div>
<button class="mdui-btn mdui-btn-icon mdui-ripple" id="closeLotteryBtn">×</button>
</div>
<div class="mdui-card-content">
<div id="loading">
<div id="loadingText" class="mdui-m-b-1"></div>
<div class="mdui-progress" id="loadingProgressBar">
<div class="mdui-progress-indeterminate"></div>
</div>
<button class="mdui-m-t-1 mdui-btn mdui-btn-raised mdui-ripple mdui-btn-block" id="failedReloadBtn">重新加载</button>
</div>
<div id="lotterySettings">
<span id="lotteryInfo"></span>
<form>
<div class="mdui-container-fluid">
<div class="mdui-row-xs-1">
<div class="mdui-col mdui-textfield mdui-textfield-floating-label">
<label class="mdui-textfield-label">抽奖人数</label>
<input class="mdui-textfield-input" name="count" value="1" type="number" min="1"/>
</div>
</div>
<div class="mdui-row-xs-2">
<label class="mdui-radio mdui-col">
<input type="radio" name="action" value="0" checked/>
<i class="mdui-radio-icon"></i>
鼓掌
</label>
<label class="mdui-radio mdui-col">
<input type="radio" name="action" value="1"/>
<i class="mdui-radio-icon"></i>
转发
</label>
<label class="mdui-radio mdui-col">
<input type="radio" name="action" value="2"/>
<i class="mdui-radio-icon"></i>
鼓掌并转发
</label>
<label class="mdui-radio mdui-col">
<input type="radio" name="action" value="3"/>
<i class="mdui-radio-icon"></i>
鼓掌或转发
</label>
</div>
<div class="mdui-row-xs-1">
<label mdui-tooltip="{content: '受知乎限制,此处判断的是你当前登录的账号,而非想法发布者的账号', position: 'right'}" class="mdui-checkbox">
<input type="checkbox" name="needFollow"/>
<i class="mdui-checkbox-icon"></i>
需关注本人
</label>
</div>
</div>
</form>
<button class="mdui-m-t-1 mdui-btn mdui-btn-raised mdui-ripple mdui-btn-block" id="reloadBtn">重新加载</button>
<button class="mdui-m-t-1 mdui-btn mdui-btn-raised mdui-ripple mdui-color-indigo mdui-btn-block" id="startLotteryBtn">开始抽奖</button>
</div>
<div id="lotteryResult">
<div id="lotteryResultText"></div>
<div id="lotteryResultContent">
<div class="mdui-list" id="lotteryResultList"></div>
<button class="mdui-m-t-1 mdui-btn mdui-btn-raised mdui-ripple mdui-btn-block" id="copyResultBtn">复制结果</button>
</div>
<button class="mdui-m-t-1 mdui-btn mdui-btn-raised mdui-ripple mdui-color-indigo mdui-btn-block" id="restartLotteryBtn">再次抽奖</button>
</div>
</div>
</div>
</div>
`).appendTo(document.body);
$("#openLotteryBtn").on("click", function () {
$("#lottery").addClass("showPanel");
fetchPinAction();
});
$("#closeLotteryBtn").on("click", function () {
$("#lottery").removeClass("showPanel");
});
$("#startLotteryBtn").on("click", function () {
startLottery();
});
$("#restartLotteryBtn").on("click", function () {
fetchPinAction();
});
$("#reloadBtn").on("click", function () {
fetchPinAction(true);
});
$("#failedReloadBtn").on("click", function () {
fetchPinAction(true);
});
$("#copyResultBtn").on("click", function () {
let clipboardText = "";
lottery.result.users.forEach(element => {
clipboardText += `@${element.name}\n`;
});
GM_setClipboard(clipboardText, "text");
});
mdui.mutation();
}
function fetchPinAction(force = false) {
if (fetchedAction && !force) {
lottery.state = STATE_READY;
return;
}
lottery.loadFailed = false;
lottery.state = STATE_LOADING;
lottery.likes = [];
lottery.repins = [];
updatePanel();
requestActions(100, 0, () => {
fetchedAction = true;
lottery.state = STATE_READY;
});
}
function updatePanel() {
switch (lottery.state) {
case STATE_LOADING:
$("#loading").show();
$("#lotterySettings").hide();
$("#lotteryResult").hide();
$("#loadingText").html(lottery.loadingText);
if (lottery.loadFailed) {
$("#failedReloadBtn").show();
$("#loadingProgressBar").hide();
} else {
$("#failedReloadBtn").hide();
$("#loadingProgressBar").show();
}
break;
case STATE_READY:
$("#loading").hide();
$("#lotterySettings").show();
$("#lotteryResult").hide();
$("#lotteryInfo").text(`共 ${lottery.likes.length} 人鼓掌,${lottery.repins.length} 人转发`);
break;
case STATE_LOTTERY:
$("#loading").hide();
$("#lotterySettings").hide();
$("#lotteryResult").show();
$("#lotteryResultText").text(lottery.result.text);
switch (lottery.result.type) {
case RESULT_NOT_ENOUGH_PEOPLE:
$("#lotteryResultContent").hide();
break;
case RESULT_SUCCESS:
$("#lotteryResultContent").show();
$("#lotteryResultList").empty();
lottery.result.users.forEach(element => {
$("#lotteryResultList").append(`
<a class="mdui-list-item mdui-ripple" target="_blank" href="${element.url}">
<div class="mdui-list-item-avatar"><img src="${element.avatar_url}"/></div>
<div class="mdui-list-item-content">${element.name}</div>
</a>
`)
});
break;
}
break;
}
}
function log(text) {
console.log(`[${dateFormat("HH:MM:SS", new Date())}]${text}`);
}
function startLottery() {
if (lottery.state != STATE_READY) {
return;
}
lottery.state = STATE_LOTTERY;
let selfUrlToken = $(".AuthorInfo-head .UserLink-link").attr("href").split("zhihu.com/people/").pop();
let lotteryCount = parseInt($(`input[name="count"]`).val());
let lotteryUsers = [];
let lotteryMode = $(`input[name="action"]:checked`).val();
let needFollow = $(`input[name="needFollow"]`).is(":checked");
switch (lotteryMode) {
case MODE_LIKE:
lotteryUsers.push(...lottery.likes);
break;
case MODE_REPIN:
lotteryUsers.push(...lottery.repins);
break;
case MODE_LIKE_AND_REPIN: {
let likeUsers = lottery.likes.map(element => element.id);
lotteryUsers.push(...lottery.repins.filter(element => likeUsers.includes(element.id)));
break;
}
case MODE_LIKE_OR_REPIN: {
let repinsUsers = lottery.repins.map(element => element.id);
lotteryUsers.push(...lottery.repins);
lotteryUsers.push(...lottery.likes.filter(element => !repinsUsers.includes(element.id)));
break;
}
}
lotteryUsers = lotteryUsers.filter(element => element.url_token != selfUrlToken);
if (needFollow) {
lotteryUsers = lotteryUsers.filter(element => element.is_followed);
}
let userCount = lotteryUsers.length;
if (userCount > lotteryCount) {
let resultUsers = [];
let count = lotteryCount;
do {
resultUsers.push(...lotteryUsers.splice(getRandom(0, lotteryUsers.length - 1), 1));
count--
} while (count > 0)
lottery.result.type = RESULT_SUCCESS;
lottery.result.users = resultUsers;
lottery.result.text = `从 ${userCount} 个符合条件的用户中抽取了 ${lotteryCount} 名用户`;
} else {
lottery.result.type = RESULT_NOT_ENOUGH_PEOPLE;
if (lotteryUsers.length > 0) {
lottery.result.text = `符合条件的用户数(${userCount} 人)不足`;
} else {
lottery.result.text = "没有符合条件的用户";
}
}
updatePanel();
}
function requestActions(limit, offset, onFinish) {
lottery.loadingText = `正在加载第 ${offset / limit + 1} 页`
$.ajax({
method: "GET",
url: `https://www.zhihu.com/api/v4/pins/${lottery.id}/actions?limit=${limit}&offset=${offset}`,
dataType: "json",
error() {
lottery.loadingText = `加载第 ${offset / limit + 1} 页失败`;
lottery.loadFailed = true;
},
success(data) {
let response = data;
response.data.forEach((element) => {
switch (element.action_type) {
case "repin":
lottery.repins.push(element.member);
break;
case "like":
lottery.likes.push(element.member);
break;
}
});
lottery.loadingText = `加载第 ${offset / limit + 1} 页完成,共 ${response.data.length} 条记录`
if (response.data.length >= limit) {
setTimeout(() => {
requestActions(limit, offset + limit, onFinish);
}, 100);
} else {
lottery.loadingText = "加载完成"
if (typeof onFinish == "function") {
onFinish();
}
}
},
});
}
GM_addStyle(GM_getResourceText("mduiCss"));
requireScript(
"https://unpkg.zhimg.com/[email protected]/dist/js/mdui.min.js",
function () {
init();
}
);
})();