preface

At the risk of being locked up in a small black room wrote a nuggets automatic check-in lottery program contains automatic execution, check-in, free lottery, happy, email notification scripts, no longer need to forget to check in every day 😄

The thing is this New Year’s day because of playing too hi forgot the nuggets to sign so one and the same thing as a professional touch fish 🐟 player how can break the sign (well I was for the second day 5120 ore) decisive bought the sign card later in thinking why not write a daily automatic implementation of the sign script??

Have the ability to

  • actionIt is executed at 9:00 every day
  • Email notification
    • Cookie Expiration email notification
  • Sign in
  • With happy
  • Lucky draw
  • Set the number of days you want to redeem the surrounding automatically calculates how many more days you need to sign in

There are currently so many capabilities that the project will continue to maintain as new features are added (as long as they are not closed by the arc). If there are better projects, please mention them in the comments section

Start quickly

The #juejinCheckInForkGo to your own warehouse and then put the projectclonedown

After opening a project with an editor, you need to modify several items of data with manual entries to your own

// Config file
module.exports = {
  // This parameter needs to be entered manually
  cookie: ' '.// Request an address
  baseUrl: 'https://api.juejin.cn'./ / API address
  api: {... },// Mailbox configuration
  emailConfig: {
    163 | qq / / email service
    service: '163'.// Enter the email address manually
    email: ' '.// Email authorization code Manually
    pass: ' ',}}Copy the code

Cookie obtaining method

Cookies have an expiration date of about a month or they expire when you log out

Log in and enter the nuggets, F12 open the console, select Network and randomly click an interface, find the cookie in the request header, select the data and right click to copy the value

Email Settings

So if you’re using email 163 for example, email qq and the same thing if you’re using email 163 you just set the service field to 163 and then you fill in your own email and you’re successfully logged in and you’ll see that you’ve sent yourself an email that looks something like this

Obtain authorization code ⚠️ : log in to email 163 and open Settings

Enable the following Settings: When IMAP/SMTP service is enabled, a pop-up window will pop up to send SMS messages. After wechat scan code, you can send SMS messages. (If QQ mailbox is enabled, you need to manually verify sending SMS messages.)

I have already added it here, so just click on the new authorization and it will pop up the QR code to scan and send SMS

After the SMS is sent, click I have sent and you will get your authorization code (note that the authorization code is only shown once). Paste the authorization into the pass field in the configuration file

After filling in all the parameters correctly, you can run the command node index.js locally. You can receive mail and there is a log message in the mail. Congratulations!

The project push will be modified after confirmation. The project push has set automatic execution. The automatic check-in will be performed at 9:00 every day and an email will be sent for notification

Since the use of automatic check-in, my mother no longer has to worry that I forget to sign in. Switching is not a dream

The specific implementation

If you are only interested in scripting, you can turn left here to 🚪. If you are interested in the implementation, I will share with you the specific implementation ideas

After I had an idea, I began to dig the gold sign-in related interface one by one to open the interface point to see what data is given to each data is used to do what after a long debugging script sign-in ability is completed function is mainly composed of the following several interfaces

    / / sign in
    checkIn: '/growth_api/v1/check_in'.// check the check-in
    getCheckStatus: '/growth_api/v1/get_today_status'.// Query the number of check-in days
    getCheckInDays: '/growth_api/v1/get_counts'.// Query the current ore
    getCurrentPoint: '/growth_api/v1/get_cur_point'.// check the lottery
    getlotteryStatus: '/growth_api/v1/lottery_config/get'./ / draw
    draw: '/growth_api/v1/lottery/draw'.// Check whether you are happy today
    getDipLuckyStatus: '/growth_api/v1/lottery_lucky/my_lucky'./ / happy
    dipLucky: '/growth_api/v1/lottery_lucky/dip_lucky'
Copy the code

Now, it’s pretty easy to connect to the interface. Who knows how to connect to a request library and just do it? I’m using Axios

Configure the request to write to index.js and throw the cookie in the config file into the request header

BaseURL = config. baseURL // Set cookie axios.defaults.headers['cookie'] = config.cookie // Intercept accordingly  axios.interceptors.response.use((response) => { const { data } = response if (data.err_msg === 'success' && data.err_no  === 0) { return data } else { return Promise.reject(data.err_msg) } }, (error) => { return Promise.reject(error) })Copy the code

The next step is to request the interface directly

The following main code


/** * check to see if you have checked in ** today@return {Boolean} Have you checked in */
const getCheckStatus = async() = > {try {
    const getCheckStatusRes = await axios({
      url: config.api.getCheckStatus,
      method: 'get'
    })
    return getCheckStatusRes.data
  } catch (error) {
    throw 'Failed to check in! 【${error}】 `}}/** * query current ore ** /
const getCurrentPoint = async() = > {try {
    const getCurrentPointRes = await axios({ url: config.api.getCurrentPoint, method: 'get' })
    console.log('Current total ore:${getCurrentPointRes.data}Several `)}catch (error) {
    throw 'Failed to query ore!${error.err_msg}`}}/** * Check the number of free lucky draws **@return {Boolean} Whether there are free draw times */
const getlotteryStatus = async() = > {try {
    const getlotteryStatusRes = await axios({ url: config.api.getlotteryStatus, method: 'get' })
    return getlotteryStatusRes.data.free_count === 0
  } catch (error) {
    throw 'Query free draw failed! 【${error}】 `}}/** ** */
const dipLucky = async() = > {try {
    const getDipLuckyStatusRes = await axios({ url: config.api.getDipLuckyStatus, method: 'post' })
    const dipLuckyRes = await axios({ url: config.api.dipLucky, method: 'post' })
    console.log('With beaming success! 🎉 [Current lucky value:${dipLuckyRes.data.total_value}/ 6000 】 `)}catch (error) {
    throw 'With beaming failure!${error}`}}/** ** lucky draw ** /
const draw = async() = > {try {
    const freeCount = await getlotteryStatus()
    if (freeCount) {
      // There are no free lucky draws
      throw 'Use up today's free draw'
    }

    // Be happy first
    await dipLucky()

    // Start the lottery
    const drawRes = await axios({ url: config.api.draw, method: 'post' })
    console.log('Congratulations on your drawing [${drawRes.data.lottery_name}】 🎉 `)

    if (drawRes.data.lottery_type === 1) {
      // check the total ore
      await getCurrentPoint()
    }
  } catch (error) {
    console.error('Lottery failure! = = = = = = = > 【${error}】 `)}}/** * Query the check-in days **@return {Object} ContinuousDay Consecutive check-in days sumCount Total check-in days */
const getCheckInDays = async() = > {try {
    const getCheckInDays = await axios({ url: config.api.getCheckInDays, method: 'get' })
    return { continuousDay: getCheckInDays.data.cont_count, sumCount: getCheckInDays.data.sum_count }
  } catch (error) {
    throw 'Failed to query check-in days! 🙁 【${getCheckInDays.err_msg}】 `}}/** ** sign in ** /
const checkIn = async() = > {try {
    // check whether you checked in today
    const checkStatusRes = await getCheckStatus()

    if(! checkStatusRes) {/ / sign in
      const checkInRes = await axios({ url: config.api.checkIn, method: 'post' })
      console.log('Check in successfully, current total ore${checkInRes.data.sum_point}`)

      // Query the number of check-in days
      const getCheckInDaysRes = await getCheckInDays()
      console.log('Continuous raffle${getCheckInDaysRes.continuousDay}Days Total check-in days${getCheckInDaysRes.sumCount}`)

      // Check in successfully to draw the lottery
      await draw()
    } else {
      console.log('Signed in today at ✅')}}catch (error) {
    console.error('Failed to sign in! = = = = = = = >${error}`)}}Copy the code

automated

About automatic execution I started to think of the scheme is through the server deployment to open a scheduled task to perform this way need to have a server is more trouble also some people with cloud function I am too lazy to go to the registration after finding a scheme is white whao Github Action through CI set scheduled task automatic execution every day Github has low requirements for everyone. Since I don’t know much about CI, I only learned a little about script this time. The specific code is as follows

name: jjCheckInScript

on:
  schedule:
    Minute hour Day Month Week = UTC +8
    - cron: "0 1 * * *"

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  start:
    Run the latest version of Ubuntu
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2  

      # installation node. Js
      - name: Setup Node.js
        uses: actions/setup-node@v2  
        with:
          node-version: '14'

      Install the dependency and execute the script
      - name: npm install
        run: npm install
      
      - name: Start task
        run: node index.js

Copy the code

The key code here is schedule, which changes the way the task is triggered to scheduled execution. The task will be automatically executed when the set time is reached. The task will run on the latest version of Ubuntu Minute hour Day month week (UTC Beijing time: **+8**)

Email 📧

The Nodemail library is used to send emails. It has very powerful functions and supports a variety of email services. It supports HTML content, plain text content, attachments, pictures and so on.

const nodemailer = require('nodemailer')
​
// Log processing renders script logs to HTML via EJS
const logs = []
console.oldLog = console.log
console.oldErr = console.error
​
console.log = (str) = > {
  logs.push({
    type: 'success'.text: str
  })
  console.oldLog(str)
}
​
console.error = (str) = > {
  logs.push({
    type: 'error'.text: str
  })
  console.oldErr(str)
}
​
/** ** Send email ** /
const sendEmail = async() = > {try {
    const template = ejs.compile(fs.readFileSync(path.resolve(__dirname, 'email.ejs'), 'utf8'));
    const transporter = nodemailer.createTransport({
      service: process.env.SERVICE, // Email service
      port: 465./ / port
      secure: true.// Use TLS, SSL encryption port 465
      secureConnection: true.auth: {
        user: process.env.EMAIL, // Sender's mailbox
        pass: process.env.PASS, // Email authorization code}})// Send an email
    await transporter.sendMail({
      from: process.env.EMAIL,
      to: process.env.EMAIL,
      subject: 'Nuggets sign up notice 🔔'.html: template({
        logs: logs
      })
    })
​
  } catch (error) {
    console.error('Failed to send mail!${error}`)}}Copy the code

I wrote a simple log template in EJS to hold the script’s log. In the previous section, I overwrote console.log console.error to store STR in the logs array

Email sending is actually quite simple and you can see the official documentation for the configuration and here’s how it works

  • service:Email serviceNode mai lThere is already a lot of support internallyEmail serviceIf you fill in this field you don’t need to write ithost
  • host:The host IP address of the mailbox is now onIMAP/SMTPThe IP address of the mailbox is displayed
  • prot:The default port number is465
  • Secure: Configures secure links
  • secureConnection:useSSL(Default false)
  • Auth. user: indicates the sender email address
  • Auth. pass: indicates the email authorization code
  • From: sender email address
  • To: indicates the recipient email address
  • Subject: indicates the email subject
  • html:Email contenthtmlstring

Actions Secrets Password security

As for the cookie email authorization code, it was written in a configuration folder at the beginning. Later, I proposed a better way is to use Actions secrets, which can avoid exposing key data (in case that boring guy takes your cookie to navigate 😏) or be careful

${{secrets.youkey}} ${{secrets.youkey}} ${{secrets.youkey}} ${{secrets.youkey Take this project for example

# Environment variablesenv:
  COOKIE: ${{ secrets.COOKIE }}
  PASS: ${{ secrets.PASS }}
  EMAIL: ${{ secrets.EMAIL }}
  SERVICE: ${{ secrets.SERVICE }}
Copy the code

And once we’ve done that we can use that data in our environment variables like this

process.env.COOKIE
process.env.EMAIL
Copy the code

However, the retrieved data cannot be displayed in Actions. In the output log, all passwords you defined are cleared and replaced with asterisks before the output log to prevent leakage

If you don’t think that’s secure enough and you can use data freely in your code you can try to encrypt your password

Q&A

Automatic execution delay

During the development test, Jobs found that he did not execute the 9:5 on time. When the company opened the Actions, he found that he did not execute the actions. At first, he thought it was the CRon time But that doesn’t affect our check-in function as long as we check in today

In my test, the corn time was set to 12:30 per day, but the actual execution time was 12:51, almost 20 minutes late

-cron: "30 4 * * *"Copy the code

The definition of Schedule in GitHub is as follows:

Note: The schedule event can be delayed during periods of high loads of GitHub Actions workflow runs. High load times include the start of every hour. To decrease the chance of delay, schedule your workflow to run at a different time of the hour.

Note: Scheduling events may be delayed during a high-load GitHub Action workflow run. High load times include the start of each hour. To reduce the chance of delay, schedule your workflow to run at different times of the hour.

In other words, the cron time in Schedule is not the actual execution time, but the queue time of workflow entering GitHub for execution. To simplify it, the queue time of workflow entering GitHub for execution depends on the load of GitHub workflow

This is not a fatal problem in check-in requirements. If you want to solve it, you can refer to the Github Action’s Schedule is not running on time

Why don’tdocument.cookie?

The cookie obtained by the console input command is incomplete

This is what the console gotcookieCompare the interfacecookieThere is a big difference

Eggs 🎉

In the debugging time found nuggets with happy interface bug!! But after I called the smudge interface, the interface was smudge successfully

Then I opened the lottery_lucky/ my_LUCKY interface and found the happiness value was +10

Then I click on the lucky draw page to be happy and successful!

The issue has been reported to the Nuggets team and is expected to be fixed soon (programmer: WTM ended again at the end of the year)

The statement 📢

This project is only suitable for learning and communication and does not have any other purposes. It has not gone through the excavation official team. If the title is blocked, it has nothing to do with me.

Other ideas or features are welcome to discuss at 👏