Greasy Fork

Greasy Fork is available in English.

掘金抽奖

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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);
})();