DEV Community

Cover image for Building a Neon-Soaked Last.fm Roast Bot for $1.75/Year
Paige Bailey for Google AI

Posted on

Building a Neon-Soaked Last.fm Roast Bot for $1.75/Year

We all have that one song in our listening history that we hope nobody sees. But instead of hiding my shame, I decided to automate it!

I recently built my own custom AI bot on Poe called lastfm-roaster. Its only job is to look at a person's Last.fm music taste (if you're curious. here's mine as an example) and absolutely destroy them.

But there was a problem: The roasts were trapped in the chatbot interface. I wanted them delivered straight to my inbox every morning so I could easily forward the best (worst) burns to my friends for a laugh.

So, I built a pipeline.

Today, I’m going to show you how I connected my custom Poe bot to a Python script that pings the API, analyzes my listening history, and uses Regex to inject random neon colors into a beautiful HTML email.

Best of all? It runs entirely for free on GitHub Actions, and the API costs less than a cup of coffee for the entire year.

Onward!

Building the Bot 🤖

I went to Poe and created a new bot using their ScriptBot feature. I gave it a specific system prompt roughly like this:

"You are a pretentious music critic. Your job is to analyze Last.fm profiles and roast them mercilessly. Be sarcastic, use slang, and do not hold back."

After iterating back and forth with the ScriptBot until I was satisfied with the results, I had the intelligence (lastfm-roaster), but I needed to get the output out of Poe and into my email.

Keys and Secrets 🗝️

We are going to use GitHub Actions to run this, which means we need to keep our API keys safe. Never hardcode passwords in your script!

1. Get the Keys

  • Poe API Key: Go to poe.com/api_key and grab your key.
  • Gmail App Password: To send email via Python, you can't use your normal password. Go to your Google Account > Security > 2-Step Verification > App Passwords. Generate one and save that 16-character code.

2. Store them in GitHub

Create a new private repository on GitHub, then go to Settings > Secrets and variables > Actions.

Add these three secrets:

  • POE_API_KEY
  • EMAIL_ADDRESS
  • EMAIL_PASSWORD

Step 3: The Code 🐍

We need a few Python libraries to make this magic happen. Create a file named requirements.txt in your repo:

openai
markdown
Enter fullscreen mode Exit fullscreen mode

(Yes, we use openai! Poe's API is now compatible with the OpenAI client, making it super easy to use. Gemini 2.5 Flash is referenced via the Poe API, so I don't have to worry about managing that key, as well.)

The "Neon" Script (lastfm_roast.py)

Here is the cool part. I didn't want the email to look boring. I wanted the "insults" (the bold text) to pop in different colors.

We use Python's re (Regex) and itertools to find every <strong> tag my bot generates and cycle through a "Dracula" color palette to inject inline CSS styles.

import os
import smtplib
import markdown
import re
import itertools
from email.message import EmailMessage
from openai import OpenAI

# Configs (Loaded safely from GitHub Secrets)
POE_API_KEY = os.environ.get("POE_API_KEY")
EMAIL_ADDRESS = os.environ.get("EMAIL_ADDRESS")
EMAIL_PASSWORD = os.environ.get("EMAIL_PASSWORD")
LASTFM_URL = "https://www.last.fm/user/profoundlypaige"

# --- NEON PALETTE ---
# A list of bright colors that look good on dark backgrounds
# (Pink, Cyan, Green, Orange, Purple, Yellow)
COLORS = ["#FF79C6", "#8BE9FD", "#50FA7B", "#FFB86C", "#BD93F9", "#F1FA8C"]

def get_roast():
    """Pings the Poe API to get the roast."""
    client = OpenAI(api_key=POE_API_KEY, base_url="https://api.poe.com/v1")

    try:
        print("🔥 Fetching roast from Poe...")
        response = client.chat.completions.create(
            model="lastfm-roaster",
            messages=[
                {"role": "user", "content": f"Roast my music taste: {LASTFM_URL}"}
            ]
        )
        return response.choices[0].message.content
    except Exception as e:
        return f"Error fetching roast: {e}"

def inject_colors(html_content):
    """
    Finds every <strong> tag and injects a different color from the palette.
    """
    color_cycle = itertools.cycle(COLORS)

    def replace_match(match):
        next_color = next(color_cycle)
        # Returns <strong style="color: #CODE">
        return f'<strong style="color: {next_color}">'

    # Regex to replace <strong> with the colored version
    return re.sub(r'<strong>', replace_match, html_content)

def create_html_email(roast_text):
    # 1. Convert Markdown to basic HTML
    raw_html = markdown.markdown(roast_text)

    # 2. Inject the rotating neon colors into bold tags
    colorful_html = inject_colors(raw_html)

    # 3. Wrap in the styled container
    html_template = f"""
    <!DOCTYPE html>
    <html>
    <head>
    <style>
        body {{ margin: 0; padding: 0; background-color: #121212; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; }}

        .container {{ 
            max-width: 600px; 
            margin: 40px auto; 
            background-color: #1e1e1e; 
            border-radius: 16px; 
            overflow: hidden; 
            box-shadow: 0 10px 30px rgba(0,0,0,0.5); 
            border: 1px solid #333;
        }}

        .header {{ 
            background: linear-gradient(135deg, #2b2b2b 0%, #1a1a1a 100%); 
            padding: 30px; 
            text-align: center; 
            border-bottom: 2px solid #333;
        }}

        /* The title is now a gradient text effect */
        .header h1 {{ 
            margin: 0; font-size: 28px; letter-spacing: 2px; text-transform: uppercase; 
            background: -webkit-linear-gradient(#FF79C6, #8BE9FD);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }}

        .content {{ padding: 30px; color: #d1d5db; line-height: 1.7; font-size: 16px; }}

        h2 {{ 
            color: #ffffff; 
            border-left: 5px solid #BD93F9; /* Purple accent */
            padding-left: 15px;
            margin-top: 30px; 
            text-transform: uppercase;
            font-size: 18px;
            letter-spacing: 1px;
        }}

        ul {{ padding-left: 20px; }}
        li {{ margin-bottom: 10px; }}

        /* Styles for links */
        a {{ color: #8BE9FD; text-decoration: none; border-bottom: 1px dotted #8BE9FD; }}

        .footer {{ 
            background-color: #121212; 
            padding: 20px; 
            text-align: center; 
            font-size: 12px; 
            color: #555; 
        }}
    </style>
    </head>
    <body>
        <div class="container">
            <div class="header">
                <h1>🔥 The Daily Burn</h1>
            </div>
            <div class="content">
                {colorful_html}
            </div>
            <div class="footer">
                Served fresh by Poe API, Gemini 2.5 Flash, & GitHub Actions<br>
                <a href="{LASTFM_URL}" style="color:#555; border:none;">View your tragic Last.fm Profile</a>
            </div>
        </div>
    </body>
    </html>
    """
    return html_template

def send_email(roast_text):
    msg = EmailMessage()
    msg["Subject"] = "Your Daily Last.fm Roast 🎸"
    msg["From"] = EMAIL_ADDRESS
    msg["To"] = EMAIL_ADDRESS
    msg.set_content(roast_text)
    msg.add_alternative(create_html_email(roast_text), subtype='html')

    try:
        with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp:
            smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
            smtp.send_message(msg)
        print("✅ Email sent successfully!")
    except Exception as e:
        print(f"❌ Failed to send email: {e}")

if __name__ == "__main__":
    roast = get_roast()
    send_email(roast)
Enter fullscreen mode Exit fullscreen mode

Automating with GitHub Actions 🤖

I don't want to run this manually -- I want to be roasted automatically. So we'll use a GitHub Actions workflow to run this script every day at 12:00 UTC.

To create .github/workflows/daily_roast.yml:

name: Daily Lastfm Roast
on:
  schedule:
    - cron: '0 12 * * *' # Noon UTC, every day

jobs:
  roast:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: '3.9'
      - run: pip install -r requirements.txt
      - run: python lastfm_roast.py
        env:
          POE_API_KEY: ${{ secrets.POE_API_KEY }}
          EMAIL_ADDRESS: ${{ secrets.EMAIL_ADDRESS }}
          EMAIL_PASSWORD: ${{ secrets.EMAIL_PASSWORD }}
Enter fullscreen mode Exit fullscreen mode

Is it worth it? 💸

This is my favorite part. Poe charges "Compute Points" to run the underlying model that powers the bot (I used Gemini 2.5 Flash for the backend of my bot). I checked my consumption after a few test runs to see what kind of bill I was racking up:

Here is the breakdown from my dashboard:

  • Model: Gemini 2.5 Flash
  • Cost per Request: ~161 Compute Points
  • Dollar Cost: ~$0.0048 per roast

If I run this every single day for a year:
$0.0048 * 365 days = $1.75

$1.75 per year.

For less than two bucks, I can get a state-of-the-art LLM to analyze my listening trends and tell me my taste in indie pop is "derivative and sad" every single morning. That is high-value ROI. 📈

The Result: Before vs After

Before (Chat Interface):
Trapped in an app. Hard to share. Markdown text.

After (The Neon Upgrade):

Now, when I open my email, I get a sleek, dark-mode card. The band names—the targets of the insults—are highlighted in Pink, Cyan, and Green, making sure I don't miss exactly who I'm being mocked for listening to.

And because it's an email, I can instantly forward the roast to my friends so they, too, can laugh at my pain.

Wrapping Up

This pattern (Custom Bot + API + HTML Generation + Actions) is my go-to for almost all my personal automation. It’s robust, free to host, and creates genuinely fun daily interactions.

Repositories mentioned:

Let me know in the comments if you try this out, or share the worst roast the AI gave you, and happy coding! ✨

Top comments (0)