gemini beta v0.1

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » gemini beta v0.1 » SIMULATION » ссука


ссука

Сообщений 1 страница 4 из 4

1

ссука

+1

2

const BankPostsComponent = {
  template: `
  <bank-category-wrapper :title="category.title" :description="category.description">

    <div class="gemini_bank_prices">
        <div class="gemini_bank_prices_item" v-for="item in category.items" :key="item.id">
            <span class="gemini_bank_price">{{ item.price }}</span>
            <span>{{ item.title }}
                <span class="gemini_bank_item_comment" v-if="item.comment"> {{ item.comment }}</span></span>
        </div>
    </div>

    <button class="gemini_bank_btn" @click="calculate" :disabled="loading || globalLoading">
        {{ loading ? 'считаю...' : 'посчитать токены за игру' }}
    </button>

    <div v-if="calculated" class="gemini_topic_block1 gemini_bank_result">
        <div v-if="result.length === 0" class="gemini_bank_result_empty">
            пока что получать токены в этой категории не за что
        </div>
        <div v-if="result.length === 1 && result[0].type === 'error'" class="gemini_bank_result_empty">
            {{ result[0].message }}
        </div>

        <template v-if="totalEarned > 0">
            <div class="gemini_bank_result_title">можно получить токены:</div>
           
            <!-- Посты -->
            <div v-if="result.filter(r => r.type === 'post').length">
                <div class="gemini_bank_result_subtitle">за посты:</div>
                <p v-for="item in result.filter(r => r.type === 'post')" :key="item.id">
                    <a :href="'/viewtopic.php?pid=' + item.id + '#p' + item.id">
                        {{ item.subject }}
                    </a>
                    <span v-if="item.postedDate" class="gemini_bank_post_date">(от {{ item.postedDate }})</span>
                    {{ item.symbols }} символов
                    <span v-if="item.boostApplied" class="voucher_label boostup_label">x{{ item.multiplier }} BOOST UP</span>
                    <label v-else-if="!item.boostApplied && (postItAvailable > 0 || item.postItApplied)"
                        class="voucher_label postit_label" :class="{ applied: item.postItApplied }">
                        <input type="checkbox" v-model="item.postItApplied" @change="togglePostIt(item)">
                        {{ item.postItApplied ? 'x2 POST IT' : 'POST IT' }}
                    </label>
                    <span class="gemini_bank_earned">{{ item.earned }} </span>
                </p>
            </div>

<!-- SNS посты -->
<div v-if="result.filter(r => r.type === 'sns' && !r.boostApplied).length || result.filter(r => r.type === 'sns' && r.boostApplied).length">
    <div class="gemini_bank_result_subtitle">за посты в SNS:</div>
   
    <div v-for="item in result.filter(r => r.type === 'sns' && !r.boostApplied)" :key="item.id">
        <p>
            {{ item.title }}
            <span class="gemini_bank_earned">{{ item.earned }}</span>
            <div v-for="(topic, index) in item.topics" :key="topic.id">
                <a :href="'/viewtopic.php?id=' + topic.id">{{ topic.subject }}</a>
                <span v-if="index < item.topics.length - 1">, </span>
            </div>
        </p>
    </div>

    <div v-for="item in result.filter(r => r.type === 'sns' && r.boostApplied)" :key="item.id">
        <p>
            {{ item.title }}
            <span class="voucher_label boostup_label">BOOST UP бонус</span>
            <span class="gemini_bank_earned">{{ item.earned }}</span>
            <div v-for="(topic, index) in item.topics" :key="topic.id">
                <a :href="'/viewtopic.php?id=' + topic.id">{{ topic.subject }}</a>
                <span v-if="index < item.topics.length - 1">, </span>
            </div>
        </p>
    </div>
</div>

            <!-- Открытые эпизоды -->
            <div v-if="result.filter(r => r.type === 'open_episode').length">
                <div class="gemini_bank_result_subtitle">за открытые эпизоды:</div>
                <p v-for="item in result.filter(r => r.type === 'open_episode')" :key="item.id">
                    <a :href="'/viewtopic.php?id=' + item.id">
                        {{ item.subject }}
                    </a>
                    <span v-if="item.multiplier > 1" class="voucher_label boostup_label">x{{ item.multiplier }} BOOST UP</span>
                    <span class="gemini_bank_earned">{{ item.earned }} </span>
                </p>
            </div>

            <!-- Закрытые эпизоды -->
            <div v-if="result.filter(r => r.type === 'closed_episode').length">
                <div class="gemini_bank_result_subtitle">за закрытые эпизоды:</div>
                <p v-for="item in result.filter(r => r.type === 'closed_episode')" :key="item.id">
                    <a :href="'/viewtopic.php?id=' + item.id">
                        {{ item.subject }}
                    </a>
                    <span v-if="item.multiplier > 1" class="voucher_label boostup_label">x{{ item.multiplier }} BOOST UP</span>
                    <span class="gemini_bank_earned">{{ item.earned }} </span>
                </p>
            </div>

<!-- 10 постов -->
<div v-if="result.filter(r => r.type === 'ten_posts').length">
    <div class="gemini_bank_result_subtitle">{{ result.filter(r => r.type === 'ten_posts')[0].subtitle }}</div>
   
    <!-- Обычные десятки (без буста) -->
    <p v-if="result.filter(r => r.type === 'ten_posts')[0].normalTens > 0">
        каждые 10 постов
        <span v-if="result.filter(r => r.type === 'ten_posts')[0].normalTens > 1">x{{ result.filter(r => r.type === 'ten_posts')[0].normalTens }}</span>
        (с {{ result.filter(r => r.type === 'ten_posts')[0].rangeFrom + result.filter(r => r.type === 'ten_posts')[0].boostedTens * 10 }} по {{ result.filter(r => r.type === 'ten_posts')[0].rangeTo }})
        <span class="gemini_bank_earned">{{ result.filter(r => r.type === 'ten_posts')[0].normalTens * 1000 }}</span>
    </p>
   
    <!-- Десятки с бустом -->
    <p v-if="result.filter(r => r.type === 'ten_posts')[0].boostedTens > 0">
        каждые 10 постов
        <span v-if="result.filter(r => r.type === 'ten_posts')[0].boostedTens > 1">x{{ result.filter(r => r.type === 'ten_posts')[0].boostedTens }}</span>
        <span class="voucher_label boostup_label">x2 BOOST UP</span>
        (с {{ result.filter(r => r.type === 'ten_posts')[0].rangeFrom }} по {{ result.filter(r => r.type === 'ten_posts')[0].rangeFrom + result.filter(r => r.type === 'ten_posts')[0].boostedTens * 10 }})
        <span class="gemini_bank_earned">{{ result.filter(r => r.type === 'ten_posts')[0].boostedTens * 1000 * 2 }}</span>
    </p>
</div>

            <div class="gemini_bank_result_total">
                итого за игру: {{ totalEarned }}
            </div>
        </template>
    </div>

</bank-category-wrapper>
  `,

  props: {
    userInfo: Object,
    vouchers: Object,
  },

  emits: ["update-total"],

  data() {
    return {
      calculated: false,
      result: [],
      loading: false,
      postItUsed: 0,
    };
  },

  computed: {
    category() {
      return BANK_CONFIG.categories.posts;
    },
    currencyName() {
      return BANK_CONFIG.settings.currencyName;
    },
    totalEarned() {
      return this.result.reduce((sum, item) => sum + item.earned, 0);
    },
    postItAvailable() {
      return (this.vouchers?.post_it || 0) - this.postItUsed;
    },
  },

  methods: {
    async calculate() {
      if (this.globalLoading) return;
      this.setGlobalLoading(true);
      this.loading = true;

      try {
        const domain = BANK_CONFIG.settings.domain;
        const playForums = BANK_CONFIG.settings.playForums;
        const snsForums = BANK_CONFIG.settings.snsForums;
        const userLogin = this.userInfo.login;
        const userId = this.userInfo.id;

        this.result = [];
        this.calculated = false;

        const delay = () => new Promise((resolve) => setTimeout(resolve, 100));

        const safeStorageGet = async (key) => {
          try {
            const result = await mybbAPI.storageGet(key);
            if (result?.storage?.data?.[key])
              return JSON.parse(result.storage.data[key]);
          } catch (err) {}
          return [];
        };

        // bank_last_post
        let savedPostsMax = 0;
        try {
          const result = await mybbAPI.storageGet("bank_last_post");
          if (result?.storage?.data?.bank_last_post)
            savedPostsMax = parseInt(result.storage.data.bank_last_post);
        } catch (err) {}
        await delay();

        let savedOpenEpsIds = (await safeStorageGet("bank_open_eps")).map(
          Number,
        );
        await delay();
        let savedClosedEpsIds = (await safeStorageGet("bank_closed_eps")).map(
          Number,
        );
        await delay();

        // SNS: загружаем сохранённый ID последнего поста
        let savedSnsLast = 0;
        try {
          const r = await mybbAPI.storageGet("bank_sns_last");
          if (r?.storage?.data?.bank_sns_last)
            savedSnsLast = parseInt(r.storage.data.bank_sns_last);
        } catch (err) {}
        await delay();

        // Шаг 1: собираем ID тем (игровые форумы)
        const forumsParam = playForums.join(",");
        let url = `https://${domain}/search.php?action=search&author=${encodeWin1251(userLogin)}&forums=${forumsParam}&show_as=topics`;
        const response = await fetch(url, { credentials: "include" });
        const html = await response.text();

        const topicIds = [];
        const topicRegex = /viewtopic\.php\?id=(\d+)/g;
        let match;
        while ((match = topicRegex.exec(html)) !== null) {
          if (!topicIds.includes(match[1])) topicIds.push(match[1]);
        }

        const pageRegex =
          /search\.php\?action=show_user_posts&amp;user_id=\d+&amp;p=(\d+)/g;
        const pages = [];
        while ((match = pageRegex.exec(html)) !== null) pages.push(match[1]);
        const maxPage = pages.length ? Math.max(...pages.map(Number)) : 1;

        for (let p = 2; p <= maxPage; p++) {
          const pageUrl = `https://${domain}/search.php?action=show_user_posts&user_id=${userId}&p=${p}`;
          const pageResponse = await fetch(pageUrl, { credentials: "include" });
          const pageHtml = await pageResponse.text();
          while ((match = topicRegex.exec(pageHtml)) !== null) {
            if (!topicIds.includes(match[1])) topicIds.push(match[1]);
          }
        }

        // Шаг 1.5: SNS темы
        const snsTopicIds = [];
        if (snsForums && snsForums.length > 0) {
          const snsForumsParam = snsForums.join(",");
          const snsUrl = `https://${domain}/search.php?action=search&author=${encodeWin1251(userLogin)}&forums=${snsForumsParam}&show_as=topics`;
          const snsResponse = await fetch(snsUrl, { credentials: "include" });
          const snsHtml = await snsResponse.text();
          while ((match = topicRegex.exec(snsHtml)) !== null) {
            if (!snsTopicIds.includes(match[1])) snsTopicIds.push(match[1]);
          }
          const snsPageRegex =
            /search\.php\?action=show_user_posts&amp;user_id=\d+&amp;p=(\d+)/g;
          const snsPages = [];
          while ((match = snsPageRegex.exec(snsHtml)) !== null)
            snsPages.push(match[1]);
          const snsMaxPage = snsPages.length
            ? Math.max(...snsPages.map(Number))
            : 1;
          for (let p = 2; p <= snsMaxPage; p++) {
            const pageUrl = `https://${domain}/search.php?action=show_user_posts&user_id=${userId}&p=${p}&forums=${snsForumsParam}`;
            const pageResponse = await fetch(pageUrl, {
              credentials: "include",
            });
            const pageHtml = await pageResponse.text();
            while ((match = topicRegex.exec(pageHtml)) !== null) {
              if (!snsTopicIds.includes(match[1])) snsTopicIds.push(match[1]);
            }
          }
        }

        // Получаем все посты (игровые)
        const allPosts = [];
        if (topicIds.length > 0) {
          let skip = 0;
          let batch;
          do {
            batch = await mybbAPI.getPosts({
              topic_id: topicIds.map(Number),
              user_id: userId,
              fields: [
                "subject",
                "message",
                "id",
                "user_id",
                "topic_id",
                "posted",
              ],
              limit: 100,
              skip: skip,
            });
            allPosts.push(...batch);
            skip += 100;
            await delay();
          } while (batch.length === 100);
        }

        // Получаем SNS посты
        const snsPosts = [];
        if (snsTopicIds.length > 0) {
          let skip = 0;
          let batch;
          do {
            batch = await mybbAPI.getPosts({
              topic_id: snsTopicIds.map(Number),
              user_id: userId,
              fields: [
                "subject",
                "message",
                "id",
                "user_id",
                "topic_id",
                "posted",
              ],
              limit: 100,
              skip: skip,
            });
            snsPosts.push(...batch);
            skip += 100;
            await delay();
          } while (batch.length === 100);
        }

        // Шаг 3: фильтруем игровые посты
        // Сначала собираем все темы и их init_post
        const topicInitMap = {};
        for (const post of allPosts) {
          if (+post.user_id !== +userId) continue;
          const topicId = +post.topic_id;
          if (!topicInitMap[topicId]) {
            topicInitMap[topicId] = {
              initPostId: null,
              hasUserPost: false,
            };
          }
          topicInitMap[topicId].hasUserPost = true;
        }

        // Получаем init_post для всех тем
        const allTopicIds = Object.keys(topicInitMap).map(Number);
        if (allTopicIds.length > 0) {
          let initSkip = 0;
          let initBatch;
          do {
            initBatch = await mybbAPI.getTopics({
              topic_id: allTopicIds.slice(initSkip, initSkip + 100),
              fields: ["id", "init_post"],
            });
            for (const t of initBatch) {
              if (topicInitMap[+t.id]) {
                topicInitMap[+t.id].initPostId = +t.init_id;
              }
            }
            initSkip += 100;
            await delay();
          } while (initBatch.length === 100);
        }

        const validPosts = [];
        const validSpeedPosts = [];
        const openEpisodeTopics = [];
        const checkedTopics = new Set();
        const speedPostTopics = new Set();
        const potentialSpeedPosts = [];

        const countSymbols = (message) => {
          return message
            .replace(/<div class="hvmask"[^>]*>[\s\S]*?<\/div>/g, "")
            .replace(/\[block=hvmask\][\s\S]*?\[\/block\]/gim, "")
            .replace(/\[html\][\s\S]*?\[\/html\]/gim, "")
            .replace(/<[^>]*>?/gim, "")
            .replace(
              /\[([^$\s\[\]=]+?)(?:="?.+?"?|\s[^$\[\]]+?)\]([\s\S]*?)\[\/\1\]/gim,
              "$2",
            )
            .replace(/\[([^$]+?)\]([\s\S]*?)\[\/\1\]/gim, "$2")
            .replace(/\[td.*?\]([\s\S]*?)\[\/td\]/gim, "$1")
            .replace(/\[(hr|add|you|br|indent)\]/gim, "")
            .replace(/&#[\d]*;/gim, "-")
            .replace(/\s+/g, " ")
            .trim().length;
        };

        for (const post of allPosts) {
          if (+post.user_id !== +userId) continue;
          const symbols = countSymbols(post.message);

          let postDate = "";
          let multiplier = 1;
          if (post.posted) {
            const d = new Date(Number(post.posted) * 1000);
            const msk = new Date(
              d.toLocaleString("en-US", { timeZone: "Europe/Moscow" }),
            );
            if (!isNaN(msk)) {
              postDate = msk.toISOString().split("T")[0];
              multiplier = this.getMultiplier(postDate);
            }
          }

          const topicId = +post.topic_id;
          const isInitPost = +post.id === topicInitMap[topicId]?.initPostId;

          if (symbols >= 1000 && +post.id > savedPostsMax) {
            validPosts.push({
              id: +post.id,
              subject: post.subject,
              symbols,
              multiplier,
              boostApplied: multiplier > 1,
              postedDate: this.formatDate(post.posted),
            });
          }
          if (symbols < 1000 && symbols > 0 && +post.id > savedPostsMax) {
            speedPostTopics.add(topicId);
            potentialSpeedPosts.push({
              id: +post.id,
              topicId: topicId,
              subject: post.subject,
              symbols,
              multiplier,
              boostApplied: multiplier > 1,
              postedDate: this.formatDate(post.posted),
            });
          }

          // Открытые эпизоды: проверяем, что это не init_post и тема ещё не сохранена
          if (
            !checkedTopics.has(topicId) &&
            !savedOpenEpsIds.includes(topicId) &&
            !isInitPost
          ) {
            checkedTopics.add(topicId);
            openEpisodeTopics.push({
              id: topicId,
              subject: post.subject,
            });
          }
        }

        // Спид-посты проверка
        const initPostsMap = {};
        if (speedPostTopics.size > 0) {
          let allSpeedTopics = [];
          let speedSkip = 0;
          let speedBatch;
          do {
            speedBatch = await mybbAPI.getTopics({
              topic_id: [...speedPostTopics].slice(speedSkip, speedSkip + 100),
              fields: ["init_post", "id"],
            });
            allSpeedTopics.push(...speedBatch);
            speedSkip += 100;
            await delay();
          } while (speedBatch.length === 100);
          for (const t of allSpeedTopics) initPostsMap[+t.id] = +t.init_id;
        }
        for (const p of potentialSpeedPosts) {
          if (+p.id !== initPostsMap[p.topicId]) validSpeedPosts.push(p);
        }

        // ===== SNS ПОСТЫ =====
        const snsItem = this.category.items.find((i) => i.id === "sns_post");
        let lastSnsPostId = savedSnsLast;
        if (snsPosts.length > 0 && snsItem) {
          const snsByTopic = {};
          const snsInitPosts = {};

          if (snsTopicIds.length > 0) {
            let initSkip = 0;
            let initBatch;
            do {
              initBatch = await mybbAPI.getTopics({
                topic_id: snsTopicIds
                  .slice(initSkip, initSkip + 100)
                  .map(Number),
                fields: ["id", "init_post", "subject"],
              });
              for (const t of initBatch) {
                snsInitPosts[+t.id] = +t.init_id;
              }
              initSkip += 100;
              await delay();
            } while (initBatch.length === 100);
          }

          const sortedSnsPosts = [...snsPosts]
            .filter((p) => +p.user_id === +userId && +p.id > savedSnsLast)
            .sort((a, b) => +a.id - +b.id);

          for (const post of sortedSnsPosts) {
            const topicId = +post.topic_id;
            const initPostId = snsInitPosts[topicId] || 0;
            if (+post.id === initPostId) continue;

            if (!snsByTopic[topicId]) {
              snsByTopic[topicId] = {
                topicId: topicId,
                subject: post.subject || `Тема ${topicId}`,
                posts: [],
              };
            }
            snsByTopic[topicId].posts.push(post);
          }

          const sortedTopics = Object.values(snsByTopic).sort((a, b) => {
            const aDate = a.posts[0]?.posted || 0;
            const bDate = b.posts[0]?.posted || 0;
            return aDate - bDate;
          });

          let snsPostCounter = 0;
          const mainBlocks = [];
          const bonusBlocks = [];

          for (const topic of sortedTopics) {
            const topicPosts = topic.posts.sort((a, b) => +a.id - +b.id);
            for (const post of topicPosts) {
              snsPostCounter++;

              if (snsPostCounter % 10 === 0) {
                const startIdx = Math.max(0, snsPostCounter - 10);
                let allPostsSoFar = [];
                for (const t of sortedTopics) {
                  for (const p of t.posts) {
                    allPostsSoFar.push(p);
                  }
                }
                allPostsSoFar.sort((a, b) => +a.id - +b.id);
                const blockPostsData = allPostsSoFar.slice(
                  startIdx,
                  snsPostCounter,
                );

                const lastPostInBlock =
                  blockPostsData[blockPostsData.length - 1];
                lastSnsPostId = Math.max(lastSnsPostId, lastPostInBlock.id);

                const blockTopics = [];
                const seenTopicIds = new Set();
                for (const p of blockPostsData) {
                  const tid = +p.topic_id;
                  if (!seenTopicIds.has(tid)) {
                    seenTopicIds.add(tid);
                    const topicInfo = sortedTopics.find(
                      (t) => t.topicId === tid,
                    );
                    if (topicInfo) {
                      blockTopics.push({
                        id: tid,
                        subject: topicInfo.subject,
                      });
                    }
                  }
                }

                let allBoosted = true;
                for (const p of blockPostsData) {
                  if (p.posted) {
                    const d = new Date(Number(p.posted) * 1000);
                    const msk = new Date(
                      d.toLocaleString("en-US", { timeZone: "Europe/Moscow" }),
                    );
                    if (
                      !isNaN(msk) &&
                      !this.isBoostActive(msk.toISOString().split("T")[0])
                    ) {
                      allBoosted = false;
                      break;
                    }
                  } else {
                    allBoosted = false;
                    break;
                  }
                }

                mainBlocks.push({
                  topics: blockTopics,
                  postCount: blockPostsData.length,
                  earned: snsItem.price,
                });

                if (allBoosted) {
                  bonusBlocks.push({
                    topics: blockTopics,
                    postCount: blockPostsData.length,
                    earned: snsItem.price,
                  });
                }
              }
            }
          }

          if (mainBlocks.length > 0) {
            const groupedMain = {
              count: mainBlocks.length,
              topics: [],
              postCount: 0,
            };
            for (const block of mainBlocks) {
              groupedMain.topics = groupedMain.topics.concat(block.topics);
              groupedMain.postCount += block.postCount;
            }
            this.result.push({
              id: "sns_main",
              type: "sns",
              title:
                mainBlocks.length > 1
                  ? `${snsItem.title} x${mainBlocks.length}`
                  : snsItem.title,
              topics: groupedMain.topics,
              postCount: groupedMain.postCount,
              boostApplied: false,
              earned: groupedMain.count * snsItem.price,
            });
          }

          if (bonusBlocks.length > 0) {
            const groupedBonus = {
              count: bonusBlocks.length,
              topics: [],
              postCount: 0,
            };
            for (const block of bonusBlocks) {
              groupedBonus.topics = groupedBonus.topics.concat(block.topics);
              groupedBonus.postCount += block.postCount;
            }
            this.result.push({
              id: "sns_bonus",
              type: "sns",
              title:
                bonusBlocks.length > 1
                  ? `${snsItem.title} x${bonusBlocks.length}`
                  : snsItem.title,
              topics: groupedBonus.topics,
              postCount: groupedBonus.postCount,
              boostApplied: true,
              earned: groupedBonus.count * snsItem.price,
            });
          }
        }

        // Открытые эпизоды - даты
        if (openEpisodeTopics.length > 0) {
          const openTopicIds = openEpisodeTopics.map((ep) => ep.id);
          const topicsInfo = await mybbAPI.getTopics({
            topic_id: openTopicIds,
            fields: ["init_post", "id"],
          });
          await delay();
          const initPostMap = {};
          for (const t of topicsInfo) initPostMap[+t.id] = +t.init_id;

          const firstPostDates = {};
          for (const topicId of openTopicIds) {
            const initPostId = initPostMap[topicId];
            const posts = await mybbAPI.getPosts({
              topic_id: topicId,
              user_id: userId,
              fields: ["posted", "id"],
              sort_by: "posted",
              order: "asc",
              limit: 2,
            });
            await delay();
            for (const fp of posts) {
              if (+fp.id !== initPostId && fp.posted) {
                const d = new Date(Number(fp.posted) * 1000);
                const msk = new Date(
                  d.toLocaleString("en-US", { timeZone: "Europe/Moscow" }),
                );
                if (!isNaN(msk)) {
                  firstPostDates[topicId] = msk.toISOString().split("T")[0];
                  break;
                }
              }
            }
          }
          for (const ep of openEpisodeTopics) {
            const dateStr = firstPostDates[ep.id];
            ep.multiplier = dateStr ? this.getMultiplier(dateStr) : 1;
          }
        }

        // Закрытые эпизоды
        const closedEpisodeTopics = [];
        const topicsToCheck = topicIds
          .map(Number)
          .filter((id) => !savedClosedEpsIds.includes(id));
        if (topicsToCheck.length > 0) {
          let allTopicsResult = [];
          let closedSkip = 0;
          let closedBatch;
          do {
            closedBatch = await mybbAPI.getTopics({
              topic_id: topicsToCheck.slice(closedSkip, closedSkip + 100),
              fields: [
                "subject",
                "num_replies",
                "closed",
                "id",
                "init_post",
                "last_post_date",
              ],
            });
            allTopicsResult.push(...closedBatch);
            closedSkip += 100;
            await delay();
          } while (closedBatch.length === 100);

          for (const topic of allTopicsResult) {
            if (topic.closed === "1" && +topic.num_replies >= 4) {
              let episodeMultiplier = 1;
              if (topic.last_post_date) {
                const lastPostDate = new Date(
                  Number(topic.last_post_date) * 1000,
                );
                const msk = new Date(
                  lastPostDate.toLocaleString("en-US", {
                    timeZone: "Europe/Moscow",
                  }),
                );
                if (!isNaN(msk)) {
                  const dateStr = msk.toISOString().split("T")[0];
                  episodeMultiplier = this.getMultiplier(dateStr);
                }
              }
              closedEpisodeTopics.push({
                id: +topic.id,
                subject: topic.subject,
                multiplier: episodeMultiplier,
              });
            }
          }
        }

        // Шаг 3.6: каждые 10 постов
        let savedPostsTotal = 0;
        try {
          const result = await mybbAPI.storageGet("bank_posts_total");
          if (result?.storage?.data?.bank_posts_total)
            savedPostsTotal = parseInt(result.storage.data.bank_posts_total);
        } catch (err) {}
        await delay();

        try {
          const result = await mybbAPI.storageGet("bank_last_post");
          if (result?.storage?.data?.bank_last_post)
            savedPostsMax = parseInt(result.storage.data.bank_last_post);
        } catch (err) {}
        await delay();

        const totalNewPosts = validPosts.length + validSpeedPosts.length;
        const currentTotalPosts = (+this.userInfo.posts || 0) + totalNewPosts;
        const oldTens = Math.floor(savedPostsTotal / 10);
        const newTens = Math.floor(currentTotalPosts / 10);
        const earnedTens = newTens - oldTens;

        let tenPostsBoostedTens = 0;
        if (earnedTens > 0 && this.vouchers?.boost_up?.length > 0) {
          const allPostIds = [
            ...validPosts.map((p) => p.id),
            ...validSpeedPosts.map((p) => p.id),
          ];
          const sortedIds = allPostIds.sort((a, b) => a - b);

          const postsToCheck = [];
          for (let i = 0; i < sortedIds.length; i++) {
            const postNumber = savedPostsTotal + i + 1;
            if (postNumber % 10 === 0) {
              postsToCheck.push(sortedIds[i]);
            }
          }

          if (postsToCheck.length > 0) {
            const chunkSize = 50;
            let allPostsWithDates = [];
            for (let i = 0; i < postsToCheck.length; i += chunkSize) {
              const chunk = postsToCheck.slice(i, i + chunkSize);
              const chunkResult = await mybbAPI.getPosts({
                post_id: chunk,
                fields: ["id", "posted"],
                limit: chunk.length,
              });
              allPostsWithDates.push(...chunkResult);
              await delay();
            }

            const postDateMap = {};
            for (const p of allPostsWithDates) {
              postDateMap[p.id] = p.posted;
            }

            for (const postId of postsToCheck) {
              const posted = postDateMap[postId];
              if (posted) {
                const d = new Date(Number(posted) * 1000);
                const msk = new Date(
                  d.toLocaleString("en-US", { timeZone: "Europe/Moscow" }),
                );
                const dateStr = msk.toISOString().split("T")[0];
                if (!isNaN(msk) && this.isBoostActive(dateStr)) {
                  tenPostsBoostedTens++;
                }
              }
            }
          }
        }

        // Шаг 4: результат
        const postSpeedItem = this.category.items.find(
          (i) => i.id === "post_speed",
        );
        const postSymbolsItem = this.category.items.find(
          (i) => i.id === "post_symbols",
        );
        const newEpisodeItem = this.category.items.find(
          (i) => i.id === "new_episode",
        );
        const closedEpisodeItem = this.category.items.find(
          (i) => i.id === "closed_episode",
        );
        const tenPostsItem = this.category.items.find(
          (i) => i.id === "post_ten",
        );

        for (const p of validSpeedPosts) {
          this.result.push({
            id: p.id,
            type: "post",
            subject: p.subject,
            symbols: p.symbols,
            multiplier: p.multiplier || 1,
            boostApplied: p.boostApplied || false,
            postedDate: p.postedDate || "",
            postItApplied: false,
            earned: postSpeedItem.price * (p.multiplier || 1),
          });
        }
        for (const p of validPosts) {
          this.result.push({
            id: p.id,
            type: "post",
            subject: p.subject,
            symbols: p.symbols,
            multiplier: p.multiplier,
            boostApplied: p.boostApplied,
            postedDate: p.postedDate,
            postItApplied: false,
            earned:
              Math.floor(p.symbols / 1000) *
              postSymbolsItem.price *
              p.multiplier,
          });
        }
        for (const ep of openEpisodeTopics) {
          this.result.push({
            id: ep.id,
            type: "open_episode",
            subject: ep.subject,
            earned: newEpisodeItem.price * (ep.multiplier || 1),
            multiplier: ep.multiplier,
          });
        }
        for (const ep of closedEpisodeTopics) {
          this.result.push({
            id: ep.id,
            type: "closed_episode",
            subject: ep.subject,
            earned: closedEpisodeItem.price * (ep.multiplier || 1),
            multiplier: ep.multiplier,
          });
        }

        if (earnedTens > 0) {
          const normalTens = earnedTens - tenPostsBoostedTens;
          const boostedTens = tenPostsBoostedTens;
          this.result.push({
            id: "post_ten",
            type: "ten_posts",
            subtitle: "за количество постов:",
            normalTens,
            boostedTens,
            rangeFrom: oldTens * 10,
            rangeTo: newTens * 10,
            earned:
              normalTens * tenPostsItem.price +
              boostedTens * tenPostsItem.price * 2,
          });
        }

        this.calculated = true;
        this.$emit("update-total", {
          category: "posts",
          total: this.totalEarned,
          data: {
            posts: this.result
              .filter((r) => r.type === "post")
              .map((r) => r.id),
            open_eps: openEpisodeTopics.map((ep) => ep.id),
            closed_eps: closedEpisodeTopics.map((ep) => ep.id),
            posts_total: currentTotalPosts,
            sns_last: lastSnsPostId || savedSnsLast,
          },
        });
      } catch (err) {
        console.error("Ошибка:", err);
        const message =
          (err.message && err.message.includes("503")) ||
          (err.stack && err.stack.includes("503"))
            ? "сервер временно перегружен. попробуй позднее."
            : "произошла ошибка. попробуй ещё раз или напиши леви.";
        this.result = [{ id: "error", type: "error", message }];
        this.calculated = true;
      } finally {
        this.loading = false;
        this.setGlobalLoading(false);
        this.postItUsed = 0;
      }
    },

    isBoostActive(dateStr) {
      if (!dateStr) return false;
      if (!this.vouchers?.boost_up) return false;
      const date = new Date(dateStr + "T00:00:00");
      if (isNaN(date)) return false;
      const ranges = this.vouchers.boost_up;
      for (let i = 0; i < ranges.length; i++) {
        const from = ranges[i][0];
        const to = ranges[i][1];
        const start = new Date(from + "T00:00:00");
        const end = new Date(to + "T23:59:59");

        if (date >= start && date <= end) return true;
      }
      return false;
    },

    getMultiplier(postDate) {
      if (!postDate || isNaN(new Date(postDate))) return 1;
      if (this.isBoostActive(postDate)) return 2;
      return 1;
    },

    formatDate(timestamp) {
      if (!timestamp) return "";
      const d = new Date(Number(timestamp) * 1000);
      const msk = new Date(
        d.toLocaleString("en-US", { timeZone: "Europe/Moscow" }),
      );
      if (isNaN(msk)) return "";
      const day = String(msk.getDate()).padStart(2, "0");
      const month = String(msk.getMonth() + 1).padStart(2, "0");
      return `${day}.${month}`;
    },

    togglePostIt(item) {
      const speedItem = this.category.items.find((i) => i.id === "post_speed");
      const symbolItem = this.category.items.find(
        (i) => i.id === "post_symbols",
      );
      const pricePerItem =
        item.symbols < 1000
          ? speedItem.price
          : Math.floor(item.symbols / 1000) * symbolItem.price;

      if (item.postItApplied) {
        this.postItUsed++;
        item.multiplier = 2;
      } else {
        this.postItUsed--;
        item.multiplier = 1;
      }
      item.earned = pricePerItem * item.multiplier;

      this.$emit("update-total", {
        category: "posts",
        total: this.totalEarned,
        data: {
          posts: this.result.filter((r) => r.type === "post").map((r) => r.id),
        },
      });
    },

    clearData() {
      this.result = [];
      this.calculated = false;
      this.$emit("update-total", {
        category: "posts",
        total: 0,
        data: {
          posts: [],
          open_eps: [],
          closed_eps: [],
          posts_total: 0,
          sns_last: 0,
        },
      });
      this.postItUsed = 0;
    },
  },
};

window.BankPostsComponent = BankPostsComponent;

+1

3

const requiredComponents = [
  "BankCategoryWrapper",
  "BankPostsComponent",
  "BankActivityComponent",
  "BankAdvertisingComponent",
  "BankOnlineComponent",
  "BankGraphicsComponent",
];

let attempts = 0;
const maxAttempts = 20;
const checkInterval = 200;

function initApp() {
  attempts++;
  const allLoaded = requiredComponents.every((name) => window[name]);

  if (allLoaded && typeof Vue !== "undefined") {
    startApp();
    return;
  }

  if (attempts < maxAttempts) {
    setTimeout(initApp, checkInterval);
  } else {
    console.error(
      "Не все компоненты загружены после " + maxAttempts + " попыток:",
      {
        Vue: typeof Vue,
        BankCategoryWrapper: !!window.BankCategoryWrapper,
        BankPostsComponent: !!window.BankPostsComponent,
        BankActivityComponent: !!window.BankActivityComponent,
        BankAdvertisingComponent: !!window.BankAdvertisingComponent,
        BankOnlineComponent: !!window.BankOnlineComponent,
        BankGraphicsComponent: !!window.BankGraphicsComponent,
      },
    );
  }
}

function startApp() {
  const { createApp } = Vue;

  const app = createApp({
    data() {
      return {
        isAnyLoading: false,
        userInfo: {
          id: window.UserID,
          login: window.UserLogin,
          posts: +window.UserFld5 || 0,
          balance: 0,
          characterKey: null,
          messages: +window.UserPosts || 0,
          respectPlus: +window.UserRespectPlus || 0,
          positivePlus: +window.UserPositivePlus || 0,
          totalTime: window.UserTotalTime || 0,
          registerDate: window.UserRegistered || 0,
        },
        categoriesTotal: {},
        bankData: {
          posts: [],
          open_eps: [],
          closed_eps: [],
          posts_total: 0,
          messages: 0,
          positive: 0,
          reputation: 0,
        },
        vouchers: {},
      };
    },

    computed: {
      totalTokens() {
        return Object.values(this.categoriesTotal).reduce(
          (sum, val) => sum + val,
          0,
        );
      },
      totalBalance() {
        return (this.userInfo.balance || 0) + this.totalTokens;
      },
      hasData() {
        return this.totalTokens > 0;
      },
    },

    methods: {
      async loadUserBalance() {
        const fld3 = window.UserFld3;
        if (!fld3) return;
        const decoded = fld3
          .replace(/&lt;/g, "<")
          .replace(/&quot;/g, '"')
          .replace(/&gt;/g, ">");
        const match = decoded.match(/id="(user_\d+)"/);
        if (!match) return;
        this.userInfo.characterKey = match[1];
        try {
          const result = await mybbAPI.call("storage.get", {
            app_id: 9,
            key: match[1],
          });
          if (result?.storage?.data?.[match[1]]) {
            this.userInfo.balance =
              parseInt(result.storage.data[match[1]]) || 0;
          }
        } catch (err) {
          console.error("Ошибка загрузки баланса:", err);
        }
      },

      async loadVouchers() {
        if (!this.userInfo.characterKey) return;
        try {
          const result = await mybbAPI.call("storage.get", {
            app_id: 3,
            key: this.userInfo.characterKey,
          });
          if (result?.storage?.data?.[this.userInfo.characterKey]) {
            this.vouchers = JSON.parse(
              result.storage.data[this.userInfo.characterKey],
            );
          }
        } catch (err) {
          console.error("Ошибка загрузки ваучеров:", err);
        }
      },

      updateCategoryTotal({ category, total, data }) {
        this.categoriesTotal[category] = total;
        if (data) Object.assign(this.bankData, data);
      },

      clearAll() {
        this.$refs.postsComponent?.clearData();
        this.$refs.activityComponent?.clearData();
        this.$refs.advertisingComponent?.clearData();
        this.$refs.onlineComponent?.clearData();
        this.$refs.graphicsComponent?.clearData();
        this.categoriesTotal = {};
        this.bankData = {
          posts: [],
          open_eps: [],
          closed_eps: [],
          posts_total: 0,
          messages: 0,
          positive: 0,
          reputation: 0,
        };
      },

      async submitForm() {
        const overlay = document.getElementById("submit-overlay");
        if (overlay) overlay.style.display = "flex";
        if (!this.userInfo.characterKey) await this.loadUserBalance();

        const textarea = document.getElementById("main-reply");
        const delay = () => new Promise((r) => setTimeout(r, 100));
        let formText = "";
        let techInfo = ""; // техническая информация

        try {
          // Токены
          if (this.totalTokens > 0) {
            const oldBalance = +this.userInfo.balance || 0;
            const newBalance = oldBalance + +this.totalTokens;
            await mybbAPI.call("storage.set", {
              key: this.userInfo.characterKey,
              value: String(newBalance),
              app_id: 9,
              token: window.ForumAPITicket,
            });
            await delay();
            window.UserFld2 = String(newBalance);
            this.userInfo.balance = newBalance;
            formText += `токенов было: ${oldBalance} -> стало: ${newBalance}\n`;
          }

          // POST IT
          const usedPostIt = this.$refs.postsComponent?.postItUsed || 0;
          if (usedPostIt > 0) {
            let vouchers = {};
            try {
              const r = await mybbAPI.call("storage.get", {
                app_id: 3,
                key: this.userInfo.characterKey,
                token: window.ForumAPITicket,
              });
              if (r?.storage?.data?.[this.userInfo.characterKey])
                vouchers = JSON.parse(
                  r.storage.data[this.userInfo.characterKey],
                );
            } catch (err) {}
            await delay();
            vouchers.post_it = (vouchers.post_it || 0) - usedPostIt;
            if (vouchers.post_it <= 0) delete vouchers.post_it;
            try {
              await mybbAPI.call("storage.delete", {
                key: this.userInfo.characterKey,
                app_id: 3,
                token: window.ForumAPITicket,
              });
            } catch (err) {}
            await delay();
            if (Object.keys(vouchers).length > 0) {
              await mybbAPI.call("storage.set", {
                key: this.userInfo.characterKey,
                value: JSON.stringify(vouchers),
                action: "append",
                app_id: 3,
                token: window.ForumAPITicket,
              });
            }
            await delay();
            this.vouchers = vouchers;
            formText += `использовано POST IT: ${usedPostIt}, осталось: ${vouchers.post_it || 0}\n`;
          }

          // === СБОР ТЕХНИЧЕСКОЙ ИНФОРМАЦИИ ===
          // bank_last_post
          if (this.bankData.posts && this.bankData.posts.length > 0) {
            let oldMax = 0;
            try {
              const r = await mybbAPI.storageGet("bank_last_post");
              if (r?.storage?.data?.bank_last_post)
                oldMax = parseInt(r.storage.data.bank_last_post);
            } catch (err) {}
            await delay();
            const newMax = Math.max(...this.bankData.posts.map(Number));
            if (newMax > oldMax) {
              await mybbAPI.storageSet("bank_last_post", String(newMax));
              await delay();
              techInfo += `bank_last_post: был ${oldMax} -> стал ${newMax}\n`;
            }
          }

          // bank_open_eps
          if (this.bankData.open_eps && this.bankData.open_eps.length > 0) {
            let oldData = [];
            try {
              const r = await mybbAPI.storageGet("bank_open_eps");
              if (r?.storage?.data?.bank_open_eps)
                oldData = JSON.parse(r.storage.data.bank_open_eps).map(Number);
            } catch (err) {}
            await delay();
            const newData = [
              ...new Set([...oldData, ...this.bankData.open_eps.map(Number)]),
            ];
            await mybbAPI.storageSet("bank_open_eps", JSON.stringify(newData));
            await delay();
            techInfo += `bank_open_eps: ${JSON.stringify(newData)}\n`;
          }

          // bank_closed_eps
          if (this.bankData.closed_eps && this.bankData.closed_eps.length > 0) {
            let oldData = [];
            try {
              const r = await mybbAPI.storageGet("bank_closed_eps");
              if (r?.storage?.data?.bank_closed_eps)
                oldData = JSON.parse(r.storage.data.bank_closed_eps).map(
                  Number,
                );
            } catch (err) {}
            await delay();
            const newData = [
              ...new Set([...oldData, ...this.bankData.closed_eps.map(Number)]),
            ];
            await mybbAPI.storageSet(
              "bank_closed_eps",
              JSON.stringify(newData),
            );
            await delay();
            techInfo += `bank_closed_eps: ${JSON.stringify(newData)}\n`;
          }

          // bank_last_contest
          if (this.bankData.contests && this.bankData.contests.length > 0) {
            let oldMax = 0;
            try {
              const r = await mybbAPI.storageGet("bank_last_contest");
              if (r?.storage?.data?.bank_last_contest)
                oldMax = parseInt(r.storage.data.bank_last_contest);
            } catch (err) {}
            await delay();
            const newMax = Math.max(...this.bankData.contests.map(Number));
            if (newMax > oldMax) {
              await mybbAPI.storageSet("bank_last_contest", String(newMax));
              await delay();
              techInfo += `bank_last_contest: был ${oldMax} -> стал ${newMax}\n`;
            }
          }

          // bank_weekly
          if (this.bankData.weekly && this.categoriesTotal.activity > 0) {
            await mybbAPI.storageSet(
              "bank_weekly",
              String(this.bankData.weekly),
            );
            await delay();
            techInfo += `bank_weekly: ${this.bankData.weekly}\n`;
          }

          // bank_posts_total
          if (
            (this.bankData.posts_total || this.bankData.posts_total === 0) &&
            this.categoriesTotal.posts > 0
          ) {
            let oldPostsTotal = 0;
            try {
              const r = await mybbAPI.storageGet("bank_posts_total");
              if (r?.storage?.data?.bank_posts_total)
                oldPostsTotal = parseInt(r.storage.data.bank_posts_total);
            } catch (err) {}
            await delay();
            if (this.bankData.posts_total !== oldPostsTotal) {
              await mybbAPI.storageSet(
                "bank_posts_total",
                String(this.bankData.posts_total),
              );
              await delay();
              techInfo += `bank_posts_total: было ${oldPostsTotal} -> стало ${this.bankData.posts_total}\n`;
            }
          }

          // Регистрация
          if (this.bankData.reg_last) {
            await mybbAPI.storageSet(
              "bank_reg_last",
              String(this.bankData.reg_last),
            );
            await delay();
            techInfo += `bank_reg_last: ${this.bankData.reg_last}\n`;
          }

          // Онлайн
          if (this.bankData.online_last) {
            await mybbAPI.storageSet(
              "bank_online_last",
              String(this.bankData.online_last),
            );
            await delay();
            techInfo += `bank_online_last: ${this.bankData.online_last}\n`;
          }

          // РЕКЛАМА
          if (this.categoriesTotal.advertising > 0) {
            if (this.bankData.adv_max_posted) {
              let oldMax = 0;
              try {
                const r = await mybbAPI.storageGet("bank_adv_last");
                if (r?.storage?.data?.bank_adv_last)
                  oldMax = parseInt(r.storage.data.bank_adv_last);
              } catch (err) {}
              await delay();
              if (this.bankData.adv_max_posted > oldMax) {
                await mybbAPI.storageSet(
                  "bank_adv_last",
                  String(this.bankData.adv_max_posted),
                );
                await delay();
                techInfo += `bank_adv_last: был ${oldMax} -> стал ${this.bankData.adv_max_posted}\n`;
              }
            }

            if (this.bankData.adv_boost_max_posted) {
              let oldMax = 0;
              try {
                const r = await mybbAPI.storageGet("bank_adv_boost_last");
                if (r?.storage?.data?.bank_adv_boost_last)
                  oldMax = parseInt(r.storage.data.bank_adv_boost_last);
              } catch (err) {}
              await delay();
              if (this.bankData.adv_boost_max_posted > oldMax) {
                await mybbAPI.storageSet(
                  "bank_adv_boost_last",
                  String(this.bankData.adv_boost_max_posted),
                );
                await delay();
                techInfo += `bank_adv_boost_last: был ${oldMax} -> стал ${this.bankData.adv_boost_max_posted}\n`;
              }
            }

            if (
              this.bankData.finished_ids &&
              this.bankData.finished_ids.length > 0
            ) {
              await mybbAPI.storageSet(
                "finished_pr_topics",
                JSON.stringify(this.bankData.finished_ids),
              );
              await delay();
              techInfo += `finished_pr_topics: ${JSON.stringify(this.bankData.finished_ids)}\n`;
            }

            if (this.bankData.unfinished_surplus !== undefined) {
              await mybbAPI.storageSet(
                "pr_unfinished_surplus",
                String(this.bankData.unfinished_surplus),
              );
              await delay();
              techInfo += `pr_unfinished_surplus: ${this.bankData.unfinished_surplus}\n`;
            }

            if (this.bankData.finished_surplus !== undefined) {
              await mybbAPI.storageSet(
                "pr_finished_surplus",
                String(this.bankData.finished_surplus),
              );
              await delay();
              techInfo += `pr_finished_surplus: ${this.bankData.finished_surplus}\n`;
            }
          }

          // Активность
          const activityKeys = [
            "bank_messages",
            "bank_positive",
            "bank_reputation",
          ];
          const activityDataKeys = ["messages", "positive", "reputation"];
          if (this.categoriesTotal.activity > 0) {
            for (let i = 0; i < activityKeys.length; i++) {
              const key = activityKeys[i],
                dataKey = activityDataKeys[i];
              if (!this.bankData[dataKey] || this.bankData[dataKey] === 0)
                continue;
              let oldValue = 0;
              try {
                const r = await mybbAPI.storageGet(key);
                if (r?.storage?.data?.[key])
                  oldValue = parseInt(r.storage.data[key]);
              } catch (err) {}
              await delay();
              await mybbAPI.storageSet(key, String(this.bankData[dataKey]));
              await delay();
              techInfo += `${key}: было ${oldValue} -> стало ${this.bankData[dataKey]}\n`;
            }
          }

          if (this.bankData.boost_tho_last) {
            await mybbAPI.storageSet(
              "bank_boost_tho_last",
              String(this.bankData.boost_tho_last),
            );
            await delay();
            techInfo += `bank_boost_tho_last: ${this.bankData.boost_tho_last}\n`;
          }

          // SNS
          if (this.bankData.sns_last) {
            await mybbAPI.storageSet(
              "bank_sns_last",
              String(this.bankData.sns_last),
            );
            await delay();
            techInfo += `bank_sns_last: ${this.bankData.sns_last}\n`;
          }

          // Графика
          if (this.bankData.graph_last) {
            await mybbAPI.storageSet(
              "bank_graph_last",
              String(this.bankData.graph_last),
            );
            await delay();
            techInfo += `bank_graph_last: ${this.bankData.graph_last}\n`;
          }

          // Посты в профиле
          const oldPosts = +this.userInfo.posts || 0;
          const newPosts = oldPosts + (this.bankData.posts?.length || 0);
          window.UserFld5 = String(newPosts);
          this.userInfo.posts = newPosts;
          this.updateProfileFields([{ selector: "#fld5", value: newPosts }]);

          if (newPosts !== oldPosts) {
            formText += `постов было: ${oldPosts} -> стало: ${newPosts}\n`;
          }

          // === ФОРМИРУЕМ ИТОГОВЫЙ ТЕКСТ ===
          let finalText = formText;
          if (techInfo.trim()) {
            finalText += `\n[html]<div class="bank-tech-info">${techInfo.trim()}</div>[/html]`;
          }

          if (textarea) {
            textarea.value = finalText;
            textarea.scrollIntoView({ behavior: "smooth", block: "center" });
          }

          this.clearAll();

          // Находим кнопку отправки и кликаем
          const submitBtn = document.querySelector(
            'input[type="submit"][name="submit"]',
          );
          if (submitBtn) {
            submitBtn.click();
            await delay();
            await delay();
          }
        } catch (err) {
          console.error(err);
          alert(
            "не удалось сохранить данные на сервер. попробуй ещё раз.\n\n" +
              err.message,
          );
        }
        if (overlay) overlay.style.display = "none";
      },

      updateProfileFields(fields) {
        var userId = window.UserID;
        var profileUrl = "/profile.php?section=fields&id=" + userId;
        var iframe = document.createElement("iframe");
        iframe.style.display = "none";
        document.body.appendChild(iframe);
        iframe.src = profileUrl + "&nohead";
        iframe.onload = function () {
          var iframeDoc =
            iframe.contentDocument || iframe.contentWindow.document;
          var form = iframeDoc.getElementById("profile8");
          if (form) {
            for (var f of fields) {
              var field = form.querySelector(f.selector);
              if (field) field.value = f.value;
            }
            form.submit();
            setTimeout(function () {
              if (iframe.parentNode) document.body.removeChild(iframe);
            }, 1000);
          } else {
            console.error("Форма profile8 не найдена");
            if (iframe.parentNode) document.body.removeChild(iframe);
          }
        };
        iframe.onerror = function () {
          console.error("Ошибка загрузки страницы профиля");
          if (iframe.parentNode) document.body.removeChild(iframe);
        };
      },
    },

    async mounted() {
      await this.loadUserBalance();
      await this.loadVouchers();
    },
  });

  app.mixin({
    computed: {
      globalLoading() {
        return this.$root.isAnyLoading;
      },
    },
    methods: {
      setGlobalLoading(value) {
        this.$root.isAnyLoading = value;
      },
    },
  });

  app.component("bank-category-wrapper", BankCategoryWrapper);
  app.component("bank-posts", BankPostsComponent);
  app.component("bank-activity", BankActivityComponent);
  app.component("bank-advertising", BankAdvertisingComponent);
  app.component("bank-online", BankOnlineComponent);
  app.component("bank-graphics", BankGraphicsComponent);
  app.mount("#bank-app");
}

setTimeout(initApp, 100);

+1

4

et attempts = 0;
const maxAttempts = 20;
const checkInterval = 200;

function initApp() {
  attempts++;
  const allLoaded = requiredComponents.every((name) => window[name]);

  if (allLoaded && typeof Vue !== "undefined") {
    startApp();
    return;
  }

  if (attempts < maxAttempts) {
    setTimeout(initApp, checkInterval);
  } else {
    console.error(
      "Не все компоненты загружены после " + maxAttempts + " попыток:",
      {
        Vue: typeof Vue,
        BankCategoryWrapper: !!window.BankCategoryWrapper,
        BankPostsComponent: !!window.BankPostsComponent,
        BankActivityComponent: !!window.BankActivityComponent,
        BankAdvertisingComponent: !!window.BankAdvertisingComponent,
        BankOnlineComponent: !!window.BankOnlineComponent,
        BankGraphicsComponent: !!window.BankGraphicsComponent,
      },
    );
  }
}

function startApp() {
  const { createApp } = Vue;

  const app = createApp({
    data() {
      return {
        isAnyLoading: false,
        userInfo: {
          id: window.UserID,
          login: window.UserLogin,
          posts: +window.UserFld5 || 0,
          balance: 0,
          characterKey: null,
          messages: +window.UserPosts || 0,
          respectPlus: +window.UserRespectPlus || 0,
          positivePlus: +window.UserPositivePlus || 0,
          totalTime: window.UserTotalTime || 0,
          registerDate: window.UserRegistered || 0,
        },
        categoriesTotal: {},
        bankData: {
          posts: [],
          open_eps: [],
          closed_eps: [],
          posts_total: 0,
          messages: 0,
          positive: 0,
          reputation: 0,
        },
        vouchers: {},
      };
    },

    computed: {
      totalTokens() {
        return Object.values(this.categoriesTotal).reduce(
          (sum, val) => sum + val,
          0,
        );
      },
      totalBalance() {
        return (this.userInfo.balance || 0) + this.totalTokens;
      },
      hasData() {
        return this.totalTokens > 0;
      },
    },

    methods: {
      async loadUserBalance() {
        const fld3 = window.UserFld3;
        if (!fld3) return;
        const decoded = fld3
          .replace(/&lt;/g, "<")
          .replace(/&quot;/g, '"')
          .replace(/&gt;/g, ">");
        const match = decoded.match(/id="(user_\d+)"/);
        if (!match) return;
        this.userInfo.characterKey = match[1];
        try {
          const result = await mybbAPI.call("storage.get", {
            app_id: 9,
            key: match[1],
          });
          if (result?.storage?.data?.[match[1]]) {
            this.userInfo.balance =
              parseInt(result.storage.data[match[1]]) || 0;
          }
        } catch (err) {
          console.error("Ошибка загрузки баланса:", err);
        }
      },

      async loadVouchers() {
        if (!this.userInfo.characterKey) return;
        try {
          const result = await mybbAPI.call("storage.get", {
            app_id: 3,
            key: this.userInfo.characterKey,
          });
          if (result?.storage?.data?.[this.userInfo.characterKey]) {
            this.vouchers = JSON.parse(
              result.storage.data[this.userInfo.characterKey],
            );
          }
        } catch (err) {
          console.error("Ошибка загрузки ваучеров:", err);
        }
      },

      updateCategoryTotal({ category, total, data }) {
        this.categoriesTotal[category] = total;
        if (data) Object.assign(this.bankData, data);
      },

      clearAll() {
        this.$refs.postsComponent?.clearData();
        this.$refs.activityComponent?.clearData();
        this.$refs.advertisingComponent?.clearData();
        this.$refs.onlineComponent?.clearData();
        this.$refs.graphicsComponent?.clearData();
        this.categoriesTotal = {};
        this.bankData = {
          posts: [],
          open_eps: [],
          closed_eps: [],
          posts_total: 0,
          messages: 0,
          positive: 0,
          reputation: 0,
        };
      },

      async submitForm() {
        const overlay = document.getElementById("submit-overlay");
        if (overlay) overlay.style.display = "flex";
        if (!this.userInfo.characterKey) await this.loadUserBalance();

        const textarea = document.getElementById("main-reply");
        const delay = () => new Promise((r) => setTimeout(r, 100));
        let formText = "";
        let techInfo = ""; // техническая информация

        try {
          // Токены
          if (this.totalTokens > 0) {
            const oldBalance = +this.userInfo.balance || 0;
            const newBalance = oldBalance + +this.totalTokens;
            await mybbAPI.call("storage.set", {
              key: this.userInfo.characterKey,
              value: String(newBalance),
              app_id: 9,
              token: window.ForumAPITicket,
            });
            await delay();
            window.UserFld2 = String(newBalance);
            this.userInfo.balance = newBalance;
            formText += `токенов было: ${oldBalance} -> стало: ${newBalance}\n`;
          }

          // POST IT
          const usedPostIt = this.$refs.postsComponent?.postItUsed || 0;
          if (usedPostIt > 0) {
            let vouchers = {};
            try {
              const r = await mybbAPI.call("storage.get", {
                app_id: 3,
                key: this.userInfo.characterKey,
                token: window.ForumAPITicket,
              });
              if (r?.storage?.data?.[this.userInfo.characterKey])
                vouchers = JSON.parse(
                  r.storage.data[this.userInfo.characterKey],
                );
            } catch (err) {}
            await delay();
            vouchers.post_it = (vouchers.post_it || 0) — usedPostIt;
            if (vouchers.post_it <= 0) delete vouchers.post_it;
            try {
              await mybbAPI.call("storage.delete", {
                key: this.userInfo.characterKey,
                app_id: 3,
                token: window.ForumAPITicket,
              });
            } catch (err) {}
            await delay();
            if (Object.keys(vouchers).length > 0) {
              await mybbAPI.call("storage.set", {
                key: this.userInfo.characterKey,
                value: JSON.stringify(vouchers),
                action: "append",
                app_id: 3,
                token: window.ForumAPITicket,
              });
            }
            await delay();
            this.vouchers = vouchers;
            formText += `использовано POST IT: ${usedPostIt}, осталось: ${vouchers.post_it || 0}\n`;
          }

          // === СБОР ТЕХНИЧЕСКОЙ ИНФОРМАЦИИ ===
          // bank_last_post
          if (this.bankData.posts && this.bankData.posts.length > 0) {
            let oldMax = 0;
            try {
              const r = await mybbAPI.storageGet("bank_last_post");
              if (r?.storage?.data?.bank_last_post)
                oldMax = parseInt(r.storage.data.bank_last_post);
            } catch (err) {}
            await delay();
            const newMax = Math.max(...this.bankData.posts.map(Number));
            if (newMax > oldMax) {
              await mybbAPI.storageSet("bank_last_post", String(newMax));
              await delay();
              techInfo += `bank_last_post: был ${oldMax} -> стал ${newMax}\n`;
            }
          }

          // bank_open_eps
          if (this.bankData.open_eps && this.bankData.open_eps.length > 0) {
            let oldData = [];
            try {

+1


Вы здесь » gemini beta v0.1 » SIMULATION » ссука


Рейтинг форумов | Создать форум бесплатно