Skip to content

elizaos-plugins/plugin-wrapped

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@elizaos/plugin-wrapped

Year-end wrapped experience - stats, story, achievements, and community belonging. A personalized reflection on the user's year with the agent.

Philosophy

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.

Design Decisions

Why Dynamic Hooks?

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.

Why ASCII Art?

We use Unicode box-drawing characters because:

  1. Works everywhere - Any chat platform, any font
  2. Copy-pasteable - Users can share in text
  3. Retro aesthetic - Intentional visual choice
  4. Zero dependencies - Pure text, no images
┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│    847      │  │   23,456    │  │     156     │
│  MESSAGES   │  │   WORDS     │  │   CONVOS    │
│ Power user! │  │  ~47 pages  │  │ 1 per 2 days│
└─────────────┘  └─────────────┘  └─────────────┘

Why Chapters (Not Sections)?

"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.

Why Achievements?

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

Why Easter Eggs?

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.

Features

  • 📊 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!)

Shareable Card System

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 Types (10 total)

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

Why Cards Work

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

Card Design Philosophy

"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)

Technical Implementation

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
  }]
});

Installation

bun add @elizaos/plugin-wrapped

Usage

import { 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"

Output Structure

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)

Example Output

🎁 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

Achievements

Tenure (Loyalty)

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

Activity (Engagement)

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

Time Patterns (Identity)

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

Engagement Style

Badge Name Description Rarity
🧠 Curious Mind 200+ questions Uncommon
📖 Storyteller Average 40+ words/message Uncommon

Community

Badge Name Description Rarity
🏆 Top Contributor Top 10% in room Rare
🏛️ Community Pillar Top 10% + 6mo tenure Epic

Consistency (Streaks)

Badge Name Description Rarity
Streak Starter 7+ day streak Common
🔥 Streak Master 20+ day streak Uncommon
🌟 The Streak 100+ day streak Legendary

Easter Eggs

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)

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        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  │
└────────────────────────────┘

Card System Architecture

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)           │
└─────────────────────────────────────┘

API

Plugin

import { wrappedPlugin } from '@elizaos/plugin-wrapped';

Stats Functions

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';

Composers

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';

Types

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';

Card System API

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';

Card Types

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';

License

MIT

About

end of year fun

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published