package main import ( "database/sql" "fmt" "time" ) // SeedModeration creates reports, moderation_actions, user_strikes, user_suspensions. func SeedModeration(db *sql.DB, cfg Config, users []SeededUser, tracks []SeededTrack) error { fmt.Println("\n═══ MODERATION ═══") mods := GetModerators(users) admins := GetAdmins(users) moderators := append(mods, admins...) if len(moderators) == 0 { return nil } // ── 1. Reports ─────────────────────────────────────────────────────────── p := NewProgress("reports", cfg.Reports) reportRows := make([][]interface{}, 0, cfg.Reports) reportReasons := []string{ "spam", "harassment", "copyright", "inappropriate_content", "misleading", "hate_speech", "violence", "impersonation", } reportStatuses := []string{"pending", "pending", "reviewed", "resolved", "dismissed"} type reportInfo struct { id string } reports := make([]reportInfo, 0, cfg.Reports) for i := 0; i < cfg.Reports; i++ { id := newUUID() reporter := users[rng.Intn(len(users))] reason := pick(reportReasons) status := pick(reportStatuses) createdAt := RandomTimeAfter(reporter.CreatedAt) // Report target: user or track var reportedUserID interface{} var contentType string var contentID interface{} if randChance(60) && len(tracks) > 0 { t := tracks[rng.Intn(len(tracks))] contentType = "track" contentID = t.ID } else { u := users[rng.Intn(len(users))] reportedUserID = u.ID contentType = "user" } reports = append(reports, reportInfo{id: id}) reportRows = append(reportRows, []interface{}{ id, reporter.ID, reportedUserID, contentType, contentID, fmt.Sprintf("Reported for %s", reason), status, nil, nil, // resolved_by, resolved_at createdAt, pick(reportReasons), "normal", "", "", nil, // category, priority, resolution_note, resolution_action, assigned_to createdAt, }) } _, err := BulkInsert(db, "reports", "id, reporter_id, reported_user_id, content_type, content_id, reason, status, resolved_by, resolved_at, created_at, category, priority, resolution_note, resolution_action, assigned_to, updated_at", reportRows) if err != nil { return fmt.Errorf("insert reports: %w", err) } p.Update(cfg.Reports) p.Done() // ── 2. Moderation actions ──────────────────────────────────────────────── actionCount := cfg.Reports / 2 // ~50% of reports get an action p = NewProgress("moderation_actions", actionCount) actionRows := make([][]interface{}, 0, actionCount) actionTypes := []string{"warn", "mute", "suspend", "ban", "content_removal", "dismiss"} for i := 0; i < actionCount && i < len(reports); i++ { mod := moderators[rng.Intn(len(moderators))] action := pick(actionTypes) targetUser := users[rng.Intn(len(users))] actionRows = append(actionRows, []interface{}{ newUUID(), mod.ID, targetUser.ID, nil, nil, // target_content_type, target_content_id action, fmt.Sprintf("Action: %s — reviewed by moderator", action), "{}", time.Now(), }) } _, _ = BulkInsert(db, "moderation_actions", "id, moderator_id, target_user_id, target_content_type, target_content_id, action, reason, metadata, created_at", actionRows) p.Update(len(actionRows)) p.Done() // ── 3. User strikes ────────────────────────────────────────────────────── strikeCount := cfg.Reports / 5 p = NewProgress("user_strikes", strikeCount) strikeRows := make([][]interface{}, 0, strikeCount) strikeReasons := []string{"spam", "harassment", "copyright_violation", "inappropriate_content"} for i := 0; i < strikeCount; i++ { targetUser := users[rng.Intn(len(users))] mod := moderators[rng.Intn(len(moderators))] reason := pick(strikeReasons) severity := pick([]string{"warning", "minor", "major", "critical"}) expiresAt := time.Now().AddDate(0, 3, 0) // 3 months strikeRows = append(strikeRows, []interface{}{ newUUID(), targetUser.ID, nil, // report_id reason, severity, mod.ID, true, false, nil, false, nil, nil, nil, // is_active, appealed, appeal_text, appeal_resolved, appeal_result, appeal_resolved_by, appeal_resolved_at expiresAt, time.Now(), time.Now(), }) } _, _ = BulkInsert(db, "user_strikes", "id, user_id, report_id, reason, severity, issued_by, is_active, appealed, appeal_text, appeal_resolved, appeal_result, appeal_resolved_by, appeal_resolved_at, expires_at, created_at, updated_at", strikeRows) p.Update(len(strikeRows)) p.Done() // ── 4. User suspensions (very few) ─────────────────────────────────────── suspCount := strikeCount / 3 if suspCount < 2 { suspCount = 2 } p = NewProgress("user_suspensions", suspCount) suspRows := make([][]interface{}, 0, suspCount) for i := 0; i < suspCount; i++ { targetUser := users[randInt(len(users)/2, len(users)-1)] // Pick from second half (less important) mod := moderators[rng.Intn(len(moderators))] reason := pick(strikeReasons) suspendedUntil := time.Now().Add(time.Duration(randInt(1, 30)) * 24 * time.Hour) suspRows = append(suspRows, []interface{}{ newUUID(), targetUser.ID, fmt.Sprintf("Suspended for %s", reason), mod.ID, suspendedUntil, true, nil, nil, // is_active, lifted_by, lifted_at time.Now(), }) } _, _ = BulkInsert(db, "user_suspensions", "id, user_id, reason, suspended_by, suspended_until, is_active, lifted_by, lifted_at, created_at", suspRows) p.Update(len(suspRows)) p.Done() return nil }