preface

It happened that the Nuggets had a new check-in activity, happened to drink coffee and couldn’t sleep, and happened to have two days off from work, so I wrote a plug-in to facilitate the check-in on weekdays (practice on the way).

Let’s take a look at the finished product

In general, the plug-in needs to achieve the following goals:

  1. Check the current user login status
  2. Determine whether the user has checked in today
  3. Send a check-in request
  4. Display information (user information, reward information)

Analysis of the request

User login credentials

Through login request [POST] / Passport/Web /user/login, it can be seen that there are several key-value pairs in the Cookie set by the request response. If a common request is selected for analysis on Postman, it can be seen that the gold nuggets use the sessionID in the Cookie as the user login credentials

Check in the relevant interface

In the request for the check-in function, there are four interfaces related to the function to be implemented this time, which are as follows:

  1. Get a summary of the number of check-in days[GET]/growth_api/v1/get_counts
  2. Gets the current ore count[GET]/growth_api/v1/get_cur_point
  3. Determine whether the user has checked in today[GET]/growth_api/v1/get_today_status
  4. User sign in[POST]/growth_api/v1/check_in

Here we can roughly analyze the request corresponding to the function. The specific meaning of the passed parameter and returned value can be viewed through the browser console (F12).

The flow chart

The following sequence diagrams of several scenarios illustrate the workflow of the plug-in

Not logged in scenario

SequenceDiagram Participant User as User Participant Plugin AS Plug-in Participant Chrome AS Browser Participant JueJin as Gold digging Chrome -->>-plugin: return sessionid plugin->>+juejin: [GET] /user_api/v1/user/ GET juejin-->>-plugin: return current user plugin-->> plugin: check current cookie is invalid plugin-->>-user: User ->>+plugin: click the plugin button plugin->>+juejin: jump to the login page ->>juejin: user login juejin-->>-plugin: Plugin -->>-user: Login is successful

Uncheck-in scenario

SequenceDiagram Participant User as User Participant Plugin AS Plug-in Participant Chrome AS Browser Participant JueJin as Gold digging Chrome -->>-plugin: return sessionid plugin->>+juejin: [GET] /user_api/v1/user/ GET juejin-->>-plugin: return current user info plugin->>plugin -> +juejin: [GET]/growth_api/v1/get_today_status juejin-->>-plugin: return plugin-->> plugin-->>-user: Plugin ->>+juejin: [POST]/growth_api/v1/check_in juejin-->>-plugin: Plugin -->>-user: check in successfully, display the reward information

Check-in scenario

SequenceDiagram Participant User as User Participant Plugin AS Plug-in Participant Chrome AS Browser Participant JueJin as Gold digging Chrome -->>-plugin: return sessionid plugin->>+juejin: [GET] /user_api/v1/user/ GET juejin-->>-plugin: return current user info plugin->>plugin -> +juejin: [GET]/growth_api/v1/get_today_status juejin-->>-plugin: return user check-in status plugin-->> plugin--> -user: Display user information, ore quantity

Build chrome plug-in development project

Quick Build chrome Plug-in development Project through VUE-Web-Extension (VUE)

Make sure these two are installed first

npm install -g @vue/cli
npm install -g @vue/cli-init
Copy the code

I then created the project with VUe-Web-extension, and I chose vue-web-extension as version V1

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

Select the features you want on demand (Axios mandatory)

Install Element UI (load on demand)

cd juejin-auto-sign && vue add element
Copy the code

Since the configuration of Element UI is written in the Babel part of package.json file, it overlaps with the original. Babelrc configuration file of the project, so the configuration of Babel part of package.json needs to be merged into the

Before the merger

#.babelrc configuration file{ "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 configuration file{... "babel": { "plugins": [ [ "component", { "libraryName": "element-ui", "styleLibraryName": "theme-chalk" } ] ] } }Copy the code

After the merge, delete the Babel part of package.json, and the.babelrc configuration file is as follows

After yarn Build is executed in the project root directory, yarn build can be packaged properly

yarn build
Copy the code

! [image-20210717175823529](/Users/luoxiongjian/Library/Application Support/typora-user-images/image-20210717175823529.png)

At this point, the project has been basically completed! It can be put into development

Common engineering commands:

  • Yarn Build Builds the plug-in and outputs it to the dist directory
  • Yarn build-zip Builds the plug-in compression package in the format of the plug-in name and version number
  • yarn watchBuild the plug-in, export it to the dist directory, and refresh it if it changes

The key code

Manifest.json configuration file

The manifest. Json file records the original information of the plug-in, including basic information of the plug-in (plug-in name, version number, ICON, etc.), related pages of the plug-in (popup, options, background, etc.), and permissions that the plug-in needs to apply for from Chrome

{
  // Plug-in name
  "name": "juejin-auto-sign".// Plug-in description
  "description": "Nuggets check-in assistant.".// Plug-in version number
  "version": "1.0.0"."manifest_version": 2."icons": {
    "48": "icons/icon.png"."128": "icons/icon.png"},...// [1] apply for nuggets cookie, network request permission
  "permissions": [
    "cookies"."*://*.juejin.cn/"."webRequest"."webRequestBlocking"]}Copy the code

[1] it can be seen that the plug-in needs to apply for the network access webRequest and webRequestBlocking, which are related to the user check-in request (POST request). Why are these two permissions required will be explained in detail later

Popup page

At present, the function of the plug-in is implemented in the Popup page, the so-called popup page is displayed in the browser plug-in bar by clicking on the page

In the case of Google Translate, the red arrow points to a popup page

Chrome plugin development has several kinds of page and script page: popup, optional, background, plugin on the different page display location is different, the purpose is different, currently only need to understand the popup page can be script: Background-js, Content Script, etc. Different scripts have different declaration cycles

Here is the main code for the popup page of the check-in assistant plug-in

<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="! </el-button> <el-button v-else type="primary" :loading="signing" :disabled="todaySign" @click="toSign">{{ todaySign ? </el-button> </div> </template> <script> export default {data() {return {// avatar imageUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png', / / nickName nickName: 'null', / / the current ore quantity currentPoint: 0, // continueSignDays: 0, // Whether login: false, // Whether todaySign: true, loading: true, signing: false,}; },... async mounted() { this.loading = true; Let resp = await getUserInfo(); 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>Copy the code

The main logic is contained in the page Mounted stage, which needs to perform a series of operations, including obtaining user information, judging the validity of cookies, obtaining the current check-in status of users and reward information, etc

Ignore a bunch of awkward <div> tags in <template>, I’ll just say <div>

Modify the request header

Growth_api /v1/check_in /growth_api/v1/check_in /growth_api/v1/check_in/growth_API /v1/check_in/growth_API /v1/check_in/growth_API /v1/check_in/growth_API /v1/check_in/growth_API Non-nuggets Origin will report 403 directly.

At this point, the plug-in needs to modify the Origin field in the request header. However, the Origin field cannot be modified arbitrarily

For example, if AXIos forces the origin value in the request header to be invalid, and the plugin console will receive an error message, this operation does not comply with the specification

At this point, you need to use the network request permissions registered in manifest.json

A properly responded request in Chrome will undergo a declaration cycle as shown in the figure below

If we need to modify the request header field, we can modify the Origin field by event listening before onBeforeSendHeaders sends the request header. Also, this event listening should continue throughout the plug-in’s lifetime, so the code should be in backback.js

/ / background. Js file
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']);Copy the code

The code above you can see, chrome. WebRequest. OnBeforeSendHeaders. AddListener accepts three parameters:

  1. The listener callback method in which the operation to modify the request header should be placed
  2. Filter to control the range of URLS to listen on, which is selected to listen on requests related to mining
  3. The meta information (opt_extraInfoSpec) simply means that the value entered in this parameter represents how the listener’s callback method is executed and the data passed in during the callback is contained in the listener’s metadata['blocking', 'requestHeaders', 'extraHeaders'], the three values respectively represent:
    • blockingThe callback method is invoked synchronously, meaning that the callback method of one request is executed before the callback method of the next request is called
    • requestHeadersRepresents the parameters of the callback methoddetailsContains the data for the request header
    • extraHeadersThis field is kind of magical becauseoriginThe request header thing doesn’t change on demand,chromeAlso not recommended, if you really want to change, you have to fill in this field, that’s rightoriginWill take effect

As you can see in the manifest configuration file, the plug-in applies for webRequest as well as webRequestBlocking permission, which is added because of the way blocking synchronization is used in the listening method

conclusion

So far, the implementation of this plug-in idea is basically introduced, in general, the implementation of the plug-in is not high, interested in trying to try to achieve it

, of course, in the form of a plug-in to realize the sign-in function there is no great extent to improve the efficiency of check-in, or in the server of the best ways to check in regularly, so even if not on the website, also can harvest full ore to draw, but it inevitably user login credentials will be exposed to go out, there may be some security risks, At the same time, the nuggets lost the point of hosting the event

In case others get their own cookie ran to delete their own articles, that is really want to cry

The nuggets of the sign-in activities personally feel that it is ok to do

  • The task difficulty is very low, the rule is simple, the activity entrance is obvious

  • Sign-in rewards more, a month sign-in down, there should be thousands of ore, should be able to draw dozens of times

The only bad thing is I didn’t get a Switch for a few days, hahaha