From ec482102d0fd1edb93e9d119ddc380499f5b6167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=BD=D0=B8=D0=BD=20=D0=95=D0=B3=D0=BE?= =?UTF-8?q?=D1=80?= Date: Thu, 18 Mar 2021 23:28:20 +1000 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20/=20=D0=9A=D0=BB=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D0=B5=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.json | 10 ++- index.mjs | 27 ++++--- modules/Widget.mjs | 181 ++++++++++++++++++++---------------------- modules/functions.mjs | 27 ------- modules/utils.mjs | 44 ++++++++++ package-lock.json | 30 ++++++- package.json | 2 +- 7 files changed, 178 insertions(+), 143 deletions(-) delete mode 100644 modules/functions.mjs create mode 100644 modules/utils.mjs diff --git a/config.json b/config.json index 896f1a6..535235b 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,9 @@ { - "widget_token": "", - "guild_id": 1, - "icon_id": null + "clusters": [ + { + "widget_token": "", + "guild_id": 1, + "icon_id": "" + } + ] } \ No newline at end of file diff --git a/index.mjs b/index.mjs index 19d6c9e..16a1c78 100644 --- a/index.mjs +++ b/index.mjs @@ -1,20 +1,21 @@ import { Widget } from "./modules/Widget"; -const updateWidget = () => { - const widget = new Widget(); +import config from "./config.json"; - widget.updateWidget(); -}; +config.clusters.forEach((cluster, index) => { + cluster.index = index + 1; -updateWidget(); + const updateWidget = () => { + const widget = new Widget(cluster); -setInterval(updateWidget, 15 * 60 * 1000); // Обновление виджета происходит раз в 15 минут, так как информация о гильдии обновляется раз в 10-15 минут. + widget.updateWidget(); + }; -console.log("[VimeWidget] Запущен!"); + updateWidget(); -// -// Made with ♥ by MrZillaGold (https://vk.com/mrzillagold) -// -// VimeWidget доступен по лицензии Creative Commons «Attribution-NonCommercial-ShareAlike» -// («Атрибуция — Некоммерческое использование — На тех же условиях») 4.0 Всемирная. -// + setInterval(updateWidget, 15 * 60 * 1000); + // Обновление виджета происходит раз в 15 минут, так как информация о гильдии обновляется раз в 10-15 минут. + +}); + +console.log("[VimeWidget] Запущен!"); \ No newline at end of file diff --git a/modules/Widget.mjs b/modules/Widget.mjs index 80a1499..f7eca6e 100644 --- a/modules/Widget.mjs +++ b/modules/Widget.mjs @@ -1,77 +1,69 @@ import axios from "axios"; -import config from "../config"; -import { declOfNum, getDate } from "./functions"; -const { guild_id, widget_token, icon_id } = config; +import { declOfNum, humanizeDate, ranks, TOP_TYPES } from "./utils.mjs"; const API_ENDPOINT = "https://api.vimeworld.ru"; -const ranks = new Map([ - ["PLAYER", ""], - ["VIP", "VIP"], - ["PREMIUM", "Premium"], - ["HOLY", "Holy"], - ["IMMORTAL", "Immortal"], - ["BUILDER", "Билдер"], - ["MAPLEAD", "Гл. билдер"], - ["YOUTUBE", "YouTube"], - ["DEV", "Dev"], - ["ORGANIZER", "Организатор"], - ["MODER", "Модер"], - ["WARDEN", "Модер"], - ["CHIEF", "Гл. модер"], - ["ADMIN", "Гл. админ"] -]); - export class Widget { - state = { - guild: null, - tops: [], - widget: { // https://vk.com/dev/objects/appWidget?f=2.%20List - title: "Гильдия - ", - rows: [ - { - title: "Лидер: ", - descr: "", - address: "", - time: "Создана: ", - text: "", - icon_id - }, - { - title: "Больше всего заработали опыта:", - text: "", - icon_id - }, - { - title: "Больше всего вложили коинов:", - text: "", - icon_id - } - ], - more: "Хочу себе такой виджет", - more_url: "https://vk.com/@mrzillagold-vimewidget" - } + guild = null; + tops = {}; + + widget = { // https://vk.com/dev/objects/appWidget?f=2.%20List + title: "Гильдия - ", + rows: [ + { + title: "Лидер: ", + descr: "", + address: "", + time: "Создана: ", + text: "" + }, + { + title: "Больше всего заработали опыта:", + text: "" + }, + { + title: "Больше всего вложили коинов:", + text: "" + } + ], + more: "Хочу себе такой виджет", + more_url: "https://vk.com/@mrzillagold-vimewidget" }; + constructor(cluster) { + this.cluster = cluster; + + const { icon_id } = cluster; + + this.widget.rows = this.widget.rows.map((row) => { + row.icon_id = icon_id || null; + + return row; + }); + } + + async updateWidget() { - let { guild, widget, tops } = this.state; + let { guild, widget, tops, cluster: { guild_id, widget_token, index } } = this; await axios.get(`${API_ENDPOINT}/guild/get?id=${guild_id}`) // Получаем информацию о гильдии с API VimeWorld .then(({ data }) => guild = data) - .catch(console.log); + .catch(console.error); if (guild) { - if (guild.error) { - return console.log(`[!] Ошибка при получении информации о гильдии.\n\n${guild.error.error_code} - ${guild.error.error_msg}`); + const error = guild.error; + + if (error) { + return console.error(`[!] Ошибка при получении информации о гильдии в кластере #${index}.\n\n${error.error_code} - ${error.error_msg}`); } widget.title += guild.name; // Имя гильдии в шапку виджета - widget.rows[0].title += guild.members.filter(member => member.status === "LEADER")[0].user.username; // Ищем лидера гильдии + widget.rows[0].title += guild.members.filter((member) => member.status === "LEADER")[0].user.username; // Ищем лидера гильдии - widget.rows[0].time += getDate(guild.created); // Превращаем дату создания гильдии в человеческий вид и записываем в виджет + widget.rows[0].time += humanizeDate(guild.created); // Превращаем дату создания гильдии в человеческий вид и записываем в виджет widget.rows[0].address += `${declOfNum(guild.members.length, ["Участник", "Участника", "Участников"])}: ${guild.members.length}/${20 + guild.perks.MEMBERS.level * 5} | `; // Количество участников гильдии widget.rows[0].address += `Последний вступивший: ${guild.members.sort((a, b) => b.joined - a.joined)[0].user.username}`; // Последний вступивший в гильдию @@ -82,85 +74,80 @@ export class Widget { await this.getTop(); // Получаем индекс гильдии в топе по уровню и коинам - widget.rows[0].descr += `Место топа по опыту: ${tops[0] || "Нет"} | Место топа по коинам: ${tops[1] || "Нет"}`; // Глобальный топ по коинам и уровню + widget.rows[0].descr += `Место топа по опыту: ${tops.level || "Нет"} | Место топа по коинам: ${tops.total_coins || "Нет"}`; // Глобальный топ по коинам и уровню await this.sortTops(guild); // Формируем топ по коинам и уровню внутри гильдии axios.get(`https://api.vk.com/method/appWidgets.update?type=list&code=return ${encodeURIComponent(JSON.stringify(widget))}%3B&access_token=${widget_token}&v=5.103`) // Обновляем виджет - .then((res) => { - const error = res.data.error; - + .then(({ data: { error } }) => { if (error) { - return console.log(`[!] Ошибка при обновлении виджета:\nКод ошибки: ${error.error_code}\n${error.error_msg}`); + return console.log(`[!] Ошибка ВКонтакте при обновлении виджета!\nКод ошибки: ${error.error_code}\n${error.error_msg}`); } - console.log("[VimeWidget] Виджет обновлён!") + console.log("[VimeWidget] Виджет обновлён!"); }) - .catch(error => console.log(`[!] Произошла ошибка при обновлении виджета!\n${error}`)); + .catch((error) => console.error(`[!] Произошла ошибка при обновлении виджета в кластере #${index}!\n${error}`)); } else { - return console.log("[!] Не удалось получить информацию о гильдии, возможно произошла ошибка.") + return console.error(`[!] Не удалось получить информацию о гильдии в кластере #${index}, возможно произошла ошибка.`); } } - async getTop() { - const { tops } = this.state; - - const topTypes = [ - { - type: "level" - }, - { - type: "total_coins" - } - ]; - - for (let i = 0; i <= topTypes.length - 1; i++) { - let top = await axios.get(`${API_ENDPOINT}/leaderboard/get/guild/${topTypes[i].type}?size=1000`) - .then(({ data }) => data); - - if (top.records[0]) { - top = top.records; - - const index = top.findIndex(element => element.id === guild_id); - - tops.push(index !== -1 ? index + 1 : null); - } - } + getTop() { + const { guild_id } = this.cluster; + + return Promise.allSettled( + TOP_TYPES.map((type) => + axios.get(`${API_ENDPOINT}/leaderboard/get/guild/${type}?size=1000`) + .then(({ data }) => { + data.type = type; + + return data; + }) + ) + ) + .then((results) => { + results.forEach(({ status, value: top }) => { + if (status === "fulfilled" && top.records[0]) { + const index = top.records.findIndex(element => element.id === guild_id); + + this.tops[top.type] = index !== -1 ? index + 1 : null; + } + }); + }); } sortTops(guild) { - let { widget } = this.state; - const sorts = [ { - forEach: member => { - widget.rows[1].text += `${this.getRank(member.user.rank)} ${member.user.username} - ${member.guildExp}\n` - }, + forEach: (member) => + this.widget.rows[1].text += `${this.getRank(member.user.rank)} ${member.user.username} - ${member.guildExp}\n`, sort: (a, b) => b.guildExp - a.guildExp }, { - forEach: member => { - widget.rows[2].text += `${this.getRank(member.user.rank)} ${member.user.username} - ${member.guildCoins}\n` - }, + forEach: (member) => + this.widget.rows[2].text += `${this.getRank(member.user.rank)} ${member.user.username} - ${member.guildCoins}\n`, sort: (a, b) => b.guildCoins - a.guildCoins } ]; - sorts.forEach(functions => guild.members.sort(functions.sort).slice(0, 3).forEach(functions.forEach)) + sorts.forEach(({ sort, forEach }) => + guild.members.sort(sort) + .slice(0, 3) + .forEach(forEach) + ); } getGuildExp(guild) { let totalExp = guild.totalExp; for (let i = 0; i <= guild.level - 2; i++) { - totalExp = totalExp - (50000 + i * 10000); + totalExp -= (50000 + i * 10000); } return totalExp; } getRank(rank) { - rank = ranks.get(rank); return rank ? `[${rank}]` : ""; diff --git a/modules/functions.mjs b/modules/functions.mjs deleted file mode 100644 index 37d0464..0000000 --- a/modules/functions.mjs +++ /dev/null @@ -1,27 +0,0 @@ -// https://gist.github.com/realmyst/1262561#gistcomment-2299442 -function declOfNum(n, titles) { - return titles[(n % 10 === 1 && n % 100 !== 11) ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2] -} - -function getDate(time) { - const currentDate = new Date(time * 1000); - - const day = currentDate.getDate(); - const month = currentDate.getMonth() + 1; - const year = currentDate.getFullYear(); - - const hours = currentDate.getUTCHours() + 3; - const minutes = currentDate.getMinutes(); - const seconds = currentDate.getSeconds(); - - return `${pad(day)}.${pad(month)}.${year} ${pad(hours)}:${pad(minutes)}:${pad(seconds)}`; - - function pad(s){ - return (s > 9 ? "" : "0") + s; - } -} - -export { - declOfNum, - getDate -}; \ No newline at end of file diff --git a/modules/utils.mjs b/modules/utils.mjs new file mode 100644 index 0000000..aeb197a --- /dev/null +++ b/modules/utils.mjs @@ -0,0 +1,44 @@ +// https://gist.github.com/realmyst/1262561#gistcomment-2299442 +export function declOfNum(n, titles) { + return titles[(n % 10 === 1 && n % 100 !== 11) ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2]; +} + +export function humanizeDate(time) { + const currentDate = new Date(time * 1000); + + const day = currentDate.getDate(); + const month = currentDate.getMonth() + 1; + const year = currentDate.getFullYear(); + + const hours = currentDate.getUTCHours() + 3; + const minutes = currentDate.getMinutes(); + const seconds = currentDate.getSeconds(); + + return `${pad(day)}.${pad(month)}.${year} ${pad(hours)}:${pad(minutes)}:${pad(seconds)}`; +} + +export function pad(number) { + return (number > 9 ? "" : "0") + number; +} + +export const TOP_TYPES = [ + "level", + "total_coins" +]; + +export const ranks = new Map([ + ["PLAYER", ""], + ["VIP", "VIP"], + ["PREMIUM", "Premium"], + ["HOLY", "Holy"], + ["IMMORTAL", "Immortal"], + ["BUILDER", "Билдер"], + ["MAPLEAD", "Гл. билдер"], + ["YOUTUBE", "YouTube"], + ["DEV", "Dev"], + ["ORGANIZER", "Организатор"], + ["MODER", "Модер"], + ["WARDEN", "Модер"], + ["CHIEF", "Гл. модер"], + ["ADMIN", "Гл. админ"] +]); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cbb4f9d..1245fec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,8 +1,34 @@ { "name": "vimewidget", - "version": "1.0.1", - "lockfileVersion": 1, + "version": "1.0.2", + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "vimewidget", + "version": "1.0.2", + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "axios": "^0.21.1" + } + }, + "node_modules/axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dependencies": { + "follow-redirects": "^1.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", + "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==", + "engines": { + "node": ">=4.0" + } + } + }, "dependencies": { "axios": { "version": "0.21.1", diff --git a/package.json b/package.json index 5d51073..25ff1a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vimewidget", - "version": "1.0.1", + "version": "1.0.2", "description": "Виджет с информацией о гильдии VimeWorld для группы VK.COM", "main": "index.mjs", "scripts": {