稀土掘金技术社区

开源一个辅助签到的油猴脚本

Image
(💰金石瓜分计划强势上线,速戳上图了解详情🔍)

引言

作为一名掘金的忠实用户,我每天打开网站的第一件事就是签到。最近在写小册《油猴脚本实战指南》的收尾章节———如何「将油猴脚本打包成原生谷歌插件」时,想着既然都提到提到谷歌插件开发了,于是顺手把 「Chrome 插件的快速入门教程」整理到小册里。

Image

就是因为这几天持续的写作,居然连续忘了好几次掘金签到,难受!作为脚本开发者,这事儿不能忍,必须写一个掘金自动签到脚本干它!

脚本效果与使用方式

要使用这个脚本,必须先通过「谷歌商店」,下载油猴扩展插件。然后,就可以通过脚本地址安装插件并使用。

Image

脚本使用非常简单,当你打开任意网页且掘金没有签到时,它就会帮你自动打开掘金签到页:

Image

如果你没有执行签到操作,此时,打开任意页面会对你进行提示:

Image

如果你执行了签到,则会记录执行结果,今日不在提示:

Image

实现原理

技术栈

脚本的技术栈很简单,就是将一段签到脚本(javascript 代码)通过油猴注入到网页中执行逻辑。

「油猴」是一个谷歌扩展程序,你可以在 Chrome 应用商店中,搜索 “篡改猴” 进行安装。

Image

用户可以将自己编写的任何 javascript 脚本放在油猴中执行,从而实现网页加强的功能。此外,油猴还提供了一些 API,实现了突破浏览器限制的 JS 能力,比如「跨域请求」、「无视 CSP 限制、后台打开标签」等。

如果你对脚本开发感兴趣,不妨看看《油猴脚本实战指南》这本小册,目前 7 折,可冲。

脚本原理

脚本的原理其实非常简单:当用户打开任意网站后,脚本会先检查今天是否已经签到;如果未签到,就利用油猴提供的 GM_openInTab API 打开掘金的签到页面(且全局只保留一个签到页)。

const SIGN_URL = "https://juejin.cn/user/center/signin"; // 掘金签到页const storedDate = GM_getValue("signTime", "");if(storedDate !== new Date().toLocaleDateString()) {  GM_openInTab(SIGN_URL, { active: true, insert: true }); // 未签到则打开}

在掘金签到页,脚本会通过获取签到按钮的 DOM 元素,「监听用户点击」,当用户点击签到后,脚本立刻用 GM_setValue 记录当天已签到(不做任何自动点击)。

Image
const btn = document.querySelector(".code-calender button.btn");if (btn) {  btn.addEventListener("click", () => {    GM_setValue("signTime", new Date().toLocaleDateString()); // 记录今天  }, { once: true });}

为了避免重复打开,脚本使用 GM_getTabs / GM_saveTab 将签到页标记为单例;如果用户没在签到页点击签到,那么每次新开页面时只做「未签到提醒」,不重复拉起多个签到标签。

// 伪示例:标记/检测单例签到页const self = await GM.getTab();self.isSignPage = true; await GM.saveTab(self);// 其他页面发现已有 isSignPage 时,仅提示未签到,不再重复打开

最后,下一次用户访问其他网页时,脚本会先判断是否已经签过到,「已签则直接退出」、不打扰;「未签则保证有且仅有一个签到页」处于激活状态,引导用户手动完成签到。

完整代码

下面是脚本的完整代码,大家可以直接复制到油猴的脚本新建页面中使用。

Image
// ==UserScript==// @name         掘金签到辅助提醒助手// @namespace    http://tampermonkey.net/// @version      0.0.2// @description  未签到时仅打开签到页并保持单例;在签到页用户点击后更新签到日期;当天已签则不做任何事。// @author       石小石Orz// @match        *://*/*// @license      MIT// @grant        GM_getValue// @grant        GM_setValue// @grant        GM_getTab// @grant        GM_getTabs// @grant        GM_saveTab// @grant        GM_addElement// @grant        GM_openInTab// @run-at       document-end// @noframes// ==/UserScript==
(async function () {  "use strict";
  const SIGN_URL = "https://juejin.cn/user/center/signin";  const today = () => new Date().toLocaleDateString();  const storedDate = GM_getValue("signTime", ""); // 上次签到日(本地格式字符串)
  // —— 已签到:直接退出 —— //  if (storedDate === today()) return;
  // UI 提示  function toast(text = "你今天还未签到", duration = 3200) {    const el = GM_addElement(document.body, "div", {      textContent: text,      style: `        position: fixed; top: 20px; left: 50%;        transform: translateX(-50%);        background: #fff7e6; color: #d46b08;        padding: 10px 14px; font-size: 14px; border-radius: 8px;        box-shadow: 0 2px 10px rgba(0,0,0,.12); z-index: 99999;        opacity: 0; transition: opacity .25s ease, transform .25s ease;      `,    });    requestAnimationFrame(() => {      el.style.opacity = "1";      el.style.transform = "translateX(-50%) translateY(8px)";    });    setTimeout(() => {      el.style.opacity = "0";      el.style.transform = "translateX(-50%) translateY(0)";      setTimeout(() => el.remove(), 250);    }, duration);  }
  // 获取当前是否在签到页  const onSignPage = location.href.startsWith(SIGN_URL);
  // —— 在签到页内的逻辑 —— //  if (onSignPage) {    // 将本标签页标记为“激活的签到页”(单例)    const selfTab = await GM.getTab();    selfTab.isSignPage = true;    await GM.saveTab(selfTab);
    // 监听“签到”按钮点击:一旦点击,就认为用户执行了签到操作,更新日期    const bindWatcher = () => {      const btn = document.querySelector(".code-calender button.btn");      if (!btn) return false;
      const onClick = () => {        // 用户主动点击签到:记录今天为已签到        GM_setValue("signTime", today());        toast("签到已记录,今天不再提示");      };      btn.addEventListener("click", onClick, { once: true });      return true;    };
    // 挂载监听;若按钮稍后才渲染,使用 MutationObserver 兜底    if (!bindWatcher()) {      const mo = new MutationObserver(() => {        if (bindWatcher()) mo.disconnect();      });      mo.observe(document.documentElement, { childList: true, subtree: true });    }    return; // 签到页不再执行下面逻辑  }
  // —— 非签到页的逻辑(未签到) —— //  const [tab, tabs] = await Promise.all([GM.getTab(), GM.getTabs()]);  const allTabs = Object.values(tabs || {});  const signTab = allTabs.find((t) => t && t.isSignPage);
  if (signTab) {    // 签到页已存在:不重复打开,但每次新页面提醒未签到    toast("掘金还没签到,快去签到把~");  } else {    // 签到页不存在:自动打开,并将其视为激活的签到页(单例)    tab.isCoordinator = true; // 可选:标记协调者    await GM.saveTab(tab);    GM_openInTab(SIGN_URL, { active: true, insert: true, setParent: true });    toast("已为你打开掘金签到页");  }})();

上述代码中,使用了一些GM_setValue、GM_getTab、GM_addElement、GM_openInTab等一些油猴的 API 能力,其余代码和普通前端代码是没有任何差异的。

❝

这些 API 小册中有详细介绍,你也可以参考官方 API 文档学习。

❞

总结

这篇文章给大家带来了一个非常实用的油猴脚本——掘金自动签到,有需求的同学还不快用起来!JYM,快成为矿石大亨!

油猴脚本开发对前端同学来说非常容易入门,感兴趣的同学可以去学习哇!

AI编程资讯AI Coding专区指南:https://aicoding.juejin.cn/aicoding

图片
点击"阅读原文"了解详情~