package models import ( "database/sql" "time" "log" "regexp" ) 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 (not directly used for display, but for notifications) } // extractMentions finds all @username mentions in content and returns a slice of unique usernames. func extractMentions(content string) []string { r := regexp.MustCompile(`@([a-zA-Z0-9_]+)`) // Matches @ followed by one or more word characters matches := r.FindAllStringSubmatch(content, -1) uniqueMentions := make(map[string]struct{}) for _, match := range matches { if len(match) > 1 { uniqueMentions[match[1]] = struct{}{} } } var result []string for username := range uniqueMentions { // Corrected: uniqueMentions result = append(result, username) } return result } // CreateChatMessage inserts a new chat message and creates notifications for mentioned users. func CreateChatMessage(db *sql.DB, msg ChatMessage) (int, error) { query := "INSERT INTO chat_messages (user_id, content, reply_to, timestamp) VALUES (?, ?, ?, NOW())" result, err := db.Exec(query, msg.UserID, msg.Content, msg.ReplyTo) if err != nil { return 0, err } // Get the ID of the newly inserted message msgID64, err := result.LastInsertId() if err != nil { return 0, err } msgID := int(msgID64) // Create notifications for mentioned users mentionedUsernames := extractMentions(msg.Content) for _, username := range mentionedUsernames { mentionedUser, err := GetUserByUsername(db, username) // models/user.go GetUserByUsername if err != nil { // Log error but don't fail message creation for non-existent users log.Printf("Warning: Could not find mentioned user '%s' for notification: %v", username, err) continue } if mentionedUser != nil && mentionedUser.ID != msg.UserID { // Don't notify self notification := Notification{ UserID: mentionedUser.ID, Type: "chat_mention", RelatedID: msgID, // The ID of the newly created chat message Read: false, } err := CreateNotification(db, notification) // models/notification.go CreateNotification if err != nil { log.Printf("Error creating chat mention notification for user %d: %v", mentionedUser.ID, err) } } } return msgID, nil } 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 } 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 } 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 }