threadr.lostcave.ddnss.de/models/chat.go

156 lines
4.5 KiB
Go

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, &timestampStr, &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, &timestampStr, &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
}