Browse Source

first pass at tarot render

Kirk Trombley 1 year ago
parent
commit
97ff7dbad1
2 changed files with 87 additions and 4 deletions
  1. BIN
      commands/commands/Lumanosimo.ttf
  2. 87 4
      commands/commands/tarot.py

BIN
commands/commands/Lumanosimo.ttf


+ 87 - 4
commands/commands/tarot.py

@@ -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