Skip to content

Latest commit

Β 

History

History
245 lines (191 loc) Β· 7.99 KB

working.md

File metadata and controls

245 lines (191 loc) Β· 7.99 KB

Let me explain the current streak implementation and how it works from frontend (Discord interactions) to backend (database and calculations).

Read , lines 1 to 30

Summarized conversation history

Read , lines 50 to 100

Summarized conversation history

Read , lines 100 to 150

Read , lines 150 to 264

Read , lines 1 to 50

Read , lines 80 to 120

Read , lines 1 to 80

Now I'll explain the complete streak implementation workflow from frontend to backend. Let me provide a comprehensive breakdown:

πŸ”₯ Streak Implementation Workflow: Frontend to Backend

1. Frontend User Experience (Discord)

User Posts Progress

  • User posts in the basher-progress category threads
  • Message must contain:
    • πŸ“Έ Image/screenshot attachment
    • ✍️ At least 50 words description
    • πŸ“ Posted in their own thread (thread owner only)

Slash Commands Available

/help     - Bot guide and requirements
/streak   - Check current streak (yours or someone else's)
/mystats  - View detailed personal statistics
/ping     - Bot health check

2. Message Processing Pipeline

Step 1: Message Validation (messageHandler.js)

// 1. Skip bot messages
if (message.author.bot) return;

// 2. Category validation - must be in basher-progress
if (!this.isInBasherProgressCategory(message)) return;

// 3. Thread ownership validation
if (message.author.id !== threadOwner) return;

// 4. Database-based duplicate prevention (deployment-safe)
const alreadyAwarded = await database.checkDailyPointsAwarded(memberId, description);
if (alreadyAwarded) {
    console.log(`Already received points today - ignoring`);
    return; // Silently skip validation for awarded users
}

Step 2: Criteria Validation (Only if not already awarded today)

// Image requirement check
const hasImage = message.attachments.some(attachment => 
    attachment.contentType?.startsWith('image/'));

// Word count validation
const wordCount = message.content.split(' ').filter(word => 
    word.length > 0).length;

// Must meet both criteria
if (!hasImage || wordCount < config.points.minimumWords) {
    await this.sendFeedback(message, 'Need screenshot + 50 words!');
    return;
}

3. Points & Streak Processing

Step 3: Award Points (supabase.js)

// Create points record with timestamp
const pointsResult = await database.createPoints(
    memberId, 
    config.points.dailyAmount,  // 10 points
    `PU-${dateString}`,         // Daily unique description
    'daily'                     // Point type
);

Step 4: Streak Calculation (streakService.js)

The core streak logic uses daily 11:59 PM cutoff:

// Daily cutoff configuration
this.dailyCutoffHour = 23;    // 11 PM  
this.dailyCutoffMinute = 59;  // 59 minutes
this.dailyCutoffSecond = 59;  // 59 seconds

Streak Algorithm:

  1. Fetch Historical Data: Get all points records for user
  2. Group by Date: Organize submissions by YYYY-MM-DD format
  3. Backward Date Traversal: Start from today, work backwards
  4. Cutoff Validation: Check if each submission was before 11:59:59 PM
  5. Consecutive Check: Count consecutive days with valid submissions
// Core streak calculation logic
while (true) {
    const dateKey = currentDate.toISOString().split('T')[0]; // YYYY-MM-DD
    
    if (dailySubmissions[dateKey] && dailySubmissions[dateKey].length > 0) {
        const validSubmission = submissions.some(submission => {
            const submissionTime = new Date(submission.created_at);
            const cutoffTime = new Date(submissionTime);
            cutoffTime.setHours(23, 59, 59, 999); // 11:59:59 PM
            
            return submissionTime <= cutoffTime; // Before midnight
        });
        
        if (validSubmission) {
            streak++;
        } else {
            console.log(`Submission after 11:59 PM - streak broken`);
            break;
        }
    } else {
        if (!isToday) {
            console.log(`No submission found - streak broken`);
            break;
        }
    }
    
    currentDate.setDate(currentDate.getDate() - 1); // Previous day
}

4. Database Persistence (supabase.js)

Tables Structure:

-- Points tracking with timestamps
points (id, member_id, amount, description, created_at, type)

-- Member statistics with streaks
member_stats (member_id, discord_streak, last_updated_at)

-- Member registration 
members (id, discord_username, created_at)

Key Database Operations:

// Check daily points already awarded (prevents duplicates)
checkDailyPointsAwarded(memberId, description)

// Award points with timestamp
createPoints(memberId, amount, description, type)

// Update streak in member_stats
updateDiscordStreak(memberId, newStreak)

// Get historical points for streak calculation
getPointsByMember(memberId)

5. Real-time User Feedback

Success Response:

const embed = new EmbedBuilder()
    .setColor('#00ff00')
    .setTitle('πŸŽ‰ Progress Recorded!')
    .setDescription(`+${points} points awarded!`)
    .addFields(
        { name: 'πŸ”₯ Current Streak', value: `${streakResult.currentStreak} days`, inline: true },
        { name: '🎯 Status', value: this.getStreakStatusMessage(streak), inline: true },
        { name: '⏰ Next Cutoff', value: `11:59 PM today`, inline: true }
    );

6. Deployment-Safe Architecture

No RAM Dependencies:

  • ❌ Before: Used in-memory cooldowns (lost on restart)
  • βœ… Now: Database-based duplicate prevention (persistent)

Daily Cutoff Benefits:

  • ❌ Before: 25-hour rolling window (confusing)
  • βœ… Now: Clear 11:59 PM daily boundary (predictable)

7. API Monitoring (server.js)

External monitoring endpoints:

GET /health          - Bot status check
GET /streak/:member  - Get member streak via API  
GET /stats/:member   - Get member statistics
POST /recalculate    - Admin streak recalculation

8. Error Handling & Edge Cases

Graceful Degradation:

// Missing user registration
if (!memberId) {
    await interaction.reply({ 
        content: `❌ Member not registered in system`,
        ephemeral: true 
    });
    return;
}

// Database errors
try {
    const streak = await streakService.calculateStreak(memberId);
} catch (error) {
    console.error('Streak calculation failed:', error);
    return 0; // Default to 0 instead of crashing
}

9. Complete Data Flow Summary

Discord Message β†’ Validation β†’ Points Award β†’ Streak Calculation β†’ Database Update β†’ User Feedback
     ↓               ↓              ↓               ↓                    ↓              ↓
[User Posts]   [Image+Words]   [10 Points]   [11:59 PM Rule]   [Supabase Store]  [Rich Embed]  
     ↓               ↓              ↓               ↓                    ↓              ↓
[Thread Only]  [Already Today?]  [PU-Date]   [Historical Scan]  [member_stats]   [Streak Info]

This system ensures:

  • βœ… Persistent streaks across bot restarts
  • βœ… Clear daily boundaries at 11:59 PM
  • βœ… No duplicate points per day
  • βœ… Rich user feedback with embeds
  • βœ… Deployment-safe architecture with database backing
  • βœ… Admin monitoring via API endpoints

The streak calculation is completely database-driven and recalculates from historical data each time, making it bulletproof against deployment changes and ensuring accuracy.