package forum import ( "fmt" "strings" "time" "dk/internal/database" ) // Forum represents a forum post or thread in the game type Forum struct { ID int Posted int64 LastPost int64 Author int Parent int Replies int Title string Content string } // New creates a new Forum with sensible defaults func New() *Forum { now := time.Now().Unix() return &Forum{ Posted: now, LastPost: now, Author: 0, Parent: 0, Replies: 0, Title: "", Content: "", } } // Validate checks if forum has valid values func (f *Forum) Validate() error { if strings.TrimSpace(f.Title) == "" { return fmt.Errorf("forum title cannot be empty") } if strings.TrimSpace(f.Content) == "" { return fmt.Errorf("forum content cannot be empty") } if f.Posted <= 0 { return fmt.Errorf("forum Posted timestamp must be positive") } if f.LastPost <= 0 { return fmt.Errorf("forum LastPost timestamp must be positive") } if f.Parent < 0 { return fmt.Errorf("forum Parent cannot be negative") } if f.Replies < 0 { return fmt.Errorf("forum Replies cannot be negative") } return nil } // CRUD operations func (f *Forum) Delete() error { return database.Exec("DELETE FROM forum WHERE id = %d", f.ID) } func (f *Forum) Insert() error { id, err := database.Insert("forum", f, "ID") if err != nil { return err } f.ID = int(id) return nil } // Query functions func Find(id int) (*Forum, error) { var forum Forum err := database.Get(&forum, "SELECT * FROM forum WHERE id = %d", id) if err != nil { return nil, fmt.Errorf("forum post with ID %d not found", id) } return &forum, nil } func All() ([]*Forum, error) { var forums []*Forum err := database.Select(&forums, "SELECT * FROM forum ORDER BY last_post DESC, id DESC") return forums, err } func Threads() ([]*Forum, error) { var forums []*Forum err := database.Select(&forums, "SELECT * FROM forum WHERE parent = 0 ORDER BY last_post DESC, id DESC") return forums, err } func ByParent(parentID int) ([]*Forum, error) { var forums []*Forum if parentID > 0 { // Replies sorted chronologically err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY posted ASC, id ASC", parentID) return forums, err } else { // Threads sorted by last activity err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY last_post DESC, id DESC", parentID) return forums, err } } func ByAuthor(authorID int) ([]*Forum, error) { var forums []*Forum err := database.Select(&forums, "SELECT * FROM forum WHERE author = %d ORDER BY posted DESC, id DESC", authorID) return forums, err } func Recent(limit int) ([]*Forum, error) { var forums []*Forum err := database.Select(&forums, "SELECT * FROM forum ORDER BY last_post DESC, id DESC LIMIT %d", limit) return forums, err } func Search(term string) ([]*Forum, error) { var forums []*Forum searchTerm := "%" + term + "%" err := database.Select(&forums, "SELECT * FROM forum WHERE title LIKE %s OR content LIKE %s ORDER BY last_post DESC, id DESC", searchTerm, searchTerm) return forums, err } func Since(since int64) ([]*Forum, error) { var forums []*Forum err := database.Select(&forums, "SELECT * FROM forum WHERE last_post >= %d ORDER BY last_post DESC, id DESC", since) return forums, err } // Helper methods func (f *Forum) PostedTime() time.Time { return time.Unix(f.Posted, 0) } func (f *Forum) LastPostTime() time.Time { return time.Unix(f.LastPost, 0) } func (f *Forum) SetPostedTime(t time.Time) { f.Posted = t.Unix() } func (f *Forum) SetLastPostTime(t time.Time) { f.LastPost = t.Unix() } func (f *Forum) IsThread() bool { return f.Parent == 0 } func (f *Forum) IsReply() bool { return f.Parent > 0 } func (f *Forum) HasReplies() bool { return f.Replies > 0 } func (f *Forum) IsRecentActivity() bool { return time.Since(f.LastPostTime()) < 24*time.Hour } func (f *Forum) ActivityAge() time.Duration { return time.Since(f.LastPostTime()) } func (f *Forum) PostAge() time.Duration { return time.Since(f.PostedTime()) } func (f *Forum) IsAuthor(userID int) bool { return f.Author == userID } func (f *Forum) Preview(maxLength int) string { if len(f.Content) <= maxLength { return f.Content } if maxLength < 3 { return f.Content[:maxLength] } return f.Content[:maxLength-3] + "..." } func (f *Forum) WordCount() int { if f.Content == "" { return 0 } // Simple word count by splitting on whitespace words := 0 inWord := false for _, char := range f.Content { if char == ' ' || char == '\t' || char == '\n' || char == '\r' { if inWord { words++ inWord = false } } else { inWord = true } } if inWord { words++ } return words } func (f *Forum) Length() int { return len(f.Content) } func (f *Forum) Contains(term string) bool { lowerTerm := strings.ToLower(term) return strings.Contains(strings.ToLower(f.Title), lowerTerm) || strings.Contains(strings.ToLower(f.Content), lowerTerm) } func (f *Forum) UpdateLastPost() { f.LastPost = time.Now().Unix() } func (f *Forum) IncrementReplies() { f.Replies++ } func (f *Forum) DecrementReplies() { if f.Replies > 0 { f.Replies-- } } func (f *Forum) GetReplies() ([]*Forum, error) { return ByParent(f.ID) } func (f *Forum) GetThread() (*Forum, error) { if f.IsThread() { return f, nil } return Find(f.Parent) }