252 lines
5.3 KiB
Go

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 newpostdate 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 newpostdate 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 postdate 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 newpostdate 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 postdate 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 newpostdate 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 newpostdate 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 newpostdate >= %d ORDER BY newpostdate 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)
}