package models import ( "database/sql" "time" ) type ChatMessage struct { ID int UserID int Content string ReplyTo int // -1 if not a reply Timestamp time.Time Username string // For display, fetched from user PfpURL string // For display, fetched from user Mentions []string // List of mentioned usernames } func CreateChatMessage(db *sql.DB, msg ChatMessage) error { query := "INSERT INTO chat_messages (user_id, content, reply_to, timestamp) VALUES (?, ?, ?, NOW())" _, err := db.Exec(query, msg.UserID, msg.Content, msg.ReplyTo) return err } func GetRecentChatMessages(db *sql.DB, limit int) ([]ChatMessage, error) { query := ` SELECT cm.id, cm.user_id, cm.content, cm.reply_to, cm.timestamp, u.username, u.pfp_url FROM chat_messages cm JOIN users u ON cm.user_id = u.id ORDER BY cm.timestamp DESC LIMIT ?` rows, err := db.Query(query, limit) if err != nil { return nil, err } defer rows.Close() var messages []ChatMessage for rows.Next() { var msg ChatMessage var timestampStr string var pfpURL sql.NullString err := rows.Scan(&msg.ID, &msg.UserID, &msg.Content, &msg.ReplyTo, ×tampStr, &msg.Username, &pfpURL) if err != nil { return nil, err } msg.Timestamp, err = time.Parse("2006-01-02 15:04:05", timestampStr) if err != nil { msg.Timestamp = time.Time{} } if pfpURL.Valid { msg.PfpURL = pfpURL.String } // Parse mentions from content (simple @username detection) msg.Mentions = extractMentions(msg.Content) messages = append(messages, msg) } return messages, nil } func GetChatMessageByID(db *sql.DB, id int) (*ChatMessage, error) { query := ` SELECT cm.id, cm.user_id, cm.content, cm.reply_to, cm.timestamp, u.username, u.pfp_url FROM chat_messages cm JOIN users u ON cm.user_id = u.id WHERE cm.id = ?` row := db.QueryRow(query, id) var msg ChatMessage var timestampStr string var pfpURL sql.NullString err := row.Scan(&msg.ID, &msg.UserID, &msg.Content, &msg.ReplyTo, ×tampStr, &msg.Username, &pfpURL) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } msg.Timestamp, err = time.Parse("2006-01-02 15:04:05", timestampStr) if err != nil { msg.Timestamp = time.Time{} } if pfpURL.Valid { msg.PfpURL = pfpURL.String } msg.Mentions = extractMentions(msg.Content) return &msg, nil } func GetUsernamesMatching(db *sql.DB, prefix string) ([]string, error) { query := "SELECT username FROM users WHERE username LIKE ? LIMIT 10" rows, err := db.Query(query, prefix+"%") if err != nil { return nil, err } defer rows.Close() var usernames []string for rows.Next() { var username string if err := rows.Scan(&username); err != nil { return nil, err } usernames = append(usernames, username) } return usernames, nil } // Simple utility to extract mentions from content func extractMentions(content string) []string { var mentions []string var currentMention string inMention := false for _, char := range content { if char == '@' { inMention = true currentMention = "@" } else if inMention && (char == ' ' || char == '\n' || char == '\t') { if len(currentMention) > 1 { mentions = append(mentions, currentMention) } inMention = false currentMention = "" } else if inMention { currentMention += string(char) } } if inMention && len(currentMention) > 1 { mentions = append(mentions, currentMention) } return mentions }