"""
█▀▀ ▄▀█ █▄▀ █▀▀ █▀ ▀█▀ █░█░█ █ ▀▄▀
█▄▄ █▀█ █░█ ██▄ ▄█ ░█░ ▀▄▀▄▀ █ █░█
Copyleft 2022 t.me/CakesTwix
This program is free software; you can redistribute it and/or modify
"""
__version__ = (1, 2, 0)
# requires: aiohttp timeago
# meta pic: https://b.thumbs.redditmedia.com/-cDkj6PuQHqdLEhPh1JYsYplTArOOUuBnKs5FC8sgKs.png
# meta developer: @cakestwix_mods
# scope: inline
import logging
import uuid
from datetime import datetime
from typing import Union
import aiohttp
import timeago
from .. import loader, utils # noqa
logger = logging.getLogger(__name__)
# From Hikka https://github.com/hikariatama/Hikka/blob/master/hikka/utils.py#L459-L461
def chunks(_list: Union[list, tuple, set], n: int, /) -> list:
"""Split provided `_list` into chunks of `n`"""
return [_list[i : i + n] for i in range(0, len(_list), n)]
@loader.tds
class InlineWynnCraftInfoMod(loader.Module):
"""A module for displaying player information on the WynnCraft rpg server"""
strings = {
"name": "InlineWynnCraft",
"error_message": "🚫 This entity does not exist or you entered it incorrectly",
"about_user": "Available player information {} {}\n",
"rank_user": "Rank: ",
"last_join_user": "\nLast Seen: ",
"first_join_user": "\nFirst Join: ",
"professions_user": "\nProfessions: ",
"guild_user": "\nGuild: {} ({})",
"general_info_user": "\n\n👉 General Information",
"chestsFound": "\n 🔍 Chests Found: ",
"blocksWalked": "\n 🚶♀️ Walked blocks: ",
"mobsKilled": "\n 🐗 Mobs Killed: ",
"itemsIdentified": "\n 🧰 Items analyzed: ",
"logins": "\n 🎟 Logins: ",
"discoveries": "\n 🔎 Discoveries: ",
"dungeons": "\n 🪨 Dungeons: ",
"raids": "\n ⚔️ Raids: ",
"quests": "\n 📕 Quests: ",
"eventsWon": "\n 🎊 Events Won: ",
"pvp_user": "\n\n🗡 PVP: ",
"deaths": "\n ☠️ Deaths: ",
"kills": "\n ⚔️ Kills: ",
"completed": "\n✅ Completed",
"skills": "\n\n🔧 Skills",
"strength": "\n 💪 Strength: ",
"dexterity": "\n 💨 Dexterity: ",
"intelligence": "\n 🧐 Intelligence: ",
"defense": "\n 🧱 Defence: ",
"defence": "\n 🛡 Defence: ",
"agility": "\n 🤸♂️ Agility: ",
"professions": "\n\n👷♀️ Professions: ",
"alchemism": "\n 💧 Alchemism: ",
"armouring": "\n 🛡 Armouring: ",
"combat": "\n ⚔️ Combat: ",
"cooking": "\n 🍽 Cooking: ",
"farming": "\n 🌾️ Farming: ",
"fishing": "\n 🎣 Fishing: ",
"jeweling": "\n 💎 Jeweling: ",
"mining": "\n ⛏ Mining: ",
"scribing": "\n 🖋 Scribing: ",
"tailoring": "\n 🪡 Tailoring: ",
"weaponsmithing": "\n 🗡️ Weaponsmithing: ",
"woodcutting": "\n 🪓 Woodcutting: ",
"woodworking": "\n 🌳️ Woodworking: ",
"btn_back": "Back",
"btn_close": "Close",
"top_list": "Top list:",
}
strings_ru = {
"error_message": "🚫 Этот объект не существует или вы ввели его неправильно",
"about_user": "Доступная информация об игроке {} {}\n",
"rank_user": "Ранг: ",
"last_join_user": "\nПоследнее посещение: ",
"first_join_user": "\nПервое присоединение: ",
"professions_user": "\nПрофессии: ",
"guild_user": "\nГильдия: {} ({})",
"general_info_user": "\n\n👉 Главная Информация",
"chestsFound": "\n 🔍 Найдено сундуков: ",
"blocksWalked": "\n 🚶♀️ Пройдено блоков: ",
"mobsKilled": "\n 🐗 Мобов убито: ",
"itemsIdentified": "\n 🧰 Проанализировано предметов: ",
"logins": "\n 🎟 Заходил на сервер: ",
"discoveries": "\n 🔎 Открытий: ",
"dungeons": "\n 🪨 Подземелья: ",
"raids": "\n ⚔️ Рейды: ",
"quests": "\n 📕 Квесты: ",
"eventsWon": "\n 🎊 Выигранные события: ",
"pvp_user": "\n\n🗡 PVP: ",
"deaths": "\n ☠️ Смертей: ",
"kills": "\n ⚔️ Убийств: ",
"completed": "\n✅ Завершено",
"skills": "\n\n🔧 Навыки",
"strength": "\n 💪 Прочность: ",
"dexterity": "\n 💨 Ловкость: ",
"intelligence": "\n 🧐 Интеллект: ",
"defense": "\n 🧱 Оборона: ",
"defence": "\n 🛡 Защита: ",
"agility": "\n 🤸♂️ Ловкость: ",
"professions": "\n\n👷♀️ Профессии: ",
"alchemism": "\n 💧 Алхимизм: ",
"armouring": "\n 🛡 Бронирование: ",
"combat": "\n ⚔️ Бой: ",
"cooking": "\n 🍽 Готовка: ",
"farming": "\n 🌾️ Сельское хозяйство: ",
"fishing": "\n 🎣 Рыбалка: ",
"jeweling": "\n 💎 Ювелирное дело: ",
"mining": "\n ⛏ Добыча: ",
"scribing": "\n 🖋 Скрайбирование: ",
"tailoring": "\n 🪡 Портняжное дело: ",
"weaponsmithing": "\n 🗡️ Оружейное дело: ",
"woodcutting": "\n 🪓 Резьба по дереву: ",
"woodworking": "\n 🌳️ Деревообработка: ",
"btn_back": "Назад",
"btn_close": "Закрыть",
"top_list": "Топ:",
}
base_url = "https://mc-heads.net/minecraft/profile/"
wynncraft_api = "https://api.wynncraft.com/v2/"
def general_info_builder(self, user) -> str:
text = self.strings["about_user"].format(
f'🟢 ({user["meta"]["location"]["server"]})'
if user["meta"]["location"]["online"]
else "🔴",
user["username"],
)
text += (
self.strings["rank_user"]
+ user["rank"]
+ (
f' ({user["meta"]["tag"]["value"]})'
if user["meta"]["tag"]["value"]
else ""
)
)
# Guild
text += (
self.strings["guild_user"].format(
user["guild"]["name"], user["guild"]["rank"]
)
if user["guild"]["name"]
else ""
)
text += self.strings["last_join_user"] + timeago.format(
datetime.strptime(user["meta"]["lastJoin"][:-5], "%Y-%m-%dT%H:%M:%S"),
datetime.now(),
)
text += self.strings["first_join_user"] + timeago.format(
datetime.strptime(user["meta"]["firstJoin"][:-5], "%Y-%m-%dT%H:%M:%S"),
datetime.now(),
)
# Global stuff
text += self.strings["general_info_user"]
for general_stuff in user["global"]:
if general_stuff in ["pvp", "totalLevel"]:
continue
text += (
self.strings[general_stuff]
+ f"{user['global'][general_stuff]}"
)
# PvP
text += self.strings["pvp_user"]
text += self.strings["kills"] + f"{user['global']['pvp']['kills']}"
text += (
self.strings["deaths"] + f"{user['global']['pvp']['deaths']}"
)
return text
def class_info_builder(self, class_) -> str:
text = self.strings["about_user"].format(
"".join([i for i in class_["name"].title() if not i.isdigit()]),
f"[{class_['level']}]",
)
# Completed stuff
text += self.strings["completed"]
for some_item in class_:
if some_item not in ["dungeons", "raids", "quests"]:
continue
text += (
self.strings[some_item]
+ f"{class_[some_item]['completed']}"
)
# Some stuff
text += self.strings["general_info_user"]
for some_item in class_:
if some_item in [
"itemsIdentified",
"mobsKilled",
"chestsFound",
"blocksWalked",
"logins",
"discoveries",
"eventsWon",
]:
text += self.strings[some_item] + f"{class_[some_item]}"
# PvP
text += self.strings["pvp_user"]
text += self.strings["kills"] + f"{class_['pvp']['kills']}"
text += self.strings["deaths"] + f"{class_['pvp']['deaths']}"
# Skills stuff
text += self.strings["skills"]
for skill in class_["skills"]:
text += self.strings[skill] + f"{class_['skills'][skill]}"
# Professions stuff
text += self.strings["professions"]
for skill in class_["professions"]:
text += self.strings[skill] + f"{class_['professions'][skill]['level']} ({class_['professions'][skill]['xp']})"
return text
def keyboard_class_builder(self, user):
return [
{
"text": f'[{class_["level"]}] {"".join([i for i in class_["name"].title() if not i.isdigit()])}',
"callback": self.inline__get_class,
"args": [class_, user],
}
for class_ in user["classes"]
]
async def wucheckcmd(self, message):
"""Check user by username"""
if not (args := utils.get_args_raw(message)):
return
async with aiohttp.ClientSession() as session:
async with session.get(f"{self.base_url}{args}") as get:
if get.status != 200:
return await utils.answer(message, self.strings["error_message"])
uuid_str = (await get.json())["id"]
# Get info about user
async with session.get(
self.wynncraft_api + "player/{}/stats".format(uuid.UUID(hex=uuid_str))
) as get:
logger.debug(str(await get.json()))
if (await get.json())["code"] != 200:
return await utils.answer(message, self.strings["error_message"])
user = (await get.json())["data"][0]
await self.inline.form(
text=self.general_info_builder(user),
message=message,
reply_markup=chunks(self.keyboard_class_builder(user), 3),
photo=f"https://wynndata.tk/gen/stats/{args}.png",
force_me=False, # optional: Allow other users to access form (all)
)
async def wplayertopcmd(self, message):
"""Top players"""
async with aiohttp.ClientSession() as session:
async with session.get(f"{self.wynncraft_api}leaderboards/player/overall/all") as get:
if get.status != 200:
return await utils.answer(message, self.strings["error_message"])
top_list = list(reversed(chunks((await get.json())["data"],10))) # oh no, cringe code
string = f"{self.strings['top_list']}\n\n"
for player in reversed(top_list[0]):
string += f"╭ {player['name']} 🎮\n"
string += f"╰─ LvL: {player['level']} / XP: {player['xp']} / Time: {player['minPlayed']}\n"
await self.inline.form(text=string, message=message, reply_markup=chunks([{"text": str(i + 1), "callback": self.inline__toplayer, "args": (top_list, i),} for i in range(10)], 5), force_me=False)
async def inline__toplayer(self, call, top_list: list, num: int):
string = f"{self.strings['top_list']}\n\n"
for player in reversed(top_list[num]):
string += f"╭ {player['name']} 🎮\n"
string += f"╰─ LvL: {player['level']} / XP: {player['xp']} / Time: {player['minPlayed']}\n"
await call.edit(text=string, reply_markup=chunks([{"text": str(i + 1), "callback": self.inline__toplayer, "args": (top_list, i),} for i in range(10)], 5), force_me=False)
async def inline__get_class(self, call, class_, player):
await call.edit(
text=self.class_info_builder(class_),
reply_markup=[
{
"text": self.strings["btn_back"],
"callback": self.inline__back,
"args": [player],
}
],
)
async def inline__back(self, call, player):
await call.edit(
text=self.general_info_builder(player),
reply_markup=chunks(self.keyboard_class_builder(player), 3),
)