From cae3796a5a8a103804a8c5b2eeba9f6fa920d074 Mon Sep 17 00:00:00 2001 From: TrustyJAID Date: Wed, 3 Apr 2024 14:23:34 -0600 Subject: [PATCH] [Tarot] 1.3.0 - Update cards into different components like rank, suit, and arcana. - Show all variations of a card's rank and suit name and allow searching cards based on any of them. - include standard french deck suit emojis. - Allow setting different card images from tarot.com. - Use HSV to get more normalized colours for embeds. - Reset random state between uses of `[p]tarot life`. --- README.md | 2 +- tarot/tarot_cards.py | 234 +++++++++++++++++++++ tarot/tarotreading.py | 470 +++++++++++++++++++++++++++++++++--------- 3 files changed, 611 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index e0d206adce..247efe3d44 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ TrustyJAID's Cogs for [Red-DiscordBot](https://github.com/Cog-Creators/Red-Disc | ServerStats | 1.8.0 |
A plethora of potentially useful commands for any bot owner.A plethora of potentially useful commands for any bot owner. Includes a way to track the bot joining new servers, find cheaters on global economies, get user avatars and even larger emojis.
| TrustyJAID and Preda | | Spotify | 1.7.2 |
Control Spotify through Discord!This cog allows you to control Spotify via OAuth through the bot on discord. Use `[p]spotify` to see available commands.
| TrustyJAID and NeuroAssassin | | Starboard | 2.6.0 |
StarboardCreate a starboard channel to save those amazing posts!
| TrustyJAID | -| Tarot | 1.2.0 |
General commandsFind your tarot reading, your life reading, or pull a random tarot card!
| TrustyJAID | +| Tarot | 1.3.0 |
General commandsFind your tarot reading, your life reading, or pull a random tarot card!
| TrustyJAID | | Timestamp | 1.1.1 |
Discord Timestamp GeneratorA cog to generate discord timestamps!
| TrustyJAID | | Translate | 2.6.0 |
Translate messages using google translate!Add flag emojis to messages to translate to that language or translate messages by command.
| Aziz and TrustyJAID | | Turbo | 1.0.0 |
Relive that 90's computer feel with turbo mode on any command!Add turbo mode to all your commands!
| TrustyJAID | diff --git a/tarot/tarot_cards.py b/tarot/tarot_cards.py index 0621686952..836d95c010 100644 --- a/tarot/tarot_cards.py +++ b/tarot/tarot_cards.py @@ -4,467 +4,701 @@ "card_name": "Ace of Wands", "card_url": "https://tarot.com/tarot/ace-of-wands/", "card_img": "http://i.imgur.com/MfJ1s77.jpg", + "rank": "ace", + "suit": "wands", + "arcana": "minor", }, "2": { "card_meaning": "Future planning, progress, decisions, discovery", "card_name": "Two of Wands", "card_url": "https://tarot.com/tarot/two-of-wands/", "card_img": "http://i.imgur.com/Ghb63pd.jpg", + "rank": "two", + "suit": "wands", + "arcana": "minor", }, "3": { "card_meaning": "Preparation, foresight, enterprise, expansion", "card_name": "Three of Wands", "card_url": "https://tarot.com/tarot/three-of-wands/", "card_img": "http://i.imgur.com/NIUAuzw.jpg", + "rank": "three", + "suit": "wands", + "arcana": "minor", }, "4": { "card_meaning": "Celebration, harmony, marriage, home, community", "card_name": "Four of Wands", "card_url": "https://tarot.com/tarot/four-of-wands/", "card_img": "http://i.imgur.com/heelDVY.jpg", + "rank": "four", + "suit": "wands", + "arcana": "minor", }, "5": { "card_meaning": "Disagreement, competition, strife, tension, conflict", "card_name": "Five of Wands", "card_url": "https://tarot.com/tarot/five-of-wands/", "card_img": "http://i.imgur.com/Qojm2p1.jpg", + "rank": "five", + "suit": "wands", + "arcana": "minor", }, "6": { "card_meaning": "Public recognition, victory, progress, self-confidence", "card_name": "Six of Wands", "card_url": "https://tarot.com/tarot/six-of-wands/", "card_img": "http://i.imgur.com/DClClQ5.jpg", + "rank": "six", + "suit": "wands", + "arcana": "minor", }, "7": { "card_meaning": "Challenge, competition, perseverance", "card_name": "Seven of Wands", "card_url": "https://tarot.com/tarot/seven-of-wands/", "card_img": "http://i.imgur.com/hsNtHC0.jpg", + "rank": "seven", + "suit": "wands", + "arcana": "minor", }, "8": { "card_meaning": "Speed, action, air travel, movement, swift change", "card_name": "Eight of Wands", "card_url": "https://tarot.com/tarot/eight-of-wands/", "card_img": "http://i.imgur.com/TaTYcNt.jpg", + "rank": "eight", + "suit": "wands", + "arcana": "minor", }, "9": { "card_meaning": "Courage, persistence, test of faith, resilience", "card_name": "Nine of Wands", "card_url": "https://tarot.com/tarot/nine-of-wands/", "card_img": "http://i.imgur.com/qjJdi8J.jpg", + "rank": "nine", + "suit": "wands", + "arcana": "minor", }, "10": { "card_meaning": "Burden, responsibility, hard work, stress, achievement", "card_name": "Ten of Wands", "card_url": "https://tarot.com/tarot/ten-of-wands/", "card_img": "http://i.imgur.com/hNC3y5g.jpg", + "rank": "ten", + "suit": "wands", + "arcana": "minor", }, "11": { "card_meaning": "Enthusiasm, exploration, discovery, free spirit", "card_name": "Page of Wands", "card_url": "https://tarot.com/tarot/page-of-wands/", "card_img": "http://i.imgur.com/44puitP.jpg", + "rank": "page", + "suit": "wands", + "arcana": "minor", }, "12": { "card_meaning": "Energy, passion, lust, action, adventure, impulsiveness", "card_name": "Knight of Wands", "card_url": "https://tarot.com/tarot/knight-of-wands/", "card_img": "http://i.imgur.com/yzCPXQX.jpg", + "rank": "knight", + "suit": "wands", + "arcana": "minor", }, "13": { "card_meaning": "Exuberance, warmth, vibrancy, determination", "card_name": "Queen of Wands", "card_url": "https://tarot.com/tarot/queen-of-wands/", "card_img": "http://i.imgur.com/LTeYFqy.jpg", + "rank": "queen", + "suit": "wands", + "arcana": "minor", }, "14": { "card_meaning": "Natural-born leader, vision, entrepreneur, honour", "card_name": "King of Wands", "card_url": "https://tarot.com/tarot/king-of-wands/", "card_img": "http://i.imgur.com/iC2Il8N.jpg", + "rank": "king", + "suit": "wands", + "arcana": "minor", }, "15": { "card_meaning": "Raw power, victory, break-throughs, mental clarity", "card_name": "Ace of Swords", "card_url": "https://tarot.com/tarot/ace-of-swords/", "card_img": "http://i.imgur.com/ja2tUWE.jpg", + "rank": "ace", + "suit": "swords", + "arcana": "minor", }, "16": { "card_meaning": "Indecision, choices, truce, stalemate, blocked emotions", "card_name": "Two of Swords", "card_url": "https://tarot.com/tarot/two-of-swords/", "card_img": "http://i.imgur.com/EBIjsot.jpg", + "rank": "two", + "suit": "swords", + "arcana": "minor", }, "17": { "card_meaning": "Painful separation, sorrow heartbreak, grief, rejection", "card_name": "Three of Swords", "card_url": "https://tarot.com/tarot/three-of-swords/", "card_img": "http://i.imgur.com/CkKUNSa.jpg", + "rank": "three", + "suit": "swords", + "arcana": "minor", }, "18": { "card_meaning": "Contemplation, recuperation, passivity, relaxation, rest", "card_name": "Four of Swords", "card_url": "https://tarot.com/tarot/four-of-swords/", "card_img": "http://i.imgur.com/y1psOrh.jpg", + "rank": "four", + "suit": "swords", + "arcana": "minor", }, "19": { "card_meaning": "Conflict, tension, loss, defeat, win at all costs, betrayal", "card_name": "Five of Swords", "card_url": "https://tarot.com/tarot/five-of-swords/", "card_img": "http://i.imgur.com/T1pDWbo.jpg", + "rank": "five", + "suit": "swords", + "arcana": "minor", }, "20": { "card_meaning": "Regretful but necessary transition, rite of passage", "card_name": "Six of Swords", "card_url": "https://tarot.com/tarot/six-of-swords/", "card_img": "http://i.imgur.com/ddXLubY.jpg", + "rank": "six", + "suit": "swords", + "arcana": "minor", }, "21": { "card_meaning": "Betrayal, deception, getting away with something, stealth", "card_name": "Seven of Swords", "card_url": "https://tarot.com/tarot/seven-of-swords/", "card_img": "http://i.imgur.com/6DNu8dU.jpg", + "rank": "seven", + "suit": "swords", + "arcana": "minor", }, "22": { "card_meaning": "Isolation, self-imposed restriction, imprisonment", "card_name": "Eight of Swords", "card_url": "https://tarot.com/tarot/eight-of-swords/", "card_img": "http://i.imgur.com/ov9j8pg.jpg", + "rank": "eight", + "suit": "swords", + "arcana": "minor", }, "23": { "card_meaning": "Depression, nightmares, intense anxiety, despair", "card_name": "Nine of Swords", "card_url": "https://tarot.com/tarot/nine-of-swords/", "card_img": "http://i.imgur.com/iWl54Xx.jpg", + "rank": "nine", + "suit": "swords", + "arcana": "minor", }, "24": { "card_meaning": "Back-stabbed, defeat, crisis, betrayal, endings, loss", "card_name": "Ten of Swords", "card_url": "https://tarot.com/tarot/ten-of-swords/", "card_img": "http://i.imgur.com/NhkCBnz.jpg", + "rank": "ten", + "suit": "swords", + "arcana": "minor", }, "25": { "card_meaning": "Talkative, curious, mentally restless, energetic", "card_name": "Page of Swords", "card_url": "https://tarot.com/tarot/page-of-swords/", "card_img": "http://i.imgur.com/decjazD.jpg", + "rank": "page", + "suit": "swords", + "arcana": "minor", }, "26": { "card_meaning": "Opinionated, hasty, action-oriented, communicative", "card_name": "Knight of Swords", "card_url": "https://tarot.com/tarot/knight-of-swords/", "card_img": "http://i.imgur.com/p87LNk8.jpg", + "rank": "knight", + "suit": "swords", + "arcana": "minor", }, "27": { "card_meaning": "Quick thinker, organised, perceptive, independent", "card_name": "Queen of Swords", "card_url": "https://tarot.com/tarot/queen-of-swords/", "card_img": "http://i.imgur.com/vgTOeBk.jpg", + "rank": "queen", + "suit": "swords", + "arcana": "minor", }, "28": { "card_meaning": "Clear thinking, intellectual power, authority, truth", "card_name": "King of Swords", "card_url": "https://tarot.com/tarot/king-of-swords/", "card_img": "http://i.imgur.com/Y0EtbYp.jpg", + "rank": "king", + "suit": "swords", + "arcana": "minor", }, "29": { "card_meaning": "Love, compassion, creativity, overwhelming emotion", "card_name": "Ace of Cups", "card_url": "https://tarot.com/tarot/ace-of-cups/", "card_img": "http://i.imgur.com/4ZcxuXf.jpg", + "rank": "ace", + "suit": "cups", + "arcana": "minor", }, "30": { "card_meaning": "Unified love, partnership, attraction, relationships", "card_name": "Two of Cups", "card_url": "https://tarot.com/tarot/two-of-cups/", "card_img": "http://i.imgur.com/K8MRy5I.jpg", + "rank": "two", + "suit": "cups", + "arcana": "minor", }, "31": { "card_meaning": "Celebration, friendship, creativity, community", "card_name": "Three of Cups", "card_url": "https://tarot.com/tarot/three-of-cups/", "card_img": "http://i.imgur.com/lqsvyuD.jpg", + "rank": "three", + "suit": "cups", + "arcana": "minor", }, "32": { "card_meaning": "Meditation, contemplation, apathy, re-evaluation", "card_name": "Four of Cups", "card_url": "https://tarot.com/tarot/four-of-cups/", "card_img": "http://i.imgur.com/wd1JMK5.jpg", + "rank": "four", + "suit": "cups", + "arcana": "minor", }, "33": { "card_meaning": "Loss, regret, disappointment, despair, bereavement", "card_name": "Five of Cups", "card_url": "https://tarot.com/tarot/five-of-cups/", "card_img": "http://i.imgur.com/8EqDUAa.jpg", + "rank": "five", + "suit": "cups", + "arcana": "minor", }, "34": { "card_meaning": "Reunion, nostalgia, childhood memories, innocence", "card_name": "Six of Cups", "card_url": "https://tarot.com/tarot/six-of-cups/", "card_img": "http://i.imgur.com/pA7oFHJ.jpg", + "rank": "six", + "suit": "cups", + "arcana": "minor", }, "35": { "card_meaning": "Fantasy, illusion, wishful thinking, choices, imagination", "card_name": "Seven of Cups", "card_url": "https://tarot.com/tarot/seven-of-cups/", "card_img": "http://i.imgur.com/NL300h2.jpg", + "rank": "seven", + "suit": "cups", + "arcana": "minor", }, "36": { "card_meaning": "Escapism, disappointment, abandonment, withdrawal", "card_name": "Eight of Cups", "card_url": "https://tarot.com/tarot/eight-of-cups/", "card_img": "http://i.imgur.com/MTgZYuZ.jpg", + "rank": "eight", + "suit": "cups", + "arcana": "minor", }, "37": { "card_meaning": "Wishes fulfilled, comfort, happiness, satisfaction", "card_name": "Nine of Cups", "card_url": "https://tarot.com/tarot/nine-of-cups/", "card_img": "http://i.imgur.com/FzW2aGy.jpg", + "rank": "nine", + "suit": "cups", + "arcana": "minor", }, "38": { "card_meaning": "Harmony, marriage, happiness, alignment", "card_name": "Ten of Cups", "card_url": "https://tarot.com/tarot/ten-of-cups/", "card_img": "http://i.imgur.com/bEm3wa8.jpg", + "rank": "ten", + "suit": "cups", + "arcana": "minor", }, "39": { "card_meaning": "A messenger, creative beginnings, synchronicity", "card_name": "Page of Cups", "card_url": "https://tarot.com/tarot/page-of-cups/", "card_img": "http://i.imgur.com/UvanQ1x.jpg", + "rank": "page", + "suit": "cups", + "arcana": "minor", }, "40": { "card_meaning": "Romance, charm, 'Knight in shining armour', imagination", "card_name": "Knight of Cups", "card_url": "https://tarot.com/tarot/knight-of-cups/", "card_img": "http://i.imgur.com/SfpE4cm.jpg", + "rank": "knight", + "suit": "cups", + "arcana": "minor", }, "41": { "card_meaning": "Emotional security, calm, intuitive, compassionate", "card_name": "Queen of Cups", "card_url": "https://tarot.com/tarot/queen-of-cups/", "card_img": "http://i.imgur.com/ZVMpaO6.jpg", + "rank": "queen", + "suit": "cups", + "arcana": "minor", }, "42": { "card_meaning": "Emotional balance and control, generosity", "card_name": "King of Cups", "card_url": "https://tarot.com/tarot/king-of-cups/", "card_img": "http://i.imgur.com/fW2cZDl.jpg", + "rank": "king", + "suit": "cups", + "arcana": "minor", }, "43": { "card_meaning": "Manifestation, new financial opportunity, prosperity", "card_name": "Ace of Pentacles", "card_url": "https://tarot.com/tarot/ace-of-pentacles/", "card_img": "http://i.imgur.com/ebAyNZZ.jpg", + "rank": "ace", + "suit": "pentacles", + "arcana": "minor", }, "44": { "card_meaning": "Balance, adaptability, time management, prioritisation", "card_name": "Two of Pentacles", "card_url": "https://tarot.com/tarot/two-of-pentacles/", "card_img": "http://i.imgur.com/F3URp9M.jpg", + "rank": "two", + "suit": "pentacles", + "arcana": "minor", }, "45": { "card_meaning": "Teamwork, initial fulfilment, collaboration, learning", "card_name": "Three of Pentacles", "card_url": "https://tarot.com/tarot/three-of-pentacles/", "card_img": "http://i.imgur.com/hzIo13n.jpg", + "rank": "three", + "suit": "pentacles", + "arcana": "minor", }, "46": { "card_meaning": "Control, stability, security, possession, conservatism", "card_name": "Four of Pentacles", "card_url": "https://tarot.com/tarot/four-of-pentacles/", "card_img": "http://i.imgur.com/caXXbBL.jpg", + "rank": "four", + "suit": "pentacles", + "arcana": "minor", }, "47": { "card_meaning": "Isolation, insecurity, worry, financial loss, poverty", "card_name": "Five of Pentacles", "card_url": "https://tarot.com/tarot/five-of-pentacles/", "card_img": "http://i.imgur.com/hIG43wQ.jpg", + "rank": "five", + "suit": "pentacles", + "arcana": "minor", }, "48": { "card_meaning": "Generosity, charity, giving, prosperity, sharing wealth", "card_name": "Six of Pentacles", "card_url": "https://tarot.com/tarot/six-of-pentacles/", "card_img": "http://i.imgur.com/NR2JdPf.jpg", + "rank": "six", + "suit": "pentacles", + "arcana": "minor", }, "49": { "card_meaning": "Vision, perseverance, profit, reward, investment", "card_name": "Seven of Pentacles", "card_url": "https://tarot.com/tarot/seven-of-pentacles/", "card_img": "http://i.imgur.com/nYYBX2T.jpg", + "rank": "seven", + "suit": "pentacles", + "arcana": "minor", }, "50": { "card_meaning": "Apprenticeship, education, quality, engagement", "card_name": "Eight of Pentacles", "card_url": "https://tarot.com/tarot/eight-of-pentacles/", "card_img": "http://i.imgur.com/rSCcjOv.jpg", + "rank": "eight", + "suit": "pentacles", + "arcana": "minor", }, "51": { "card_meaning": "Gratitude, luxury, self-sufficiency, culmination", "card_name": "Nine of Pentacles", "card_url": "https://tarot.com/tarot/nine-of-pentacles/", "card_img": "http://i.imgur.com/TsHqgIQ.jpg", + "rank": "nine", + "suit": "pentacles", + "arcana": "minor", }, "52": { "card_meaning": "Wealth, inheritance, family, establishment, retirement", "card_name": "Ten of Pentacles", "card_url": "https://tarot.com/tarot/ten-of-pentacles/", "card_img": "http://i.imgur.com/p8VSgOO.jpg", + "rank": "ten", + "suit": "pentacles", + "arcana": "minor", }, "53": { "card_meaning": "Manifestation, financial opportunity, new job", "card_name": "Page of Pentacles", "card_url": "https://tarot.com/tarot/page-of-pentacles/", "card_img": "http://i.imgur.com/WsQEzyQ.jpg", + "rank": "page", + "suit": "pentacles", + "arcana": "minor", }, "54": { "card_meaning": "Efficiency, routine, conservatism, methodical", "card_name": "Knight of Pentacles", "card_url": "https://tarot.com/tarot/knight-of-pentacles/", "card_img": "http://i.imgur.com/yEL8ZAr.jpg", + "rank": "knight", + "suit": "pentacles", + "arcana": "minor", }, "55": { "card_meaning": "Practical, homely, motherly, down-to-earth, security", "card_name": "Queen of Pentacles", "card_url": "https://tarot.com/tarot/queen-of-pentacles/", "card_img": "http://i.imgur.com/OX60YCi.jpg", + "rank": "queen", + "suit": "pentacles", + "arcana": "minor", }, "56": { "card_meaning": "Security, control, power, discipline, abundance", "card_name": "King of Pentacles", "card_url": "https://tarot.com/tarot/king-of-pentacles/", "card_img": "http://i.imgur.com/Dk6tR9R.jpg", + "rank": "king", + "suit": "pentacles", + "arcana": "minor", }, "57": { "card_meaning": "Beginnings, innocence, spontaneity, a free spirit", "card_name": "Fool", "card_url": "https://tarot.com/tarot/the-fool/", "card_img": "http://i.imgur.com/vetEoDu.jpg", + "rank": "fool", + "suit": "trump", + "arcana": "major", }, "58": { "card_meaning": "Power, skill, concentration, action, resourcefulness", "card_name": "Magician", "card_url": "https://tarot.com/tarot/the-magician/", "card_img": "http://i.imgur.com/9VJLy9S.jpg", + "rank": "magician", + "suit": "trump", + "arcana": "major", }, "59": { "card_meaning": "Intuition, Higher powers, mystery, subconscious mind", "card_name": "High Priestess", "card_url": "https://tarot.com/tarot/the-high-priestess/", "card_img": "http://i.imgur.com/bablzG8.jpg", + "rank": "high_priestess", + "suit": "trump", + "arcana": "major", }, "60": { "card_meaning": "Fertility, femininity, beauty, nature, abundance", "card_name": "Empress", "card_url": "https://tarot.com/tarot/the-empress/", "card_img": "http://i.imgur.com/sh0Fzdt.jpg", + "rank": "empress", + "suit": "trump", + "arcana": "major", }, "61": { "card_meaning": "Authority, father-figure, structure, solid foundation", "card_name": "Emperor", "card_url": "https://tarot.com/tarot/the-emperor/", "card_img": "http://i.imgur.com/sc76hNS.jpg", + "rank": "emperor", + "suit": "trump", + "arcana": "major", }, "62": { "card_meaning": "Religion, group identification, conformity, tradition, beliefs", "card_name": "Hierophant", "card_url": "https://tarot.com/tarot/the-hierophant/", "card_img": "http://i.imgur.com/JFvG1um.jpg", + "rank": "heirophant", + "suit": "trump", + "arcana": "major", }, "63": { "card_meaning": "Love, union, relationships, values alignment, choices", "card_name": "Lovers", "card_url": "https://tarot.com/tarot/the-lovers/", "card_img": "http://i.imgur.com/U6CcViS.jpg", + "rank": "lovers", + "suit": "trump", + "arcana": "major", }, "64": { "card_meaning": "Control, will power, victory, assertion, determination", "card_name": "Chariot", "card_url": "https://tarot.com/tarot/the-chariot/", "card_img": "http://i.imgur.com/xilfsxU.jpg", + "rank": "chariot", + "suit": "trump", + "arcana": "major", }, "65": { "card_meaning": "Strength, courage, patience, control, compassion", "card_name": "Strength", "card_url": "https://tarot.com/tarot/strength/", "card_img": "http://i.imgur.com/DhNmgJ5.jpg", + "rank": "strength", + "suit": "trump", + "arcana": "major", }, "66": { "card_meaning": "Soul-searching, introspection, being alone, inner guidance", "card_name": "Hermit", "card_url": "https://tarot.com/tarot/the-hermit/", "card_img": "http://i.imgur.com/yTzCitM.jpg", + "rank": "hermit", + "suit": "trump", + "arcana": "major", }, "67": { "card_meaning": "Good luck, karma, life cycles, destiny, a turning point", "card_name": "Wheel of Fortune", "card_url": "https://tarot.com/tarot/wheel-of-fortune/", "card_img": "http://i.imgur.com/jrY1gDP.jpg", + "rank": "wheel_of_fortune", + "suit": "trump", + "arcana": "major", }, "68": { "card_meaning": "Justice, fairness, truth, cause and effect, law", "card_name": "Justice", "card_url": "https://tarot.com/tarot/justice/", "card_img": "http://i.imgur.com/eyHEpUR.jpg", + "rank": "justice", + "suit": "trump", + "arcana": "major", }, "69": { "card_meaning": "Suspension, restriction, letting go, sacrifice", "card_name": "Hanged Man", "card_url": "https://tarot.com/tarot/the-hanged-man/", "card_img": "http://i.imgur.com/b5Sml3N.jpg", + "rank": "hanged_man", + "suit": "trump", + "arcana": "major", }, "70": { "card_meaning": "Endings, beginnings, change, transformation, transition", "card_name": "Death", "card_url": "https://tarot.com/tarot/death/", "card_img": "http://i.imgur.com/ujauT3a.jpg", + "rank": "death", + "suit": "trump", + "arcana": "major", }, "71": { "card_meaning": "Balance, moderation, patience, purpose, meaning", "card_name": "Temperance", "card_url": "https://tarot.com/tarot/temperance/", "card_img": "http://i.imgur.com/i2iwvA0.jpg", + "rank": "temperance", + "suit": "trump", + "arcana": "major", }, "72": { "card_meaning": "Bondage, addiction, sexuality, materialism", "card_name": "Devil", "card_url": "https://tarot.com/tarot/the-devil/", "card_img": "http://i.imgur.com/ChhcGKF.jpg", + "rank": "devil", + "suit": "trump", + "arcana": "major", }, "73": { "card_meaning": "Disaster, upheaval, sudden change, revelation", "card_name": "Tower", "card_url": "https://tarot.com/tarot/the-tower/", "card_img": "http://i.imgur.com/ZYpEMKl.jpg", + "rank": "tower", + "suit": "trump", + "arcana": "major", }, "74": { "card_meaning": "Hope, spirituality, renewal, inspiration, serenity", "card_name": "Star", "card_url": "https://tarot.com/tarot/the-star/", "card_img": "http://i.imgur.com/2X56bTd.jpg", + "rank": "star", + "suit": "trump", + "arcana": "major", }, "75": { "card_meaning": "Illusion, fear, anxiety, insecurity, subconscious", "card_name": "Moon", "card_url": "https://tarot.com/tarot/the-moon/", "card_img": "http://i.imgur.com/8Cpfekc.jpg", + "rank": "moon", + "suit": "trump", + "arcana": "major", }, "76": { "card_meaning": "Fun, warmth, success, positivity, vitality", "card_name": "Sun", "card_url": "https://tarot.com/tarot/the-sun/", "card_img": "http://i.imgur.com/zgJJy7b.jpg", + "rank": "sun", + "suit": "trump", + "arcana": "major", }, "77": { "card_meaning": "Judgement, rebirth, inner calling, absolution", "card_name": "Judgement", "card_url": "https://tarot.com/tarot/judgement/", "card_img": "http://i.imgur.com/JyALFQF.jpg", + "rank": "judgement", + "suit": "trump", + "arcana": "major", }, "78": { "card_meaning": "Completion, integration, accomplishment, travel", "card_name": "World", "card_url": "https://tarot.com/tarot/the-world/", "card_img": "http://i.imgur.com/3ZncYkn.jpg", + "rank": "world", + "suit": "trump", + "arcana": "major", }, } diff --git a/tarot/tarotreading.py b/tarot/tarotreading.py index b989f9edee..7270cb31d3 100644 --- a/tarot/tarotreading.py +++ b/tarot/tarotreading.py @@ -2,13 +2,12 @@ import random import re -from dataclasses import dataclass -from random import choice, sample -from typing import Optional +from enum import Enum +from typing import List, Optional, Union import discord from red_commons.logging import getLogger -from redbot.core import commands +from redbot.core import Config, commands from redbot.core.utils.views import SimpleMenu from .tarot_cards import card_list as tarot_cards @@ -16,48 +15,284 @@ log = getLogger("red.trusty-cogs.tarot") -TAROT_RE = re.compile(r"|".join(t["card_name"] for _id, t in tarot_cards.items()), flags=re.I) +def get_colour() -> discord.Colour: + # Thanks Sinbad + return discord.Color.from_hsv(random.random(), 1.0, 0.8) + + +class TarotMeaning(Enum): + past = 0 + present = 1 + future = 2 + potential = 3 + reason = 4 + + def __str__(self) -> str: + return self.name.title() + + +class TarotSuit(Enum): + trump = 0 # Represents the major arcana + swords = 1 + spades = 1 + cups = 2 + hearts = 2 + pentacles = 3 + diamonds = 3 + disks = 3 + coins = 3 + wands = 4 + rods = 4 + clubs = 4 + batons = 4 + + def __str__(self): + return self.emoji + " | ".join(i.title() for i in self.aliases) + + @staticmethod + def re(): + return re.compile(r"|".join(k for k in TarotSuit.__members__.keys()), flags=re.I) + + @property + def offset(self): + return self.value * 16 + + @property + def aliases(self) -> List[str]: + ret = [] + for alias, value in TarotSuit.__members__.items(): + if value is self: + ret.append(alias) + return ret + + @classmethod + def from_name(cls, name: str) -> TarotSuit: + for alias, value in cls.__members__.items(): + if alias.lower() == name: + return value + return cls(0) + + @property + def emoji(self) -> str: + return { + TarotSuit.trump: "\N{PLAYING CARD BLACK JOKER}", + TarotSuit.wands: "\N{BLACK CLUB SUIT}\N{VARIATION SELECTOR-16}", + TarotSuit.swords: "\N{BLACK SPADE SUIT}\N{VARIATION SELECTOR-16}", + TarotSuit.cups: "\N{BLACK HEART SUIT}\N{VARIATION SELECTOR-16}", + TarotSuit.pentacles: "\N{BLACK DIAMOND SUIT}\N{VARIATION SELECTOR-16}", + }[self] + + +class TarotRank(Enum): + trump = 0 + ace = 1 + two = 2 + three = 3 + four = 4 + five = 5 + six = 6 + seven = 7 + eight = 8 + nine = 9 + ten = 10 + page = 11 + jack = 11 + knave = 11 + knight = 12 + queen = 13 + king = 14 + + def __str__(self): + return " | ".join(i.title() for i in self.aliases) + + @staticmethod + def re(): + return re.compile( + r"|".join(rf"{k}|{v.value}" for k, v in TarotRank.__members__.items()), flags=re.I + ) + + @property + def aliases(self) -> List[str]: + ret = [] + for alias, value in TarotRank.__members__.items(): + if value is self: + ret.append(alias) + return ret + + @classmethod + def from_name(cls, name: str) -> TarotRank: + for alias, value in cls.__members__.items(): + if alias.lower() == name or str(value.value) == name: + return value + return cls(0) + + +class Arcana(Enum): + minor = 0 + major = 1 + + def __str__(self): + return f"{self.name.title()} Arcana" + + @classmethod + def from_name(cls, name: str) -> Arcana: + for alias, value in cls.__members__.items(): + if alias.lower() == name: + return value + return cls(0) + + +class MajorArcana(Enum): + fool = 0 + magician = 1 + high_priestess = 2 + empress = 3 + emperor = 4 + heirophant = 5 + lovers = 6 + chariot = 7 + strength = 8 + hermit = 9 + wheel_of_fortune = 10 + justice = 11 + hanged_man = 12 + death = 13 + temperance = 14 + devil = 15 + tower = 16 + star = 17 + moon = 18 + sun = 19 + judgement = 20 + world = 21 + + def __str__(self): + return self.name.replace("_", " ").title() + + @staticmethod + def re(): + return re.compile(r"|".join(str(v) for v in MajorArcana.__members__.values()), flags=re.I) + + @classmethod + def from_name(cls, name: str) -> MajorArcana: + for alias, value in cls.__members__.items(): + if alias.lower() == name: + return value + return MajorArcana.fool -@dataclass class TarotCard: - id: int - card_meaning: str - card_name: str - card_url: str - card_img: str + def __init__( + self, + id: int, + card_meaning: str, + card_url: str, + card_img: str, + rank: Union[MajorArcana, TarotRank], + suit: TarotSuit, + arcana: Arcana, + ): + self.id: int = id + self.card_meaning: str = card_meaning + self.card_url: str = card_url + self.card_img: str = card_img + self.rank = rank + self.suit = suit + self.arcana = arcana + + @property + def emoji(self): + if self.arcana is not Arcana.major: + return chr(0x1F090 + self.rank.value + self.suit.offset) + return chr(0x1F0E0 + self.rank.value) + + @property + def card_name(self): + if self.arcana is Arcana.minor: + return f"{self.rank} of {self.suit}" + return f"{self.rank} {self.suit.emoji}" + + @staticmethod + def re(): + return re.compile( + rf"(?P{TarotRank.re().pattern})\s(of)?\s?(?P{TarotSuit.re().pattern})|(?P{MajorArcana.re().pattern})", + flags=re.I, + ) + + def get_card_img(self, deck: Optional[str]): + if deck is None: + return self.card_img + url = "https://gfx.tarot.com/images/site/decks/{deck}/full_size/{card_number}.jpg" + card_number = 0 + offsets = { + TarotSuit.wands: 0, + TarotSuit.cups: 1, + TarotSuit.swords: 2, + TarotSuit.pentacles: 3, + } + if self.arcana is Arcana.major: + card_number = self.rank.value + else: + card_number = 21 + self.rank.value + offsets[self.suit] * 14 + return url.format(deck=deck, card_number=card_number) @classmethod - async def convert(self, ctx: commands.Context, argument: str) -> Optional[TarotCard]: - if find := TAROT_RE.match(argument): - card_name = find.group(0) + def from_json(cls, id: int, data: dict) -> TarotCard: + arcana = Arcana.from_name(data["arcana"]) + if arcana is Arcana.major: + rank = MajorArcana.from_name(data["rank"]) + else: + rank = TarotRank.from_name(data["rank"]) + suit = TarotSuit.from_name(data["suit"]) + return cls( + id=id, + card_meaning=data["card_meaning"], + card_url=data["card_url"], + card_img=data["card_img"], + rank=rank, + suit=suit, + arcana=arcana, + ) + + @classmethod + async def convert(cls, ctx: commands.Context, argument: str) -> Optional[TarotCard]: + match = TarotCard.re().match(argument) + log.debug(match) + if match and not argument.isdigit(): + suit_str = match.group("suit") + rank_str = match.group("rank") + suit = None + rank = None + if suit_str: + suit = TarotSuit.from_name(suit_str) + if rank_str: + rank = TarotRank.from_name(rank_str) + arcana = match.group("arcana") for _id, card in tarot_cards.items(): - if card_name.lower() == card["card_name"].lower(): - return TarotCard(id=_id, **card) + if arcana and arcana.lower() == card["card_name"].lower(): + return TarotCard.from_json(id=int(_id), data=card) + elif suit and rank: + if suit.name == card["suit"] and rank.name == card["rank"]: + return TarotCard.from_json(id=int(_id), data=card) else: try: card = tarot_cards[str(argument)] - return TarotCard(id=int(argument), **card) + return TarotCard.from_json(id=int(argument), data=card) except KeyError: raise commands.BadArgument(f"`{argument}` is not an available Tarot card.") return None - def get_colour(self) -> int: - colour = "".join([choice("0123456789ABCDEF") for x in range(6)]) - return int(colour, 16) - - def embed(self): - embed = discord.Embed( - title=self.card_name, - description=self.card_meaning, - colour=discord.Colour(value=self.get_colour()), - url=self.card_url, + def embed(self, deck: Optional[str] = None): + return ( + discord.Embed( + title=self.card_name, + description=self.card_meaning, + colour=get_colour(), + url=self.card_url, + ) + .set_image(url=self.get_card_img(deck)) + .add_field(name="Arcana", value=str(self.arcana)) ) - embed.set_image(url=self.card_img) - return embed - - -TAROT_CARDS = {num: TarotCard(id=int(num), **data) for num, data in tarot_cards.items()} class TarotReading(commands.Cog): @@ -66,11 +301,17 @@ class TarotReading(commands.Cog): """ __author__ = ["TrustyJAID"] - __version__ = "1.2.0" + __version__ = "1.3.0" def __init__(self, bot): self.bot = bot super().__init__() + self.tarot_cards = { + num: TarotCard.from_json(id=int(num), data=data) for num, data in tarot_cards.items() + } + self.config = Config.get_conf(self, 218773382617890828) + self.config.register_guild(deck=None) + self.config.register_global(deck=None) def format_help_for_context(self, ctx: commands.Context) -> str: """ @@ -85,55 +326,120 @@ async def red_delete_data_for_user(self, **kwargs): """ return - def get_colour(self) -> int: - colour = "".join([choice("0123456789ABCDEF") for x in range(6)]) - return int(colour, 16) + async def get_deck(self, ctx: commands.Context) -> str: + if ctx.guild: + deck = await self.config.guild(ctx.guild).deck() + if deck: + return deck + return await self.config.deck() @commands.hybrid_group() async def tarot(self, ctx: commands.Context) -> None: """Receive a tarot reading""" pass - @tarot.command(name="life") - async def _life(self, ctx: commands.Context, user: Optional[discord.Member] = None) -> None: + @tarot.group(name="set", with_app_command=False) + async def tarot_set(self, ctx: commands.Context) -> None: + """Set commands for tarot""" + pass + + @tarot_set.command(name="deck") + @commands.mod_or_permissions(manage_messages=True) + @commands.guild_only() + async def _set_deck(self, ctx: commands.Context, deck_name: Optional[str] = None) -> None: """ - Unique reading based on your discord user ID. Doesn't change. + Set which deck to use from https://www.tarot.com/tarot/decks - `[user]` Optional user who you want to see a life tarot reading for. - If no user is provided this will run for the user who is running the command. + `deck_name` must be the name in the URL for the deck you want to use. + If not provided will revert to the default to the Rider–Waite Tarot Deck. + """ + if deck_name is None: + await self.config.guild(ctx.guild).deck.clear() + else: + await self.config.guild(ctx.guild).deck.set(deck_name) + card = random.choice(list(self.tarot_cards.values())) + em = card.embed(deck_name) + await ctx.send( + "Set this server's tarot deck to {deck_name}.".format( + deck_name=deck_name or "Rider–Waite" + ), + embed=em, + ) + + @tarot_set.command(name="globaldeck") + @commands.is_owner() + async def _set_global_deck( + self, ctx: commands.Context, deck_name: Optional[str] = None + ) -> None: """ - card_meaning = ["Past", "Present", "Future", "Potential", "Reason"] - if user is None: - user = ctx.message.author - userseed = user.id + Set which deck to use from https://www.tarot.com/tarot/decks - random.seed(int(userseed)) - cards = [] - cards = sample((range(1, 78)), 5) + This sets it for every server the bot is in by default. Servers + can specify their own deck to use via `[p]tarot set deck` + `deck_name` must be the name in the URL for the deck you want to use. + If not provided will revert to the default to the Rider–Waite Tarot Deck. + """ + if deck_name is None: + await self.config.deck.clear() + else: + await self.config.deck.set(deck_name) + card = random.choice(list(self.tarot_cards.values())) + em = card.embed(deck_name) + await ctx.send( + "Set the global tarot deck to {deck_name}.".format( + deck_name=deck_name or "Rider–Waite" + ), + embed=em, + ) + + async def tarot_reading( + self, ctx: commands.Context, user: Union[discord.Member, discord.User], cards: List[int] + ): embed = discord.Embed( title="Tarot reading for {}".format(user.display_name), - colour=discord.Colour(value=self.get_colour()), - url=TAROT_CARDS[str(cards[-1])].card_url, + colour=get_colour(), + url=self.tarot_cards[str(cards[-1])].card_url, ) - embed.set_thumbnail(url=TAROT_CARDS[str(cards[-1])].card_img) + deck = await self.get_deck(ctx) + embed.set_thumbnail(url=self.tarot_cards[str(cards[-1])].get_card_img(deck)) embed.timestamp = ctx.message.created_at embed.set_author(name=user.name, icon_url=user.display_avatar) - number = 0 - for card in cards: + for meaning in TarotMeaning: + try: + card_id = cards[meaning.value] + card = self.tarot_cards[str(card_id)] + except IndexError: + # incase this gets passed an incorrect number of cards + continue embed.add_field( - name="{0}: {1}".format(card_meaning[number], TAROT_CARDS[str(card)].card_name), - value=TAROT_CARDS[str(card)].card_meaning, + name="{meaning}: {name}".format(meaning=str(meaning), name=card.card_name), + value=f"__{card.arcana}__\n{card.card_meaning}", ) - number += 1 embeds = [] for card_number in cards: - card = TAROT_CARDS[str(card_number)] + card = self.tarot_cards[str(card_number)] em = embed.copy() - em.set_image(url=card.card_img) + em.set_image(url=card.get_card_img(deck)) embeds.append(em) await ctx.send(embeds=embeds) + @tarot.command(name="life") + async def _life(self, ctx: commands.Context, user: Optional[discord.Member] = None) -> None: + """ + Unique reading based on your discord user ID. Doesn't change. + + `[user]` Optional user who you want to see a life tarot reading for. + If no user is provided this will run for the user who is running the command. + """ + member = user or ctx.message.author + state = random.getstate() + random.seed(int(member.id)) + cards = [] + cards = random.sample((range(1, 78)), 5) + random.setstate(state) + await self.tarot_reading(ctx, member, cards) + @tarot.command(name="reading") async def _reading(self, ctx: commands.Context, user: Optional[discord.Member] = None) -> None: """ @@ -142,35 +448,10 @@ async def _reading(self, ctx: commands.Context, user: Optional[discord.Member] = `[user]` Optional user you want to view a tarot reading for. If no user is provided this will run for the user who is running the command. """ - card_meaning = ["Past", "Present", "Future", "Potential", "Reason"] - if user is None: - user = ctx.message.author - + member = user or ctx.message.author cards = [] - cards = sample((range(1, 78)), 5) - - embed = discord.Embed( - title="Tarot reading for {}".format(user.display_name), - colour=discord.Colour(value=self.get_colour()), - url=TAROT_CARDS[str(cards[-1])].card_url, - ) - embed.set_thumbnail(url=TAROT_CARDS[str(cards[-1])].card_img) - embed.timestamp = ctx.message.created_at - embed.set_author(name=user.name, icon_url=user.display_avatar) - number = 0 - for card in cards: - embed.add_field( - name="{0}: {1}".format(card_meaning[number], TAROT_CARDS[str(card)].card_name), - value=TAROT_CARDS[str(card)].card_meaning, - ) - number += 1 - embeds = [] - for card_number in cards: - card = TAROT_CARDS[str(card_number)] - em = embed.copy() - em.set_image(url=card.card_img) - embeds.append(em) - await ctx.send(embeds=embeds) + cards = random.sample((range(1, 78)), 5) + await self.tarot_reading(ctx, member, cards) @tarot.command(name="card") async def _card(self, ctx: commands.Context, *, tarot_card: Optional[TarotCard]) -> None: @@ -185,21 +466,22 @@ async def _card(self, ctx: commands.Context, *, tarot_card: Optional[TarotCard]) card = None if tarot_card is None: - card = TAROT_CARDS[str(random.randint(1, 78))] + card = self.tarot_cards[str(random.randint(1, 78))] else: card = tarot_card cards = [] - for c in TAROT_CARDS.values(): - embed = c.embed() + deck = await self.get_deck(ctx) + for c in self.tarot_cards.values(): + embed = c.embed(deck) embed.timestamp = ctx.message.created_at embed.set_author(name=user.name, icon_url=user.display_avatar) cards.append(embed) - menu = SimpleMenu(cards, page_start=card.id, use_select_menu=True) + menu = SimpleMenu(cards, page_start=card.id - 1, use_select_menu=True) options = [ - discord.SelectOption(label=c.card_name, description=c.id, value=str(c.id - 1)) - for c in TAROT_CARDS.values() + discord.SelectOption(label=c.card_name, description=str(c.id), value=str(c.id - 1)) + for c in self.tarot_cards.values() ] menu.select_options = options await menu.start(ctx) @@ -207,6 +489,6 @@ async def _card(self, ctx: commands.Context, *, tarot_card: Optional[TarotCard]) @_card.autocomplete("tarot_card") async def tarot_autocomplete(self, interaction: discord.Interaction, current: str): choices = [] - for _id, card in tarot_cards.items(): - choices.append(discord.app_commands.Choice(name=card["card_name"], value=_id)) + for _id, card in self.tarot_cards.items(): + choices.append(discord.app_commands.Choice(name=card.card_name, value=_id)) return [c for c in choices if current.lower() in c.name.lower()][:25]