wynncraft-bot/cogs/wynncraft/wynncraftcog.py

576 lines
40 KiB
Python

import asyncio
import itertools
import math
from redbot.core import commands
from redbot.core import Config, checks
from discord import TextChannel
import discord
import traceback
import requests
from datetime import datetime, timezone, timedelta
def utc_to_local(utc_dt):
return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
class WynncraftCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=48775419874)
default_guild = {
"guild_name": None, # OK
"ping_levels": False, # OK
"ping_channel": None, # OK
"role_levels": [
750098524976185525, # 0-9
750098542802239488, # 10-19
750098546883166292, # 20-29
750098551756947496, # 30-39
750098557444292740, # 40-49
750098560732889088, # 50-59
750098568194424872, # 60-69
750098573353418822, # 70-79
750098579535822918, # 80-89
750098584082448434, # 90-99
750098593976811550, # 100+
],
"role_class": {
"mage": 750097053840965752,
"assassin": 750097058362425417,
"shaman": 750097056093438012,
"archer": 750097050867204246,
"warrior": 750097044433272833
},
"update_msg": 750124413529358467,
"role_professions": {
"farming": [
743544309059551292, # 0-9
750103671077077004, # 10-19
750103673178423397, # 20-29
750103675195752528, # 30-39
750103677011755080, # 40-49
750103681046675527, # 50-59
750103683349479452, # 60-69
750103692182552667, # 70-79
750103687589920789, # 80-89
750103685295767734, # 90-99
750103689536077966, # 100-109
750103679314558980, # 110
],
"mining": [
743544348821553305, # 0-9
750105003464851508, # 10-19
750105011173851176, # 20-29
750105019260469288, # 30-39
750104996204380211, # 40-49
750105001522888874, # 50-59
750105005759135805, # 60-69
750105016433377380, # 70-79
750105008644554783, # 80-89
750105013749153792, # 90-99
750104998670762075, # 100-109
750104994392309801, # 110
],
"fishing": [
743544397152518146, # 0-9
750105742127923303, # 10-19
750105731482779779, # 20-29
750105744774529124, # 30-39
750105734615662672, # 40-49
750105754739933345, # 50-59
750105729284964472, # 60-69
750105749899837551, # 70-79
750105752374345769, # 80-89
750105747219546122, # 90-99
750105737254011051, # 100-109
750105739716067488, # 110
],
"woodcutting": [
743920688607264818, # 0-9
750106425174261771, # 10-19
750106428253012048, # 20-29
750106431033835570, # 30-39
750106433785430126, # 40-49
750106436419452949, # 50-59
750106438931579032, # 60-69
750106441557475430, # 70-79
750106444493488129, # 80-89
750106446883979326, # 90-99
750106449790631937, # 100-109
750106452277985301, # 110
]
}
}
default_global = {
"log": True,
"grind_spots": [
{"minlevel": 1, "maxlevel": 10, "location": "-465, -1575"},
{"minlevel": 11, "maxlevel": 15, "location": "610, -1550"},
{"minlevel": 16, "maxlevel": 17, "location": "-600, -1200"},
{"minlevel": 18, "maxlevel": 20, "location": "545, -1300"},
{"minlevel": 21, "maxlevel": 25, "location": "-30, -1980"},
{"minlevel": 26, "maxlevel": 30, "location": "640, -1735"},
{"minlevel": 31, "maxlevel": 35, "location": "1500, -1460"},
{"minlevel": 36, "maxlevel": 40, "location": "950, -2255"},
{"minlevel": 41, "maxlevel": 45, "location": "-2120, -4720"},
{"minlevel": 46, "maxlevel": 50, "location": "-350, -280"},
{"minlevel": 51, "maxlevel": 55, "location": "-510, -840"},
{"minlevel": 56, "maxlevel": 60, "location": "-620, -1000"},
{"minlevel": 61, "maxlevel": 65, "location": "-500, -810"},
{"minlevel": 66, "maxlevel": 70, "location": "-1100, -5590"},
{"minlevel": 71, "maxlevel": 75, "location": "-10, -4935"},
{"minlevel": 76, "maxlevel": 80, "location": "-900, -5600"},
{"minlevel": 81, "maxlevel": 85, "location": "410, -5085"},
{"minlevel": 86, "maxlevel": 90, "location": "780, -5370"},
{"minlevel": 91, "maxlevel": 95, "location": "1500, -5560"},
{"minlevel": 96, "maxlevel": 100, "location": "1400, -5060"},
{"minlevel": 101, "maxlevel": 106, "location": "1170, -4385"},
],
"dungeons": [
{"minlevel": 9, "name": "Decrepit Sewers", "url": "https://wynncraft.gamepedia.com/Decrepit_Sewers", "location": "-919, -1883"},
{"minlevel": 18, "name": "Infested Pit", "url": "https://wynncraft.gamepedia.com/Infested_Pit", "location": "-171, -1820"},
{"minlevel": 24, "name": "Lost Sanctuary", "url": "https://wynncraft.gamepedia.com/Lost_Sanctuary", "location": "-262, -1045"},
{"minlevel": 27, "name": "Underworld Crypt", "url": "https://wynncraft.gamepedia.com/Underworld_Crypt", "location": "227, -1950"},
{"minlevel": 36, "name": "Sand-Swept Tomb", "url": "https://wynncraft.gamepedia.com/Sand-Swept_Tomb", "location": "1409, -1830"},
{"minlevel": 45, "name": "Ice Barrows", "url": "https://wynncraft.gamepedia.com/Ice_Barrows", "location": "117, -667"},
{"minlevel": 54, "name": "Undergrowth Ruins", "url": "https://wynncraft.gamepedia.com/Undergrowth_Ruins", "location": "-660, -835"},
{"minlevel": 63, "name": "Galleon's Graveyard", "url": "https://wynncraft.gamepedia.com/Galleon%27s_Graveyard", "location": "-583, -3468"},
{"minlevel": 70, "name": "Corrupted Decrepit Sewers", "url": "https://wynncraft.gamepedia.com/Corrupted_Decrepit_Sewers", "location": "-860, -4900"},
{"minlevel": 74, "name": "Corrupted Infested Pit", "url": "https://wynncraft.gamepedia.com/Corrupted_Infested_Pit", "location": "-860, -4900"},
{"minlevel": 78, "name": "Corrupted Lost Sanctuary", "url": "https://wynncraft.gamepedia.com/Corrupted_Lost_Sanctuary", "location": "-860, -4900"},
{"minlevel": 82, "name": "Corrupted Underworld Crypt", "url": "https://wynncraft.gamepedia.com/Corrupted_Underworld_Crypt", "location": "-860, -4900"},
{"minlevel": 86, "name": "Corrupted Sand-Swept Tomb", "url": "https://wynncraft.gamepedia.com/Corrupted_Sand-Swept_Tomb", "location": "-860, -4900"},
{"minlevel": 90, "name": "Fallen Factory", "url": "https://wynncraft.gamepedia.com/Fallen_Factory", "location": "-1675, -2600"},
{"minlevel": 90, "name": "Corrupted Ice Barrows", "url": "https://wynncraft.gamepedia.com/Corrupted_Ice_Barrows", "location": "-860, -4900"},
{"minlevel": 94, "name": "Corrupted Undergrowth Ruins", "url": "https://wynncraft.gamepedia.com/Corrupted_Undergrowth_Ruins", "location": "-860, -4900"},
{"minlevel": 101, "name": "Eldritch Outlook", "url": "https://wynncraft.gamepedia.com/Eldritch_Outlook", "location": "-1310, -777"},
],
"quests": [
{"level": 1, "name": "King's Recruit", "url": "https://wynncraft.gamepedia.com/King%27s_Recruit"},
{"level": 1, "name": "Enzan's Brother", "url": "https://wynncraft.gamepedia.com/Enzan%27s_Brother"},
{"level": 2, "name": "Poisoning the Pest", "url": "https://wynncraft.gamepedia.com/Poisoning_the_Pest"},
{"level": 4, "name": "Cook Assistant", "url": "https://wynncraft.gamepedia.com/Cook_Assistant"},
{"level": 5, "name": "Tunnel Trouble", "url": "https://wynncraft.gamepedia.com/Tunnel_Trouble"},
{"level": 5, "name": "The Sewers of Ragni", "url": "https://wynncraft.gamepedia.com/The_Sewers_of_Ragni"},
{"level": 6, "name": "Infested Plants", "url": "https://wynncraft.gamepedia.com/Infested_Plants"},
{"level": 8, "name": "Underwater", "url": "https://wynncraft.gamepedia.com/Underwater"},
{"level": 10, "name": "Elemental Exercise", "url": "https://wynncraft.gamepedia.com/Elemental_Exercise"},
{"level": 12, "name": "Mushroom Man", "url": "https://wynncraft.gamepedia.com/Mushroom_Man"},
{"level": 14, "name": "Creeper Infiltration", "url": "https://wynncraft.gamepedia.com/Creeper_Infiltration"},
{"level": 14, "name": "Arachnids' Ascent", "url": "https://wynncraft.gamepedia.com/Arachnids%27_Ascent"},
{"level": 14, "name": "Deja Vu", "url": "https://wynncraft.gamepedia.com/Deja_Vu"},
{"level": 15, "name": "Potion Making", "url": "https://wynncraft.gamepedia.com/Potion_Making"},
{"level": 18, "name": "Maltics Well", "url": "https://wynncraft.gamepedia.com/Maltic%27s_Well"},
{"level": 20, "name": "Grave Digger", "url": "https://wynncraft.gamepedia.com/Grave_Digger"},
{"level": 21, "name": "Macabre Masquerade Hallowynn 2014", "url": "https://wynncraft.gamepedia.com/Macabre_Masquerade_%27%27Hallowynn_2014%27%27"},
{"level": 21, "name": "Studying the Corrupt", "url": "https://wynncraft.gamepedia.com/Studying_the_Corrupt"},
{"level": 23, "name": "Pit of the Dead", "url": "https://wynncraft.gamepedia.com/Pit_of_the_Dead_(Quest)"},
{"level": 23, "name": "Cluck Cluck", "url": "https://wynncraft.gamepedia.com/Cluck_Cluck"},
{"level": 24, "name": "Dwelling Walls", "url": "https://wynncraft.gamepedia.com/Dwelling_Walls"},
{"level": 24, "name": "The Dark Descent", "url": "https://wynncraft.gamepedia.com/The_Dark_Descent"},
{"level": 25, "name": "Recover the Past", "url": "https://wynncraft.gamepedia.com/Recover_the_Past"},
{"level": 26, "name": "Lost Tower", "url": "https://wynncraft.gamepedia.com/Lost_Tower"},
{"level": 26, "name": "The Corrupted Village", "url": "https://wynncraft.gamepedia.com/The_Corrupted_Village"},
{"level": 28, "name": "The Mercenary", "url": "https://wynncraft.gamepedia.com/The_Mercenary"},
{"level": 28, "name": "Misadventure on the Sea", "url": "https://wynncraft.gamepedia.com/Misadventure_on_the_Sea"},
{"level": 30, "name": "Craftmas Chaos", "url": "https://wynncraft.gamepedia.com/Craftmas_Chaos"},
{"level": 31, "name": "Green Gloop", "url": "https://wynncraft.gamepedia.com/Green_Gloop"},
{"level": 32, "name": "A Sandy Scandal", "url": "https://wynncraft.gamepedia.com/A_Sandy_Scandal"},
{"level": 33, "name": "Meaningful Holiday", "url": "https://wynncraft.gamepedia.com/Meaningful_Holiday"},
{"level": 33, "name": "Kingdom of Sand", "url": "https://wynncraft.gamepedia.com/Kingdom_of_Sand"},
{"level": 34, "name": "Stable Story", "url": "https://wynncraft.gamepedia.com/Stable_Story"},
{"level": 35, "name": "Tribal Aggression", "url": "https://wynncraft.gamepedia.com/Tribal_Aggression"},
{"level": 35, "name": "WynnExcavation Site A", "url": "https://wynncraft.gamepedia.com/WynnExcavation_Site_A"},
{"level": 36, "name": "Wrath of the Mummy", "url": "https://wynncraft.gamepedia.com/Wrath_of_the_Mummy"},
{"level": 38, "name": "Canyon Condor", "url": "https://wynncraft.gamepedia.com/Canyon_Condor"},
{"level": 39, "name": "Pirate's Trove", "url": "https://wynncraft.gamepedia.com/Pirate%27s_Trove"},
{"level": 40, "name": "Ice Nations", "url": "https://wynncraft.gamepedia.com/Ice_Nations"},
{"level": 40, "name": "Tower of Ascension", "url": "https://wynncraft.gamepedia.com/Tower_of_Ascension_(Quest)"},
{"level": 41, "name": "Heart of Llevigar", "url": "https://wynncraft.gamepedia.com/Heart_of_Llevigar"},
{"level": 42, "name": "Star Thief", "url": "https://wynncraft.gamepedia.com/Star_Thief"},
{"level": 42, "name": "Clearing the Camps", "url": "https://wynncraft.gamepedia.com/Clearing_the_Camps"},
{"level": 43, "name": "Underice", "url": "https://wynncraft.gamepedia.com/Underice"},
{"level": 43, "name": "Fate of the Fallen", "url": "https://wynncraft.gamepedia.com/Fate_of_the_Fallen"},
{"level": 44, "name": "Green Skinned Trouble", "url": "https://wynncraft.gamepedia.com/Green_Skinned_Trouble"},
{"level": 45, "name": "Bob's Lost Soul", "url": "https://wynncraft.gamepedia.com/Bob%27s_Lost_Soul"},
{"level": 46, "name": "Wynn Excavation Site B", "url": "https://wynncraft.gamepedia.com/WynnExcavation_Site_B"},
{"level": 48, "name": "Frost Bite", "url": "https://wynncraft.gamepedia.com/Frost_Bite"},
{"level": 49, "name": "An Iron Heart Part I", "url": "https://wynncraft.gamepedia.com/An_Iron_Heart_Part_I"},
{"level": 49, "name": "Rise of the Quartron", "url": "https://wynncraft.gamepedia.com/Rise_of_the_Quartron"},
{"level": 49, "name": "The House of Twain", "url": "https://wynncraft.gamepedia.com/The_House_of_Twain_(Quest)"},
{"level": 50, "name": "A Grave Mistake", "url": "https://wynncraft.gamepedia.com/A_Grave_Mistake"},
{"level": 51, "name": "The Maiden Tower", "url": "https://wynncraft.gamepedia.com/The_Maiden_Tower"},
{"level": 52, "name": "Jungle Fever", "url": "https://wynncraft.gamepedia.com/Jungle_Fever"},
{"level": 52, "name": "Crop Failure", "url": "https://wynncraft.gamepedia.com/Crop_Failure"},
{"level": 52, "name": "Corrupted Betrayal", "url": "https://wynncraft.gamepedia.com/Corrupted_Betrayal"},
{"level": 53, "name": "Master Piece", "url": "https://wynncraft.gamepedia.com/Master_Piece"},
{"level": 53, "name": "Death Whistle", "url": "https://wynncraft.gamepedia.com/Death_Whistle"},
{"level": 54, "name": "The Shadow of the Beast", "url": "https://wynncraft.gamepedia.com/The_Shadow_of_the_Beast"},
{"level": 54, "name": "The Worm Holes", "url": "https://wynncraft.gamepedia.com/The_Worm_Holes"},
{"level": 55, "name": "WynnExcavation Site C", "url": "https://wynncraft.gamepedia.com/WynnExcavation_Site_C"},
{"level": 55, "name": "Zhight Island", "url": "https://wynncraft.gamepedia.com/Zhight_Island_(Quest)"},
{"level": 57, "name": "The Passage", "url": "https://wynncraft.gamepedia.com/The_Passage_(Quest)"},
{"level": 58, "name": "An Iron Heart Part II", "url": "https://wynncraft.gamepedia.com/An_Iron_Heart_Part_II"},
{"level": 59, "name": "The Order of the Grook", "url": "https://wynncraft.gamepedia.com/The_Order_of_the_Grook"},
{"level": 60, "name": "Beneath the Depths", "url": "https://wynncraft.gamepedia.com/Beneath_the_Depths"},
{"level": 61, "name": "Redbeard's Booty", "url": "https://wynncraft.gamepedia.com/Redbeard%27s_Booty"},
{"level": 61, "name": "Reclaiming the House", "url": "https://wynncraft.gamepedia.com/Reclaiming_the_House"},
{"level": 62, "name": "Lost in the Jungle", "url": "https://wynncraft.gamepedia.com/Lost_in_the_Jungle"},
{"level": 62, "name": "Taproot", "url": "https://wynncraft.gamepedia.com/Taproot_(Quest)"},
{"level": 63, "name": "Out of My Mind", "url": "https://wynncraft.gamepedia.com/Out_of_my_Mind"},
{"level": 64, "name": "A Fighting Species", "url": "https://wynncraft.gamepedia.com/A_Fighting_Species"},
{"level": 64, "name": "The Headless Hunt", "url": "https://wynncraft.gamepedia.com/The_Headless_Hunt"},
{"level": 65, "name": "Lost Royalty", "url": "https://wynncraft.gamepedia.com/Lost_Royalty"},
{"level": 66, "name": "Lost Soles", "url": "https://wynncraft.gamepedia.com/Lost_Soles"},
{"level": 67, "name": "From the Mountains", "url": "https://wynncraft.gamepedia.com/From_the_Mountains"},
{"level": 67, "name": "Memory Paranoia", "url": "https://wynncraft.gamepedia.com/Memory_Paranoia"},
{"level": 68, "name": "Temple of Legends", "url": "https://wynncraft.gamepedia.com/Temple_of_the_Legends_(Quest)"},
{"level": 69, "name": "Grand Youth", "url": "https://wynncraft.gamepedia.com/Grand_Youth"},
{"level": 69, "name": "Lazarus Pit", "url": "https://wynncraft.gamepedia.com/Lazarus_Pit"},
{"level": 70, "name": "Haven Antiquity", "url": "https://wynncraft.gamepedia.com/Haven_Antiquity"},
{"level": 70, "name": "WynnExcavation Site D", "url": "https://wynncraft.gamepedia.com/WynnExcavation_Site_D"},
{"level": 70, "name": "Shattered Minds", "url": "https://wynncraft.gamepedia.com/Shattered_Minds"},
{"level": 71, "name": "Finding The Light", "url": "https://wynncraft.gamepedia.com/Finding_The_Light"},
{"level": 72, "name": "Forbidden Prison", "url": "https://wynncraft.gamepedia.com/Forbidden_Prison_(Quest)"},
{"level": 72, "name": "Eye of the Storm", "url": "https://wynncraft.gamepedia.com/Eye_of_the_Storm"},
{"level": 73, "name": "Hollow Sirene", "url": "https://wynncraft.gamepedia.com/Hollow_Sirene"},
{"level": 73, "name": "Troubled Tribesmen", "url": "https://wynncraft.gamepedia.com/Troubled_Tribesmen"},
{"level": 74, "name": "Acquiring Credentials", "url": "https://wynncraft.gamepedia.com/Acquiring_Credentials"},
{"level": 74, "name": "Reincarnation", "url": "https://wynncraft.gamepedia.com/Reincarnation"},
{"level": 74, "name": "Murder Mystery", "url": "https://wynncraft.gamepedia.com/Murder_Mystery"},
{"level": 74, "name": "The Realm of Light", "url": "https://wynncraft.gamepedia.com/The_Realm_of_Light_(Quest)"},
{"level": 75, "name": "Flight in Distress", "url": "https://wynncraft.gamepedia.com/Flight_in_Distress"},
{"level": 75, "name": "The Ultimate Weapon", "url": "https://wynncraft.gamepedia.com/The_Ultimate_Weapon"},
{"level": 76, "name": "The Bigger Picture", "url": "https://wynncraft.gamepedia.com/The_Bigger_Picture"},
{"level": 76, "name": "Aldorei's Secret Part I", "url": "https://wynncraft.gamepedia.com/Aldorei%27s_Secret_Part_I"},
{"level": 77, "name": "Purple and Blue", "url": "https://wynncraft.gamepedia.com/Purple_and_Blue"},
{"level": 77, "name": "The Hunger of Gerts Part 1", "url": "https://wynncraft.gamepedia.com/The_Hunger_of_Gerts_Part_1"},
{"level": 78, "name": "Aldorei's Secret Part II", "url": "https://wynncraft.gamepedia.com/Aldorei%27s_Secret_Part_II"},
{"level": 78, "name": "The Hunger of Gerts Part 2", "url": "https://wynncraft.gamepedia.com/The_Hunger_of_Gerts_Part_2"},
{"level": 79, "name": "Fallen Delivery", "url": "https://wynncraft.gamepedia.com/Fallen_Delivery"},
{"level": 80, "name": "???", "url": "https://wynncraft.gamepedia.com/%3F%3F%3F"},
{"level": 80, "name": "From the Bottom", "url": "https://wynncraft.gamepedia.com/From_the_Bottom"},
{"level": 80, "name": "General's Orders", "url": "https://wynncraft.gamepedia.com/General%27s_Orders"},
{"level": 80, "name": "The Qira Hive", "url": "https://wynncraft.gamepedia.com/The_Qira_Hive_(Quest)"},
{"level": 81, "name": "The Thanos Vaults", "url": "https://wynncraft.gamepedia.com/The_Thanos_Vaults"},
{"level": 82, "name": "The Belly of the Beast", "url": "https://wynncraft.gamepedia.com/The_Belly_of_the_Beast"},
{"level": 83, "name": "A Marauder's Dues", "url": "https://wynncraft.gamepedia.com/A_Marauder%27s_Dues"},
{"level": 83, "name": "The Envoy Part I", "url": "https://wynncraft.gamepedia.com/The_Envoy_Part_I"},
{"level": 84, "name": "The Canyon Guides", "url": "https://wynncraft.gamepedia.com/The_Canyon_Guides"},
{"level": 85, "name": "The Canary Calls", "url": "https://wynncraft.gamepedia.com/The_Canary_Calls"},
{"level": 85, "name": "The Lost", "url": "https://wynncraft.gamepedia.com/The_Lost"},
{"level": 86, "name": "Cowfusion", "url": "https://wynncraft.gamepedia.com/Cowfusion"},
{"level": 86, "name": "Desperate Metal", "url": "https://wynncraft.gamepedia.com/Desperate_Metal"},
{"level": 87, "name": "Beyond the Grave", "url": "https://wynncraft.gamepedia.com/Beyond_the_Grave"},
{"level": 87, "name": "Mixed Feelings", "url": "https://wynncraft.gamepedia.com/Mixed_Feelings"},
{"level": 88, "name": "The Hidden City", "url": "https://wynncraft.gamepedia.com/The_Hidden_City"},
{"level": 89, "name": "The Envoy Part II", "url": "https://wynncraft.gamepedia.com/The_Envoy_Part_II"},
{"level": 89, "name": "Enter the Dojo", "url": "https://wynncraft.gamepedia.com/Enter_the_Dojo"},
{"level": 90, "name": "Fantastic Voyage", "url": "https://wynncraft.gamepedia.com/Fantastic_Voyage"},
{"level": 91, "name": "Dwarves and Doguns Part I", "url": "https://wynncraft.gamepedia.com/Dwarves_and_Doguns_Part_I"},
{"level": 92, "name": "Dwarves and Doguns Part II", "url": "https://wynncraft.gamepedia.com/Dwarves_and_Doguns_Part_II"},
{"level": 93, "name": "Dwarves and Doguns Part III", "url": "https://wynncraft.gamepedia.com/Dwarves_and_Doguns_Part_III"},
{"level": 94, "name": "Dwarves and Doguns Part IV", "url": "https://wynncraft.gamepedia.com/Dwarves_and_Doguns_Part_IV"},
{"level": 95, "name": "One Thousand Meters Under", "url": "https://wynncraft.gamepedia.com/One_Thousand_Meters_Under"},
{"level": 96, "name": "Recipe For Disaster", "url": "https://wynncraft.gamepedia.com/Recipe_For_Disaster"},
{"level": 97, "name": "The Fortuneteller", "url": "https://wynncraft.gamepedia.com/The_Fortuneteller"},
{"level": 98, "name": "Royal Trials", "url": "https://wynncraft.gamepedia.com/Royal_Trials"},
{"level": 100, "name": "A Journey Beyond", "url": "https://wynncraft.gamepedia.com/A_Journey_Beyond"},
{"level": 100, "name": "The Olmic Rune", "url": "https://wynncraft.gamepedia.com/The_Olmic_Rune"},
{"level": 101, "name": "A Journey Further", "url": "https://wynncraft.gamepedia.com/A_Journey_Further"},
{"level": 102, "name": "Point of No Return", "url": "https://wynncraft.gamepedia.com/Point_of_No_Return"},
{"level": 103, "name": "A Hunter's Calling", "url": "https://wynncraft.gamepedia.com/A_Hunter%27s_Calling"},
]
}
self.config.register_guild(**default_guild)
self.config.register_global(**default_global)
@commands.command()
async def quefaire(self, ctx):
player_name = ctx.author.display_name
req = requests.get("https://api.wynncraft.com/v2/player/%s/stats" % player_name).json()
if not req["data"]:
await ctx.send(":x: impossible de t'aider, je ne te trouve pas sur Wynncraft :(")
return
max_class = None
for cl in req["data"][0]["classes"]:
if not max_class or max_class["professions"]["combat"]["level"] < cl["professions"]["combat"]["level"]:
max_class = cl
if not max_class:
await ctx.send(":x: impossible de t'aider, je ne te trouve pas ta classe sur Wynncraft :(")
return
# check for available quests
quests = await self.config.quests()
quests.reverse()
for quest in quests:
if quest["name"] in max_class["quests"]["list"]:
continue # player has already done this quest
if quest["level"] <= max_class["professions"]["combat"]["level"] and max_class["professions"]["combat"]["level"] - quest["level"] < 10:
# quest is doable
await ctx.send(":dizzy: Woosh! Tu peux faire la quête **%s** (%s)" % (quest["name"], quest["url"]))
return
# check for dungeons and grind spots
dungeons = await self.config.dungeons()
dungeons.reverse()
grind_spots = await self.config.grind_spots()
grind_spots.reverse()
hints = []
for dungeon in dungeons:
if max_class["professions"]["combat"]["level"] >= dungeon["minlevel"] and max_class["professions"]["combat"]["level"] - dungeon["minlevel"] < 10:
hints.append(":cyclone: Tu peux faire le donjon **%s** aux coordonnées %s (%s)" % (dungeon["name"], dungeon["location"], dungeon["url"]))
break
for grind_spot in grind_spots:
if grind_spot["minlevel"] <= max_class["professions"]["combat"]["level"] <= grind_spot["maxlevel"]:
hints.append(":star: Tu peux grinder aux coordonnées %s jusqu'au niveau %s" % (grind_spot["location"], grind_spot["maxlevel"]))
break
if hints:
await ctx.send(":dizzy: Woosh! Voici comment tu peux continuer ton aventure :\n%s" % "\n\t".join(hints))
else:
await ctx.send(":x: je sais pas comment t'aider :(")
@commands.command()
@checks.admin_or_permissions(manage_guild=True)
async def ping_channel(self, ctx, channel: TextChannel):
i = channel.id
await self.config.guild(ctx.guild).ping_channel.set(i)
await self.config.guild(ctx.guild).ping_levels.set(True)
await ctx.send("Les messages de montée de niveau seront désormais envoyés dans %s" % channel.mention)
@commands.command()
@checks.admin_or_permissions(manage_guild=True)
async def ping_levels(self, ctx, state):
if state == "true" or state == "on":
await self.config.ping_levels.set(True)
await ctx.send(":white_check_mark: Activé!")
elif state == "false" or state == "off":
await self.config.ping_levels.set(False)
await ctx.send(":white_check_mark: Désactivé!")
else:
await ctx.send(":x:")
@commands.command()
@checks.admin_or_permissions(manage_guild=True)
async def set_guild(self, ctx, guild_name):
if guild_name == "no":
await self.config.guild(ctx.guild).guild_name.set(False)
await ctx.send(":white_check_mark: Check sur les membres de la guilde désactivé")
else:
await self.config.guild(ctx.guild).guild_name.set(guild_name)
await ctx.send(":white_check_mark: Votre guilde est désormais **%s**" % guild_name)
@commands.command()
async def player(self, ctx, player_name):
req = requests.get("https://api.wynncraft.com/v2/player/%s/stats" % player_name).json()
if not req["data"]:
await ctx.send(":x: joueur non trouvé")
return
player_data = req["data"][0]
real_player_name = player_data["username"]
output = "**__%s__**\n" % real_player_name
output += "\t:black_square_button: **Joueur depuis:** %s\n" % player_data["meta"]["firstJoin"]
output += "\t:black_square_button: **Niveau total:** %s\n" % player_data["global"]["totalLevel"]["combat"]
output += "**__Classes__**:\n"
for c in player_data["classes"]:
output += "\t:diamond_shape_with_a_dot_inside: **%s:** level %s\n" % (c["name"], c["professions"]["combat"]["level"])
await ctx.send(output)
@commands.command()
@checks.is_owner()
async def log(self, ctx, state):
state = state.lower()
if state == "true" or state == "on":
await self.config.log.set(True)
await ctx.send(":white_check_mark:")
print("Logs on")
elif state == "false" or state == "off":
await self.config.log.set(False)
await ctx.send(":white_check_mark:")
print("Logs off")
else:
await ctx.send(":x:")
async def _log(self, s):
if await self.config.log():
print(s)
async def loop(self):
await self.bot.wait_until_ready()
last_status = {}
while self is self.bot.get_cog("WynncraftCog"):
await self._log("Looping guilds")
for guild in self.bot.guilds:
roles_combat_level = await self.config.guild(guild).role_levels()
roles_classes = await self.config.guild(guild).role_class()
roles_professions = await self.config.guild(guild).role_professions()
guild_id = guild.id
online_players = []
almost_online_players = []
await self._log("Loop id %s" % guild_id)
try:
guild_name = await self.config.guild(guild).guild_name()
if not guild_name:
members = [a.display_name for a in guild.members]
else:
r = requests.get("https://api.wynncraft.com/public_api.php?action=guildStats&command=%s" % guild_name).json()
members = [a["name"] for a in r["members"]]
for member_name in members:
await self._log("calling API for player %s" % member_name)
r = requests.get("https://api.wynncraft.com/v2/player/%s/stats" % member_name).json()
max_combat_lvl_class = None
best_professions = dict.fromkeys(list(roles_professions.keys()), 0)
if not r["data"]:
await self._log("%s had empty data %s" % (member_name, r["data"]))
continue
for cl in r["data"][0]["classes"]:
if guild_id not in last_status:
last_status[guild_id] = {}
if member_name not in last_status[guild_id]:
last_status[guild_id][member_name] = {}
if cl["name"] not in last_status[guild_id][member_name]:
last_status[guild_id][member_name][cl["name"]] = cl
# ping level up
ping_channel_id = await self.config.guild(guild).ping_channel()
if await self.config.guild(guild).ping_levels() and ping_channel_id:
await self._log("Checking for level up")
if cl["professions"]["combat"]["level"] > last_status[guild_id][member_name][cl["name"]]["professions"]["combat"]["level"]:
ping_channel = self.bot.get_channel(ping_channel_id)
await ping_channel.send(":high_brightness: %s a level up au niveau %s! GG!" % (member_name, cl["professions"]["combat"]["level"]))
else:
await self._log("Player %s had last level %s and current %s" % (member_name, last_status[guild_id][member_name][cl["name"]]["professions"]["combat"]["level"], cl["professions"]["combat"]["level"]))
last_status[guild_id][member_name][cl["name"]] = cl
# get max_combat_lvl_class
if not max_combat_lvl_class or cl["professions"]["combat"]["level"] > max_combat_lvl_class["professions"]["combat"]["level"]:
max_combat_lvl_class = cl
# set max_lvl_professions
for prof_name in list(roles_professions.keys()):
if cl["professions"][prof_name]["level"] > best_professions[prof_name]:
best_professions[prof_name] = cl["professions"][prof_name]["level"]
if r["data"][0]["meta"]["location"]["online"]:
await self._log("%s is online" % member_name)
online_players.append(r)
else:
last_join_dt_utc = datetime.strptime(r["data"][0]["meta"]["lastJoin"], "%Y-%m-%dT%H:%M:%S.%fZ")
last_join_dt_local = utc_to_local(last_join_dt_utc)
if last_join_dt_local + timedelta(minutes=10) > datetime.now(last_join_dt_local.tzinfo):
await self._log("%s is ALMOST online" % member_name)
almost_online_players.append(r)
else:
await self._log("%s was offline: %s" % (member_name, r["data"][0]["meta"]["location"]))
await self._log("Setting roles")
# set max_combat_lvl_class role
discord_member = discord.utils.find(lambda m: m.display_name.lower() == member_name.lower(), guild.members)
if max_combat_lvl_class and discord_member:
await self._log("max_combat_lvl_class and discord_member True")
# update level
max_combat_lvl = max_combat_lvl_class["professions"]["combat"]["level"]
combat_role_index = max_combat_lvl // 10
if combat_role_index > 10: combat_role_index = 10
combat_role_id = roles_combat_level[combat_role_index]
combat_role = guild.get_role(combat_role_id)
if combat_role not in discord_member.roles:
await self._log("Add role: %s" % combat_role)
await discord_member.add_roles(combat_role)
roles_to_remove = list(filter(lambda role: role.id in roles_combat_level and role.id != combat_role_id, discord_member.roles))
if roles_to_remove:
await self._log("remove roles: %s" % roles_to_remove)
await discord_member.remove_roles(*roles_to_remove)
# update class
class_name = max_combat_lvl_class["name"]
class_role_id = None
if class_name.startswith("archer") or class_name.startswith("hunter"):
class_role_id = roles_classes["archer"]
if class_name.startswith("mage") or class_name.startswith("darkwizard"):
class_role_id = roles_classes["mage"]
if class_name.startswith("assassin") or class_name.startswith("ninja"):
class_role_id = roles_classes["assassin"]
if class_name.startswith("warrior") or class_name.startswith("knight"):
class_role_id = roles_classes["warrior"]
if class_name.startswith("shaman") or class_name.startswith("skyseer"):
class_role_id = roles_classes["shaman"]
class_role = guild.get_role(class_role_id)
if class_role not in discord_member.roles:
await self._log("Add role: %s" % class_role)
await discord_member.add_roles(class_role)
roles_to_remove = list(filter(lambda role: role.id in list(roles_classes.values()) and role.id != class_role_id, discord_member.roles))
if roles_to_remove:
await self._log("remove roles: %s" % roles_to_remove)
await discord_member.remove_roles(*roles_to_remove)
# update professions
role_ids_to_add = []
for prof_name in best_professions:
prof_level = best_professions[prof_name]
if prof_level < 5: continue # do not add roles for professions < lvl 5
prof_role_index = prof_level // 10
if prof_role_index > 10: prof_role_index = 10
role_ids_to_add.append(roles_professions[prof_name][prof_role_index])
roles_to_add = []
for role_id_to_add in role_ids_to_add:
roles_to_add.append(guild.get_role(role_id_to_add))
await self._log("Add role: %s" % roles_to_add)
await discord_member.add_roles(*roles_to_add)
all_prof_ids = list(itertools.chain.from_iterable([roles_professions[b] for b in roles_professions]))
roles_to_remove = list(filter(lambda role: role.id in all_prof_ids and role.id not in role_ids_to_add, discord_member.roles))
if roles_to_remove:
await self._log("remove roles: %s" % roles_to_remove)
await discord_member.remove_roles(*roles_to_remove)
else:
await self._log("max_combat_lvl_class: %s, discord_member: %s (name: %s)" % (max_combat_lvl_class, discord_member, member_name))
# update online players
await self._log("Updating online list")
full_text = ""
for online_player in online_players:
s = ":green_circle: **%s** connecté sur %s" % (online_player["data"][0]["username"], online_player["data"][0]["meta"]["location"]["server"])
full_text += s + "\n"
for almost_online_player in almost_online_players:
last_join_dt_utc = datetime.strptime(almost_online_player["data"][0]["meta"]["lastJoin"], "%Y-%m-%dT%H:%M:%S.%fZ")
last_join_dt_local = utc_to_local(last_join_dt_utc)
mins = math.ceil((datetime.now(last_join_dt_local.tzinfo) - last_join_dt_local).total_seconds() // 60)
s = ":grey_question: **%s** était connecté il y a %s minutes" % (almost_online_player["data"][0]["username"], mins)
full_text += s + "\n"
if not online_players and not almost_online_players:
full_text = "Personne n'est connecté :disappointed_relieved:\n"
full_text += "\n*Dernière mise à jour: %s*\n*Les vraies informations peuvent prendre jusqu'à 5 minutes de plus*" % datetime.now().strftime("%d/%m/%Y %H:%M:%S")
online_channel = self.bot.get_channel(750100968766701708)
update_msg = await online_channel.fetch_message(await self.config.guild(guild).update_msg())
await update_msg.edit(content=full_text)
except:
if await self.config.log():
traceback.print_exc()
await asyncio.sleep(3600)