Year-end wrapped experience - stats, story, achievements, and community belonging. A personalized reflection on the user's year with the agent.
This plugin is NOT a dashboard. It's a story.
Dashboards dump data. Stories create emotion. We want users to feel:
| Emotion | Example | Why It Matters |
|---|---|---|
| VALIDATED | "847 messages - that's a lot!" | Confirms their engagement was meaningful |
| NOSTALGIC | "Remember your first question?" | Creates emotional anchor to the past |
| PROUD | "You're in the Top 10%!" | Identity and social proof |
| BELONGING | "You contributed 4.2% of this room" | Part of something larger |
Every stat, every chapter, every achievement serves an emotional purpose.
The opening line is the MOST important part. Different users deserve different hooks:
Power user: "Top contributor. You're not just here. You're part of this."
Night owl: "847 messages. Most of them after dark."
Veteran: "One year ago, you typed your first message."
Curious mind: "423 questions in one year. Never stop asking."
A generic "Here are your stats!" fails to create the "they SEE me" moment.
We use Unicode box-drawing characters because:
- Works everywhere - Any chat platform, any font
- Copy-pasteable - Users can share in text
- Retro aesthetic - Intentional visual choice
- Zero dependencies - Pure text, no images
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 847 │ │ 23,456 │ │ 156 │
│ MESSAGES │ │ WORDS │ │ CONVOS │
│ Power user! │ │ ~47 pages │ │ 1 per 2 days│
└─────────────┘ └─────────────┘ └─────────────┘
"CHAPTER 2: THE JOURNEY" tells a story. "Section 2: Activity Data" dumps data.
The narrative structure creates emotional investment. Users read left-to-right, top-to-bottom, experiencing a journey through their year.
Achievements serve multiple purposes:
- Identity - "I'm a Night Owl" becomes part of self-concept
- Validation - Recognition for effort
- Aspiration - Locked badges create goals
- Shareability - "I got the legendary badge!" is social content
Easter eggs (42, 69, 420, 1337) reward attention and create shareable moments. They turn stats into stories. "I got 'The Answer'!" is a conversation starter.
- 📊 The Numbers: Personal stats with prominent display - message counts, time patterns, records
- 📜 Story Chapters: Narrative journey through the year
- Chapter 1: The Beginning - First message, origin story
- Chapter 2: The Journey - Monthly rhythm, patterns, streaks
- Chapter 3: The Growth - Q1 vs Q4 evolution, question complexity
- Chapter 4: Your Place - Community belonging, rank, contribution
- 🏆 Achievements: 18 unlockable badges based on activity
- 🎲 Fun Facts & Trivia: Quirky observations and easter eggs
- 📱 Shareable Card: Compact summary for sharing
- 🖼️ Shareable Image Cards: 10 beautiful PNG cards for social media (NEW!)
The plugin now generates beautiful PNG image cards that users can download and share on social media. Each card is designed to:
- Look premium (dark mode, modern typography)
- Work across all social platforms
- Make users proud to share
- Promote ElizaOS and the community
| Card | Purpose | Eligibility |
|---|---|---|
| Headline | Main stats (messages, conversations, streak) | Always |
| Rank | Community standing (TOP X%) | Top 25% only |
| Vibe | Archetype (Night Owl, Question Machine, etc.) | Clear pattern |
| Signature | Monthly activity "DNA" visualization | 50+ messages |
| Origin | First message quote | Safe quote exists |
| Evolution | Q1 vs Q4 quote comparison | Growth + quotes |
| 3AM | Late night quote | Safe late-night quote |
| Plot Twist | Unexpected stat (humor) | Interesting anomaly |
| Trophy | Achievement badges | 2+ badges |
| Invitation | Community promo | Always |
TEXT WRAPPED: "You sent 847 messages"
IMAGE CARD: [Beautiful PNG showing 847 in giant typography]
TEXT WRAPPED: Gets copied, loses formatting, looks generic
IMAGE CARD: Gets shared as-is, looks premium, goes viral
"Digital Artifact" - Not trying to copy Spotify. Our vibe:
- Dark mode native (works everywhere, feels premium)
- Generous whitespace (cards breathe)
- One hero element (each card has ONE thing that pops)
- Typography hierarchy (big numbers, subtle labels)
Cards are rendered using:
- satori: JSX → SVG (React-like syntax)
- resvg: SVG → PNG (Rust-based, high quality)
- Base64 data URLs: No file hosting needed
// Each card is sent as a message with an image attachment
await callback({
text: 'Chapter 1: The Numbers',
attachments: [{
url: card.dataUrl, // Base64 PNG
contentType: ContentType.IMAGE
}]
});bun add @elizaos/plugin-wrappedimport { wrappedPlugin } from '@elizaos/plugin-wrapped';
const agent = {
plugins: [wrappedPlugin],
};Users can trigger wrapped with phrases like:
- "show me my wrapped"
- "what was our year like"
- "give me my year in review"
- "how much have we talked"
- "my stats"
The output follows a deliberate emotional arc:
1. HOOK → Grab attention ("Remember...?")
2. THE NUMBERS → Validate importance (big stats in boxes)
3. CHAPTER 1 → Origin story (nostalgia)
4. CHAPTER 2 → The journey (rhythm, streaks)
5. CHAPTER 3 → Growth (evolution, progress)
6. CHAPTER 4 → Belonging (community, rank)
7. ACHIEVEMENTS → Identity (badges earned)
8. TRIVIA → Delight (fun facts, easter eggs)
9. CLOSE → Forward momentum ("Here's to next year")
10. CARD → Shareability (compact summary)
🎁 YOUR 2024 WRAPPED
━━━━━━━━━━━━━━━━━━━━
Remember "Can you help me debug this?"?
Look at you now.
📊 YOUR 2024 BY THE NUMBERS
━━━━━━━━━━━━━━━━━━━━━━━━━━━
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 847 │ │ 23,456 │ │ 156 │
│ MESSAGES │ │ WORDS │ │ CONVOS │
│ Power user! │ │ ~47 pages │ │ 1 per 2 days│
└─────────────┘ └─────────────┘ └─────────────┘
YOUR RECORDS
━━━━━━━━━━━━
🏆 Longest conversation 67 msgs Oct 23
📝 Longest message 312 words Oct 15
🔥 Longest streak 23 days Mar 3-26
🌙 Latest night owl 3:47 AM Feb 14
📈 CHAPTER 2: THE JOURNEY
━━━━━━━━━━━━━━━━━━━━━━━━━
From January to December, 156 conversations.
Once every 2.3 days, on average.
But averages hide the real story:
Jan ░░░░░░░░░░ 45 Jul ░░░░░░░░░░ 23
Feb ▓░░░░░░░░░ 67 Aug ▓░░░░░░░░░ 48
Mar ▓▓░░░░░░░░ 89 Sep ▓▓░░░░░░░░ 76
Apr ▓░░░░░░░░░ 72 Oct ▓▓▓▓▓░░░░░ 127 ← your peak
May ▓░░░░░░░░░ 54 Nov ▓▓░░░░░░░░ 94
Jun ░░░░░░░░░░ 31 Dec ▓▓░░░░░░░░ 71
October. 127 messages. Something was happening.
🏆 ACHIEVEMENTS UNLOCKED (9/18)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EARNED
✅ Year One 🎂 365+ days
✅ Night Owl 🦉 50+ msgs after midnight
✅ Curious Mind 🧠 200+ questions asked
✅ Streak Master 🔥 20+ day streak
| Badge | Name | Description | Rarity |
|---|---|---|---|
| 🎂 | Year One | Been here 365+ days | Uncommon |
| 🌱 | Early Adopter | Joined in first 30 days | Rare |
| 🏛️ | Founding Member | One of first 10 in room | Legendary |
| Badge | Name | Description | Rarity |
|---|---|---|---|
| 💬 | Conversation Starter | 50+ conversations | Common |
| 🤿 | Deep Diver | 10+ deep conversations | Uncommon |
| 💯 | Centurion | 100+ messages in one month | Rare |
| 🏃 | Marathon | 50+ message conversation | Rare |
| Badge | Name | Description | Rarity |
|---|---|---|---|
| 🦉 | Night Owl | 50+ messages after midnight | Uncommon |
| 🐦 | Early Bird | 50+ messages before 8am | Uncommon |
| ⚔️ | Weekend Warrior | 60%+ on weekends | Uncommon |
| 🌙 | Night Shift | 100+ messages 12am-5am | Epic |
| Badge | Name | Description | Rarity |
|---|---|---|---|
| 🧠 | Curious Mind | 200+ questions | Uncommon |
| 📖 | Storyteller | Average 40+ words/message | Uncommon |
| Badge | Name | Description | Rarity |
|---|---|---|---|
| 🏆 | Top Contributor | Top 10% in room | Rare |
| 🏛️ | Community Pillar | Top 10% + 6mo tenure | Epic |
| Badge | Name | Description | Rarity |
|---|---|---|---|
| ⚡ | Streak Starter | 7+ day streak | Common |
| 🔥 | Streak Master | 20+ day streak | Uncommon |
| 🌟 | The Streak | 100+ day streak | Legendary |
The plugin detects special numbers and patterns:
- 🥚 Nice. - Exactly 69 messages in a month
- 🥚 The Answer - 42-day streak (Hitchhiker's Guide reference)
- 🥚 Blaze It - Exactly 420 messages
- 🥚 L33T - 1337 messages
- 🥚 Palindrome - Message count reads the same backwards (e.g., 121)
┌─────────────────────────────────────────────────────────────────┐
│ ACTION LAYER │
│ GET_WRAPPED action handles triggers │
│ Orchestrates: data → text output → card gallery │
└────────────────────────────┬────────────────────────────────────┘
│
┌───────────────────┴───────────────────┐
▼ ▼
┌────────────────────────────┐ ┌────────────────────────────────┐
│ DATA LAYER │ │ CARD LAYER (NEW!) │
│ │ │ │
│ collector.ts - Raw msgs │ │ generator.ts - PNG rendering │
│ personal.ts - User stats │ │ selector.ts - Eligibility │
│ community.ts - Rankings │ │ quotes.ts - Safe quotes │
│ growth.ts - Evolution │ │ colors.ts - Theming │
│ streaks.ts - Streaks │ │ fonts.ts - Font loading │
│ │ │ │
└────────────┬───────────────┘ │ designs/ │
│ │ headline.tsx - Stats card │
▼ │ rank.tsx - TOP X% │
┌────────────────────────────┐ │ trophy.tsx - Badges │
│ COMPOSER LAYER │ │ vibe.tsx - Archetype │
│ │ │ signature.tsx - DNA viz │
│ hook.ts - Opening │ │ origin.tsx - First msg │
│ numbers.ts - Stats │ │ evolution.tsx - Q1 vs Q4 │
│ beginning.ts - Ch. 1 │ │ threeAm.tsx - Late night │
│ journey.ts - Ch. 2 │ │ plotTwist.tsx - Humor │
│ growth.ts - Ch. 3 │ │ invitation.tsx- Promo │
│ belonging.ts - Ch. 4 │ │ │
│ achievements - Badges │ └────────────────────────────────┘
│ trivia.ts - Fun facts │
│ close.ts - Ending │
│ card.ts - Text card │
└────────────┬───────────────┘
│
▼
┌────────────────────────────┐
│ UTILITY LAYER │
│ │
│ formatters.ts - ASCII art │
│ time.ts - Dates │
│ text.ts - Analysis │
└────────────────────────────┘
USER DATA (PersonalStats, StreakData, etc.)
│
▼
┌─────────────────────────────────────┐
│ ELIGIBILITY CHECK │
│ selector.ts: Which cards qualify? │
│ │
│ • Has enough messages? │
│ • In top 25%? │
│ • Safe quotes available? │
│ • Interesting anomaly found? │
└─────────────────┬───────────────────┘
│
▼
┌─────────────────────────────────────┐
│ CARD GENERATION │
│ │
│ For each eligible card: │
│ 1. Create JSX component │
│ 2. satori: JSX → SVG │
│ 3. resvg: SVG → PNG Buffer │
│ 4. Convert to Base64 data URL │
└─────────────────┬───────────────────┘
│
▼
┌─────────────────────────────────────┐
│ GALLERY OUTPUT │
│ │
│ Send cards in chapters: │
│ Ch 1: Numbers (Headline, Rank) │
│ Ch 2: Identity (Vibe, Signature) │
│ Ch 3: Story (Origin, Evolution...) │
│ Ch 4: Fun (Plot Twist, Trophy) │
│ Ch 5: Promo (Invitation) │
└─────────────────────────────────────┘
import { wrappedPlugin } from '@elizaos/plugin-wrapped';import {
collectUserMessages, // Get all messages for a user
computePersonalStats, // Calculate personal metrics
computeStreaks, // Calculate streak data
computeCommunityStats, // Calculate room rankings
computeGrowthStats, // Compare Q1 to Q4
evaluateAchievements, // Check which badges earned
} from '@elizaos/plugin-wrapped';import {
composeHookSection, // Opening line
composeNumbersSection, // Stats with comparisons
composeBeginningSection, // Chapter 1
composeJourneySection, // Chapter 2
composeGrowthSection, // Chapter 3
composeBelongingSection, // Chapter 4
composeAchievementsSection, // Badges
composeTriviaSection, // Fun facts
composeCloseSection, // Ending
composeCardSection, // Shareable card
} from '@elizaos/plugin-wrapped';import type {
PersonalStats, // User's message stats
StreakData, // Streak information
CommunityStats, // Room rankings
GrowthStats, // Q1 vs Q4 comparison
ComparisonStats, // Percentiles and averages
Achievement, // Badge definition
AchievementResult, // Badge evaluation result
WrappedData, // Complete wrapped bundle
FunFact, // Trivia item
EasterEgg, // Special number detection
} from '@elizaos/plugin-wrapped';import {
// Core rendering
renderCard, // Render JSX to PNG buffer
renderCards, // Render multiple cards in parallel
// Card components
HeadlineCard, // Main stats card
RankCard, // Community ranking card
TrophyCard, // Achievements card
VibeCard, // Archetype card
SignatureCard, // Activity DNA card
OriginCard, // First message card
EvolutionCard, // Q1 vs Q4 card
ThreeAmCard, // Late night card
PlotTwistCard, // Humor card
InvitationCard, // Promo card
// Eligibility
checkCardEligibility, // Check which cards user qualifies for
getEligibleCards, // Get list of eligible card types
// Quote safety
selectSafeQuote, // Find a safe quote to display
selectFirstSafeQuote, // Get first safe message
selectLateNightQuote, // Get safe 3AM quote
isQuoteSafe, // Check if text is safe to share
// Theming
selectTheme, // Choose theme based on behavior
themes, // Available theme presets
} from '@elizaos/plugin-wrapped';import type {
CardFormat, // 'square' | 'story' | 'wide'
RenderedCard, // { buffer, width, height, dataUrl }
CardType, // All card type names
CardEligibility, // Eligibility result for one card
EligibilityResult, // All eligibility results
QuoteCriteria, // Quote selection options
SelectedQuote, // Selected quote with metadata
VibeData, // Archetype information
PlotTwistData, // Plot twist headline/subtext
Theme, // Color theme
} from '@elizaos/plugin-wrapped';MIT