ссука
Сообщений 1 страница 4 из 4
Поделиться22026-06-18 15:54:19
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&user_id=\d+&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&user_id=\d+&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;
Поделиться32026-06-28 16:02:30
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(/</g, "<")
.replace(/"/g, '"')
.replace(/>/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);
Поделиться42026-06-28 16:51:39
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(/</g, "<")
.replace(/"/g, '"')
.replace(/>/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 {


