|
@@ -1,6 +1,10 @@
|
|
|
import random
|
|
|
+import os.path
|
|
|
+from io import BytesIO
|
|
|
|
|
|
-from rollbot import as_command
|
|
|
+from PIL import Image, ImageDraw, ImageFont
|
|
|
+
|
|
|
+from rollbot import as_command, Attachment
|
|
|
from rollbot.injection import Arg
|
|
|
|
|
|
# Extracted from https://labyrinthos.co/blogs/tarot-card-meanings-list
|
|
@@ -325,17 +329,59 @@ DECK = {
|
|
|
),
|
|
|
}
|
|
|
|
|
|
+SUIT_COLOR = {
|
|
|
+ "Pentacles": (0, 130, 3, 255),
|
|
|
+ "Swords": (170, 5, 140, 255), # TODO aaaaaaaaaaaaaa
|
|
|
+ "Cups": (23, 86, 247, 255),
|
|
|
+ "Wands": (249, 2, 1, 255),
|
|
|
+}
|
|
|
+
|
|
|
+NUMBER_DISPLAY = {
|
|
|
+ "Ace": "A",
|
|
|
+ "Two": "2",
|
|
|
+ "Three": "3",
|
|
|
+ "Four": "4",
|
|
|
+ "Five": "5",
|
|
|
+ "Six": "6",
|
|
|
+ "Seven": "7",
|
|
|
+ "Eight": "8",
|
|
|
+ "Nine": "9",
|
|
|
+ "Ten": "10",
|
|
|
+ "Page": "P",
|
|
|
+ "Knight": "N",
|
|
|
+ "Queen": "Q",
|
|
|
+ "King": "K",
|
|
|
+}
|
|
|
+
|
|
|
+CARD_W = 80
|
|
|
+# cards are 2x3
|
|
|
+CARD_H = 3 * CARD_W // 2
|
|
|
+# spacing of 1/8 the width
|
|
|
+CARD_GAP = CARD_W // 8
|
|
|
+CARD_WG = CARD_W + 2 * CARD_GAP
|
|
|
+# vertical offset of center card
|
|
|
+CARD_OFF = CARD_H // 10
|
|
|
+
|
|
|
+# https://fonts.google.com/specimen/Lumanosimo
|
|
|
+FONT = ImageFont.truetype(
|
|
|
+ os.path.join(os.path.dirname(os.path.realpath(__file__)), "Lumanosimo.ttf"),
|
|
|
+ CARD_W // 4,
|
|
|
+)
|
|
|
+
|
|
|
|
|
|
@as_command
|
|
|
def tarot(
|
|
|
hide: Arg(convert=lambda h: h.lower() == "hide", required=False, default=False),
|
|
|
- seven: Arg(convert=lambda h: h.lower() in ("7", "seven"), required=False, default=False),
|
|
|
+ seven: Arg(
|
|
|
+ convert=lambda h: h.lower() in ("7", "seven"), required=False, default=False
|
|
|
+ ),
|
|
|
):
|
|
|
"""
|
|
|
Get a tarot drawing. Include the argument 'hide' to hide explanations, or the argument 'seven' to get a 7 card draw.
|
|
|
"""
|
|
|
+ drawn_cards = []
|
|
|
message = ""
|
|
|
- for (p1, p2) in random.sample(DECK.keys(), 7 if seven else 3):
|
|
|
+ for p1, p2 in random.sample(list(DECK.keys()), 7 if seven else 3):
|
|
|
message += f"{p1} "
|
|
|
if "." not in p1: # minor arcana
|
|
|
message += "of "
|
|
@@ -346,4 +392,41 @@ def tarot(
|
|
|
if not hide:
|
|
|
message += f"\n {DECK[(p1, p2)][face]}"
|
|
|
message += "\n"
|
|
|
- return message.strip()
|
|
|
+ drawn_cards.append((p1, p2, face))
|
|
|
+
|
|
|
+ card_images = []
|
|
|
+ for p1, p2, face in drawn_cards:
|
|
|
+ with Image.new("RGBA", (CARD_W, CARD_H), color=(0, 0, 0, 0)) as image:
|
|
|
+ draw = ImageDraw.Draw(image)
|
|
|
+ draw.rounded_rectangle(
|
|
|
+ xy=(0, 0, CARD_W - 1, CARD_H - 1),
|
|
|
+ radius=8,
|
|
|
+ outline=(244, 170, 9, 255),
|
|
|
+ width=2,
|
|
|
+ fill=(71, 6, 47, 255),
|
|
|
+ )
|
|
|
+ draw.text(
|
|
|
+ xy=(8, 8),
|
|
|
+ text=NUMBER_DISPLAY.get(p1, p1),
|
|
|
+ fill=(244, 170, 9, 255),
|
|
|
+ font=FONT,
|
|
|
+ stroke_width=3,
|
|
|
+ )
|
|
|
+ draw.text(
|
|
|
+ xy=(8, 8),
|
|
|
+ text=NUMBER_DISPLAY.get(p1, p1),
|
|
|
+ fill=SUIT_COLOR.get(p2, (0, 0, 0, 0)),
|
|
|
+ font=FONT,
|
|
|
+ )
|
|
|
+ # TODO symbol
|
|
|
+ card_images.append(image if face == 0 else image.rotate(180))
|
|
|
+
|
|
|
+ with Image.new(
|
|
|
+ "RGBA", (len(card_images) * CARD_WG, CARD_H + CARD_OFF), color=(0, 0, 0, 0)
|
|
|
+ ) as image:
|
|
|
+ for i, card_img in enumerate(card_images):
|
|
|
+ image.paste(card_img, (i * CARD_WG, ((i + 1) % 2) * CARD_OFF))
|
|
|
+ with BytesIO() as buf:
|
|
|
+ image.save(buf, "PNG")
|
|
|
+ att = Attachment(name="image", body=buf.getvalue())
|
|
|
+ return message.strip(), att
|