Sharkey/Misskey抽選ツール
基于ChatGPT的Sharkey/Misskey抽奖脚本。
仅使用所在实例测试能跑通,反正只是我自己用的(你)过低版本Misskey应该用不了。
思路仿照Mastodon抽奖脚本,先拉取通知,填入相关帖子链接,选取抽奖条件,输入结果。
抽奖方式有四种。
- 仅转发
- 仅表情回应(Mastodon端为点赞)
- 1+2
- 1或2
使用方法:打开域名/my/notifications
页面,确保界面语言为中文,按F12-Console-粘贴脚本,根据提示信息填入,最后结果会自动复制到剪贴板上。
也放一下to-do-list看我有没有空继续做……
(async () => {
// **滚动加载所有通知**
async function scrollToLoadMoreNotifications() {
const timeout = Date.now() + 10000; // 最长等待 10 秒
let lastHeight = document.body.scrollHeight;
while (Date.now() < timeout) {
window.scrollTo(0, document.body.scrollHeight);
await new Promise(resolve => setTimeout(resolve, 1000));
if (document.body.scrollHeight === lastHeight) break;
lastHeight = document.body.scrollHeight;
}
if (Date.now() >= timeout) {
alert("⏳ 加载通知超时,可能未加载完所有通知。");
}
}
// **等待通知加载**
async function waitForNotifications() {
const timeout = Date.now() + 10000;
while (document.querySelectorAll('article').length === 0 && Date.now() < timeout) {
await new Promise(resolve => setTimeout(resolve, 500));
}
if (Date.now() >= timeout) {
alert("⏳ 无法加载通知,可能没有新通知。");
} else {
console.log("通知加载完成!");
}
}
// **确保在 Misskey 通知页面**
if (!window.location.pathname.startsWith("/my/notifications")) {
alert("❌ 请先打开 Misskey 的通知页面(/my/notifications)再运行此脚本!");
return;
}
await scrollToLoadMoreNotifications();
await waitForNotifications();
// **获取帖子 ID**
function validatePostUrl(url) {
if (!url || !url.includes("/notes/")) {
alert("❌ 请输入正确的帖子链接!");
return null;
}
return url.split("/notes/").pop().split("?")[0];
}
const postId = validatePostUrl(prompt("请输入帖子链接(如 https://misskey.io/notes/xxxxx):"));
if (!postId) return;
console.log("指定帖子 ID:", postId);
// **获取抽奖人数**
function validateDrawCount(count) {
return count >= 1 ? count : null;
}
const drawCount = validateDrawCount(parseInt(prompt("请输入中奖人数(默认 1):") || "1"));
if (!drawCount) {
alert("❌ 中奖人数必须至少为 1!");
return;
}
// **选择筛选方式**
const filterType = prompt(
"请输入筛选方式:\n" +
"1️⃣ 仅抽转发(Renote)用户\n" +
"2️⃣ 仅抽回应(Reaction)用户\n" +
"3️⃣ 转发 + 回应的用户\n" +
"4️⃣ 转发或回应的用户\n" +
"(输入 1、2、3 或 4,默认 1)"
) || "1";
// **获取互动用户**
function getUsersByType(type) {
const users = new Set();
document.querySelectorAll('.MkNotification-root-9Bba').forEach(notification => {
const text = notification.innerText;
const noteLink = notification.querySelector(`a[href*="/notes/${postId}"]`);
if (!noteLink) return;
notification.querySelectorAll('a[href^="/@"]').forEach(userLink => {
const username = userLink.getAttribute("title")?.split('@')[0];
if (!username) return;
if (type === "renote" && /转发了|Renote/.test(text)) users.add(username);
if (type === "reaction" && /回应了|Reaction/.test(text)) users.add(username);
});
notification.querySelectorAll('.MkNotification-reactionsItem-ENB4 a[title]').forEach(item => {
users.add(item.getAttribute("title")?.split('@')[0]);
});
});
console.log(type === "renote" ? "转发用户:" : "回应用户:", users);
return [...users];
}
const renoteUsers = getUsersByType("renote");
const reactionUsers = getUsersByType("reaction");
// **筛选符合条件的用户**
let eligibleUsers = [];
if (filterType === "1") {
eligibleUsers = renoteUsers;
} else if (filterType === "2") {
eligibleUsers = reactionUsers;
} else if (filterType === "3") {
eligibleUsers = renoteUsers.filter(user => reactionUsers.includes(user));
} else {
eligibleUsers = [...new Set([...renoteUsers, ...reactionUsers])];
}
if (eligibleUsers.length === 0) {
alert("❌ 没有符合条件的用户!");
return;
}
console.log("符合条件的用户:", eligibleUsers);
// **随机抽取中奖者**
function pickWinners(users, count) {
const shuffled = [...users];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled.slice(0, count);
}
const winners = pickWinners(eligibleUsers, drawCount);
if (winners.length === 0) return;
// **生成中奖公告**
const winnerText = `🎉 抽奖结果 🎉\n\n` +
`感谢大家的参与,本次中奖者是:\n@${winners.join("\n@")}` +
`\n\n请私信领奖!🎁 #Misskey抽奖`;
alert(winnerText);
// **自动复制到剪贴板**
try {
await navigator.clipboard.writeText(winnerText);
alert("✅ 中奖信息已复制!");
} catch {
alert("❌ 复制失败,请手动复制!");
}
})();