前言
碰巧 掘金新上签到流动,碰巧 喝了咖啡睡不着,碰巧 双休不必下班,所以 写了个插件,不便工作日签到(顺道练练手)
先来看看成品
总的来看,插件须要实现以下几个指标:
- 检测以后用户登录态
- 判断用户明天是否已签到
- 发送签到申请
- 展现信息(用户信息,处分信息)
申请剖析
用户登录凭证
通过登录申请 [POST] /passport/web/user/login
能够看到,申请响应设置的 Cookie
中有好几个键值对,抉择一个一般的申请在 postman
上剖析,能够看到 掘金
通过 Cookie
中的 sessionid
作为 用户登录凭证
签到相干的接口
在签到性能的申请中,跟这次要实现性能相干的接口有 4 个,别离是:
- 获取签到天数的汇总信息
[GET]/growth_api/v1/get_counts
- 获取以后的矿石数量
[GET]/growth_api/v1/get_cur_point
- 判断用户明天是否已签到
[GET]/growth_api/v1/get_today_status
- 用户签到
[POST]/growth_api/v1/check_in
这里大抵剖析一下性能对应的申请即可,具体传参以及返回值的含意能够通过浏览器控制台查看(
F12
)
流程图
上面通过几个场景的时序图来论述分明插件的工作流程
未登录场景
未签到场景
已签到场景
搭建 chrome 插件开发工程
通过 vue-web-extension 实现疾速搭建 chrome 插件开发工程(Vue)
首先确保这两个曾经装置了
npm install -g @vue/cli
npm 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.json
中babel
局部删除,.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]处能够看到,插件须要申请网络权限 webRequest
和 webRequestBlocking
,这两个权限是跟用户签到申请有关系的(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
承受三个参数:
- 监听器回调办法,批改申请头的操作应该放在这个办法中
- 过滤器,用于管制监听的 URL 范畴,这里抉择监听掘金相干的申请
-
元信息(opt_extraInfoSpec),简略来说就是,这个参数填写的值代表监听器的回调办法的执行形式以及回调时传入的数据
本次监听器的元数据中蕴含['blocking', 'requestHeaders', 'extraHeaders']
,这三个值别离示意:blocking
示意回调办法是同步调用的,就是说一个申请的回调办法执行完之后才会轮到下一个申请的回调办法requestHeaders
示意回调办法的参数details
中蕴含申请头的数据extraHeaders
这个字段比拟神奇,因为origin
申请头这玩意不是说改就改的,chrome
也不举荐,如果真的要批改的话,就必须要填写这个字段,这样对origin
的批改才会失效
在 manifest 配置文件中能够看到,插件申请的权限除了
webRequest
之外,还有webRequestBlocking
,增加这个权限是因为监听办法中应用blocking
同步的形式
总结
至此,这个插件的实现思路根本介绍完了,总的来看,插件的实现难度不高,有趣味的能够本人试着尝试实现一下
当然,以插件的模式来实现签到的性能其实并没有极大幅度地进步签到的效率,最好的形式必定还是在服务器中定时签到,这样即便不上掘金网站,也能播种满满的矿石用以抽奖,然而这样不可避免地将用户登录凭证裸露进来,可能存在一些平安危险,同时也丢失了掘金举办这个流动的意义
万一被他人拿到本人的 cookie 跑去删除本人的文章,那真的是欲哭无泪
这次掘金的签到流动集体感觉还是办的能够的
- 工作难度很低,规定简略,流动入口显著
- 签到处分比拟多,一个月签到下来,应该都有好几千矿石,应该能抽奖好几十次
惟一不好的中央就是我抽了好几天都没抽到 switch,哈哈哈哈