前言

碰巧掘金新上签到流动,碰巧喝了咖啡睡不着,碰巧双休不必下班,所以写了个插件,不便工作日签到(顺道练练手)

先来看看成品

总的来看,插件须要实现以下几个指标:

  1. 检测以后用户登录态
  2. 判断用户明天是否已签到
  3. 发送签到申请
  4. 展现信息(用户信息,处分信息)

申请剖析

用户登录凭证

通过登录申请[POST] /passport/web/user/login能够看到,申请响应设置的Cookie中有好几个键值对,抉择一个一般的申请在postman上剖析,能够看到掘金通过Cookie中的sessionid作为用户登录凭证

签到相干的接口

在签到性能的申请中,跟这次要实现性能相干的接口有4个,别离是:

  1. 获取签到天数的汇总信息 [GET]/growth_api/v1/get_counts
  2. 获取以后的矿石数量 [GET]/growth_api/v1/get_cur_point
  3. 判断用户明天是否已签到 [GET]/growth_api/v1/get_today_status
  4. 用户签到 [POST]/growth_api/v1/check_in
这里大抵剖析一下性能对应的申请即可,具体传参以及返回值的含意能够通过浏览器控制台查看(F12

流程图

上面通过几个场景的时序图来论述分明插件的工作流程

未登录场景

未签到场景

已签到场景

搭建chrome插件开发工程

通过vue-web-extension实现疾速搭建chrome插件开发工程(Vue)

首先确保这两个曾经装置了

npm install -g @vue/clinpm install -g @vue/cli-init

而后通过vue-web-extension创立工程,我抉择vue-web-extension的版本是v1

vue init kocal/vue-web-extension#v1 juejin-auto-sign

按需抉择本人须要的性能(axios必选)

装置element ui(按需加载)

cd juejin-auto-sign && vue add element

因为element ui的配置会写在package.json文件中babel局部,跟工程原有的.babelrc配置文件重叠了,须要将package.json中对于babel局部的配置合并到.babelrc文件中

合并前

# .babelrc配置文件{  "plugins": [    "@babel/plugin-proposal-optional-chaining"  ],  "presets": [    ["@babel/preset-env", {      "useBuiltIns": "usage",      "corejs": 3,      "targets": {        // https://jamie.build/last-2-versions        "browsers": ["> 0.25%", "not ie 11", "not op_mini all"]      }    }]  ]}# package.json配置文件{  .......  "babel": {    "plugins": [      [        "component",        {          "libraryName": "element-ui",          "styleLibraryName": "theme-chalk"        }      ]    ]  }}

合并后,将package.jsonbabel局部删除,.babelrc配置文件如下

在工程根目录下执行yarn build,可能失常打包

yarn build

至此,工程曾经根本搭建实现了!能够正式投入开发

工程常用命令:

  • yarn build 构建插件,输入到dist目录下
  • yarn build-zip 依照插件名+版本号的模式,构建插件压缩包
  • yarn watch 构建插件,输入到dist目录下,如果产生改变,会即时刷新

要害代码

manifest.json配置文件

manifest.json文件中记录着插件的原信息,其中包含插件的根底信息(插件名称,版本号,ICON等),以及插件波及页面(popup,options,background等),还有插件须要向chrome申请的权限

{  // 插件名称  "name": "juejin-auto-sign",  // 插件形容  "description": "掘金签到助手",  // 插件版本号  "version": "1.0.0",  "manifest_version": 2,  "icons": {    "48": "icons/icon.png",    "128": "icons/icon.png"  },  ......  // [1] 申请掘金的cookie、网络申请的权限  "permissions": [    "cookies",    "*://*.juejin.cn/",    "webRequest",    "webRequestBlocking"  ]}

[1]处能够看到,插件须要申请网络权限 webRequestwebRequestBlocking,这两个权限是跟用户签到申请有关系的(POST申请),前面会具体介绍为什么须要这两个权限

popup页面

目前插件的性能实现都是在popup页面,所谓popup页面就是在浏览器插件栏处点击展现的页面

Google翻译插件来看,红色箭头指向的页面就是popup页面

chrome插件开发有分好几种页面以及脚本
页面有:popup,optional,background,插件上不同页面的展现地位是不同,用处也不同,目前只须要理解到popup页面即可
脚本有:background.js,content script等,不同的脚本申明周期也是不同的

上面展现签到助手插件popup页面的次要代码

<template>  <div class="sign-body">    <div class="sign-image">      <el-avatar size="large" :src="imageUrl"></el-avatar>    </div>    <div class="sign-text">{{ nickName }}</div>    <div class="sign-label">      以后矿石数量:<el-tag size="mini" type="success">{{ currentPoint }}</el-tag>    </div>    <div class="sign-label">      间断签到天数:<el-tag size="mini" type="success">{{ continueSignDays }}</el-tag>    </div>    <div class="sign-btn" v-if="!loading">      <el-button v-if="!login" type="primary" @click="toLogin">去登录</el-button>      <el-button v-else type="primary" :loading="signing" :disabled="todaySign" @click="toSign">{{ todaySign ? '已签到' : '去签到' }}</el-button>    </div>  </div></template><script>export default {  data() {    return {      // 头像      imageUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',      // 昵称      nickName: 'null',      // 以后矿石数量      currentPoint: 0,      // 间断签到天数      continueSignDays: 0,      // 是否登录      login: false,      // 今日是否已签到      todaySign: true,      loading: true,      signing: false,    };  },  .......  async mounted() {    this.loading = true;    // 获取用户信息    let resp = await getUserInfo();    // 判断cookie有效性    this.login = !resp.data.err_no && resp.data.data;    if (!this.login) {      this.loading = false;      return;    }    // 头像,昵称    this.imageUrl = resp.data.data.avatar_large;    this.nickName = resp.data.data.user_name;    // 矿石数量    resp = await getCurrentPoint();    this.currentPoint = resp.data.data;    // 间断签到天数    resp = await getSignData();    this.continueSignDays = resp.data.data.cont_count;    // 以后签到情况    resp = await getTodaySign();    this.todaySign = resp.data.data;    this.loading = false;  },};</script>

次要的逻辑都蕴含在页面mounted阶段,该阶段须要执行一系列操作,包含获取用户信息,判断cookie有效性,获取用户以后签到状态以及处分信息等等

疏忽\<template\>中的一堆蠢笨的\<div\>标签,前端我只会写\<div\>

批改申请头

签到申请/growth_api/v1/check_in是一个POST申请,浏览器会主动带上origin申请头,其值为chrome-extension://xxxxx,此时掘金会校验申请头中的origin,非掘金的origin会间接报403(应该是网关层做了申请起源的校验)

此时插件就须要批改申请头中的origin字段,然而origin字段并不能随便批改

比方说axios强制指定申请头中origin的值是不失效的,并且插件的控制台会有对应的谬误提醒,该操作不符合规范

此时就须要应用到manifest.json中注册的网络申请的权限

一个失常响应的申请在chrome中会经验如下图所示的申明周期

如果咱们须要批改申请头字段的话,则能够在onBeforeSendHeaders发送申请头之前通过事件监听的形式批改origin字段,同时,该事件监听应该是贯通整个插件的生命周期的,所以代码应该写在background.js

// background.js文件chrome.webRequest.onBeforeSendHeaders.addListener(  function(details) {    details.requestHeaders.push({ name: 'origin', value: 'https://juejin.cn' });    return { requestHeaders: details.requestHeaders };  },  { urls: ['*://*.juejin.cn/*'] },  ['blocking', 'requestHeaders', 'extraHeaders']);

上述代码中能够看到,chrome.webRequest.onBeforeSendHeaders.addListener承受三个参数:

  1. 监听器回调办法,批改申请头的操作应该放在这个办法中
  2. 过滤器,用于管制监听的URL范畴,这里抉择监听掘金相干的申请
  3. 元信息(opt_extraInfoSpec),简略来说就是,这个参数填写的值代表监听器的回调办法的执行形式以及回调时传入的数据
    本次监听器的元数据中蕴含['blocking', 'requestHeaders', 'extraHeaders'],这三个值别离示意:

    • blocking 示意回调办法是同步调用的,就是说一个申请的回调办法执行完之后才会轮到下一个申请的回调办法
    • requestHeaders 示意回调办法的参数details 中蕴含申请头的数据
    • extraHeaders 这个字段比拟神奇,因为origin申请头这玩意不是说改就改的,chrome也不举荐,如果真的要批改的话,就必须要填写这个字段,这样对origin的批改才会失效
在manifest配置文件中能够看到,插件申请的权限除了webRequest之外,还有webRequestBlocking,增加这个权限是因为监听办法中应用blocking同步的形式

总结

至此,这个插件的实现思路根本介绍完了,总的来看,插件的实现难度不高,有趣味的能够本人试着尝试实现一下

当然,以插件的模式来实现签到的性能其实并没有极大幅度地进步签到的效率,最好的形式必定还是在服务器中定时签到,这样即便不上掘金网站,也能播种满满的矿石用以抽奖,然而这样不可避免地将用户登录凭证裸露进来,可能存在一些平安危险,同时也丢失了掘金举办这个流动的意义

万一被他人拿到本人的cookie跑去删除本人的文章,那真的是欲哭无泪

这次掘金的签到流动集体感觉还是办的能够的

  • 工作难度很低,规定简略,流动入口显著
  • 签到处分比拟多,一个月签到下来,应该都有好几千矿石,应该能抽奖好几十次
惟一不好的中央就是我抽了好几天都没抽到switch,哈哈哈哈