掘金抽奖

掘金抽奖 签到 免费抽奖 5连抽 10连抽 可视化抽奖 petite-vue

// ==UserScript==
// @name         掘金抽奖
// @namespace    http://tampermonkey.net/
// @version      1.2.5
// @description  掘金抽奖 签到 免费抽奖 5连抽 10连抽 可视化抽奖 petite-vue
// @author       无仙
// @match        https://juejin.cn/*
// @icon         https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web//static/favicons/favicon-32x32.png
// @require      https://unpkg.com/petite-vue
// ==/UserScript==

(async function () {
  'use strict';

  const { createApp } = PetiteVue; // 不会吧不会吧,不会还有人不知道petite-vue吧

  const root = document.createElement('div');
  root.class = 'wx_draw_wrap';
  root.innerHTML = `
    <div v-show="!popup" class="wx_draw" @click="open">掘金抽奖</div>

    <div v-if="popup" class="wx_popup">
      <div class="wx_mask" @click="popup = false"></div>

      <div class="wx_main">
        <div class="wx_header">
          <div>掘金抽奖</div>
          <div class="wx_score">当前矿石:{{ score }}</div>
        </div>

        <div class="wx_body">
          <div class="wx_options">
            <div @click="check_in" v-if="check_status === -1 || check_status === false">签到</div>
            <div @click="get_free" v-else>签到成功</div>
            <div @click="draw(5)">5连抽</div>
            <div @click="draw(10)">10连抽</div>
            <div @click="draw(undefined)">梭哈抽奖</div>
          </div>

          <table cellpadding="0" cellspacing="0" border="0" width="100%">
            <thead>
              <tr>
                <th>奖品图片</th>
                <th>奖品名称</th>
                <th>中奖次数</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in award">
                <td><img :src="item.lottery_image"/></td>
                <td>{{ item.lottery_name }}</td>
                <td>{{ item.times }}</td>
              </tr>
            </tbody>
          </table>

          <div class="wx_loading" v-if="loading">
            <svg class="circular" viewBox="25 25 50 50">
              <circle class="path" cx="50" cy="50" r="20" fill="none" />
            </svg>
          </div>
        </div>

        <div class="wx_footer">
          <div class="wx_confirm wx_btn" @click="popup = false">关闭</div>
        </div>
      </div>
    </div>
  `;

  // 查询奖品列表
  const res = await fetch('https://api.juejin.cn/growth_api/v1/lottery_config/get', {
    headers: {
      cookie: document.cookie
    },
    method: 'GET',
    credentials: 'include'
  }).then((res) => res.json());

  const award = (res.data && res.data.lottery ? res.data.lottery : []).map((item) => ({ ...item, times: 0 }));
  const { free_count, point_cost } = res.data; // 剩余免费抽奖次数,单次抽奖消耗数

  document.body.appendChild(root); // 插入DOM

  // petite-vue init初始化
  createApp({
    award,
    popup: false,
    loading: false,
    score: 0,
    free_count,
    check_status: -1,

    async open() {
      const res = await fetch('https://api.juejin.cn/growth_api/v1/get_cur_point', {
        headers: {
          cookie: document.cookie
        },
        method: 'GET',
        credentials: 'include'
      }).then((res) => res.json());

      this.score = res.data; // 当前分数

      this.popup = true;

      (this.check_status === -1 || this.check_status === false) && this.get_status();
    },
    async draw(times, is_not_free = true) {
      if (this.loading || times === 0) return;

      // const is_not_free = !(this.free_count && times === 1);

      if (is_not_free && this.score < point_cost * (times || 1)) return alert('分都不够想啥呢?');

      let i = 0;
      const drawFn = async () => {
        if ((is_not_free && this.score < point_cost) || i === times) {
          this.free_count = 0;
          this.loading = false;
          this.open();
          console.log(`${times ? times + '连抽' : '梭哈'}结束!`);
          return;
        }

        const result = await fetch('https://api.juejin.cn/growth_api/v1/lottery/draw', {
          headers: {
            cookie: document.cookie
          },
          method: 'POST',
          credentials: 'include'
        }).then((res) => res.json());

        if (result.err_no !== 0) {
          console.log(result.err_msg);
          this.loading = false;
          this.open();
          return;
        }

        i++;
        is_not_free && (this.score -= point_cost);

        if (result.data.lottery_type === 1) this.score += 66;

        const item = this.award.find((item) => item.lottery_id === result.data.lottery_id);
        item.times++;

        console.log(`抽到:${result.data.lottery_name}`);
        drawFn();
      };

      console.log(`开始${times ? times + '连抽' : '梭哈'}!`);
      this.loading = true;
      this.award.forEach((item) => {
        item.times = 0;
      });
      try {
        drawFn();
      } catch (error) {
        this.loading = false;
        console.error(error);
      }
    },
    async check_in() {
      if (this.check_status) {
        this.get_free(); // 免费抽奖
        return;
      }

      // 签到
      const check_in = await fetch('https://api.juejin.cn/growth_api/v1/check_in', {
        headers: {
          cookie: document.cookie
        },
        method: 'POST',
        credentials: 'include'
      }).then((res) => res.json());

      if (check_in.err_no !== 0) {
        alert('签到失败!');
        this.check_status = false;
        return;
      }

      this.check_status = true;
      this.score = check_in.data.sum_point;
      this.get_free(); // 免费抽奖
    },
    async get_status() {
      // 查询签到状态
      const today_status = await fetch('https://api.juejin.cn/growth_api/v1/get_today_status', {
        headers: {
          cookie: document.cookie
        },
        method: 'GET',
        credentials: 'include'
      }).then((res) => res.json());

      this.check_status = today_status.data;
    },
    async get_free() {
      // 查询是否有免费抽奖次数
      const res = await fetch('https://api.juejin.cn/growth_api/v1/lottery_config/get', {
        headers: {
          cookie: document.cookie
        },
        method: 'GET',
        credentials: 'include'
      }).then((res) => res.json());

      this.free_count = res.data.free_count;

      if (res.data.free_count) {
        // 有免费抽奖次数
        this.draw(res.data.free_count, false);
      }
    }
  }).mount();

  // 处理样式
  const style = `
    .wx_draw_wrap {
      box-sizing: border-box;
      position: fixed;
      top: 50%;
      left: 0px;
      z-index: 888888;
      margin-top: -20px;
    }
    .wx_draw {
      box-sizing: border-box;
      position: fixed;
      top: 50%;
      left: 0px;
      z-index: 888888;
      width: 40px;
      height: 40px;
      line-height: 16px;
      font-size: 12px;
      padding: 4px;
      background-color: rgb(232, 243, 255);
      border: 1px solid rgb(232, 243, 255);
      color: rgb(30, 128, 255);
      text-align: center;
      overflow: hidden;
      cursor: pointer;
    }
    .wx_popup {
      position: fixed;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      z-index: 999999;
    }
    .wx_mask {
      width: 100%;
      height: 100%;
      background: rgba(0,0,0,0.5);
    }
    .wx_main {
      --width: 460px;
      position: absolute;
      left: 50%;
      top: 50%;
      width: var(--width);
      transform: translate(-50%, -50%);
      background: #fff;
      border-radius: 4px;
    }
    .wx_main .wx_header {
      height: 40px;
      line-height: 40px;
      font-size: 16px;
      padding: 0 16px;
      border-bottom: 1px solid #999;
      display: flex;
      align-items: center;
      justify-content: space-between;
      color: #000;
      font-weight: 400;
    }
    .wx_score {
      font-size: 12px;
      font-size: #00a100;
    }
    .wx_main .wx_body {
      padding: 16px;
      border-bottom: 1px solid #999;
      position: relative;
    }
    .wx_main .wx_options {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 8px;
    }
    .wx_main .wx_options div {
      width: 80px;
      text-align: center;
      height: 24px;
      line-height: 24px;
      background-color: rgb(232, 243, 255);
      border: 1px solid #c9d4e3;
      color: rgb(30, 128, 255);
      cursor: pointer;
      border-radius: 2px;
    }
    .wx_main .wx_body p {
      margin: 0 0 8px;
    }
    .wx_body table {
      width: 100%;
      text-align: center;
      border-left: 1px solid #ccc;
      border-top: 1px solid #ccc;
    }
    .wx_body table th,
    .wx_body table td {
      border-right: 1px solid #ccc;
      border-bottom: 1px solid #ccc;
      line-height: 20px;
    }
    .wx_body table th {
      line-height: 28px;
    }
    .wx_main .wx_body img {
      vertical-align: middle;
      width: 40px;
      height: 40px;
    }
    .wx_main .wx_footer {
      padding: 12px 16px;
      text-align: right;
    }
    .wx_btn {
      display: inline-block;
      width: 48px;
      cursor: pointer;
      text-align: center;
      height: 20px;
      line-height: 20px;
      background-color: rgb(232, 243, 255);
      border: 1px solid #c9d4e3;
      color: rgb(30, 128, 255);
      border-radius: 2px;
    }
    .wx_loading {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      z-index: 9999999;
      background: rgba(0,0,0,0.65);
    }
    .wx_loading .circular {
      height: 42px;
      width: 42px;
      -webkit-animation: loading-rotate 2s linear infinite;
      animation: loading-rotate 2s linear infinite;
      position: absolute;
      left: 50%;
      top: 50%;
      margin-top: -21px;
      margin-left: -21px;
    }
    .wx_loading .path {
      -webkit-animation: loading-dash 1.5s ease-in-out infinite;
      animation: loading-dash 1.5s ease-in-out infinite;
      stroke-dasharray: 90, 150;
      stroke-dashoffset: 0;
      stroke-width: 2;
      stroke: #409eff;
      stroke-linecap: round;
    }
    @keyframes loading-rotate {
      100% {
        -webkit-transform: rotate(360deg);
        transform: rotate(360deg);
      }
    }
    @keyframes loading-dash {
      0% {
        stroke-dasharray: 1, 200;
        stroke-dashoffset: 0;
      }
      50% {
        stroke-dasharray: 90, 150;
        stroke-dashoffset: -40px;
      }
      100% {
        stroke-dasharray: 90, 150;
        stroke-dashoffset: -120px;
      }
    }
  `;

  const styleEl = document.createElement('style');
  styleEl.textContent = style;
  document.head.appendChild(styleEl);
})();