feat(bots): bot framework polish + store

- /ws/bot endpoint: bot token auth via query param, SHA-256 lookup
- Bot WS actions: SEND_MESSAGE + DELETE_MESSAGE handled in gateway
- Bot messages: bot_id on messages table, bot badge in chat (green + BOT tag)
- Bot store: /bots lists all bots with server count + add-to-server
- Bot manager moved to /bots/manage
- Fix: command routes were double-nested under /bots/{botID}/commands
- Fix: fetchServerCommands route corrected to /bots/servers/...
This commit is contained in:
2026-07-15 12:45:44 -04:00
parent 56af584ede
commit 5bdb758d23
13 changed files with 557 additions and 22 deletions
+41 -7
View File
@@ -159,6 +159,9 @@ type messageResponse struct {
AuthorID string `json:"author_id"`
AuthorName string `json:"author_username"`
DisplayName *string `json:"author_display_name"`
AuthorBot bool `json:"author_bot"`
BotID *string `json:"bot_id,omitempty"`
BotName *string `json:"bot_name,omitempty"`
Content string `json:"content"`
ReplyTo *string `json:"reply_to,omitempty"`
EditedAt *string `json:"edited_at"`
@@ -505,18 +508,20 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
var err error
if before != "" {
rows, err = h.db.QueryContext(r.Context(), `
SELECT m.id, m.channel_id, m.author_id, u.username, u.display_name, m.content, m.reply_to::text, m.edited_at::text, m.pinned, m.created_at::text
SELECT m.id, m.channel_id, m.author_id, u.username, u.display_name, m.content, m.reply_to::text, m.edited_at::text, m.pinned, m.created_at::text, m.bot_id, b.name
FROM messages m
JOIN users u ON m.author_id = u.id
LEFT JOIN bots b ON m.bot_id = b.id
WHERE m.channel_id = $1 AND m.created_at < (SELECT created_at FROM messages WHERE id = $2)
ORDER BY m.created_at DESC
LIMIT $3
`, channelID, before, limit)
} else {
rows, err = h.db.QueryContext(r.Context(), `
SELECT m.id, m.channel_id, m.author_id, u.username, u.display_name, m.content, m.reply_to::text, m.edited_at::text, m.pinned, m.created_at::text
SELECT m.id, m.channel_id, m.author_id, u.username, u.display_name, m.content, m.reply_to::text, m.edited_at::text, m.pinned, m.created_at::text, m.bot_id, b.name
FROM messages m
JOIN users u ON m.author_id = u.id
LEFT JOIN bots b ON m.bot_id = b.id
WHERE m.channel_id = $1
ORDER BY m.created_at DESC
LIMIT $2
@@ -534,7 +539,9 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
var editedAt sql.NullString
var createdAt sql.NullString
var replyTo sql.NullString
err := rows.Scan(&msg.ID, &msg.ChannelID, &msg.AuthorID, &msg.AuthorName, &msg.DisplayName, &msg.Content, &replyTo, &editedAt, &msg.Pinned, &createdAt)
var botID sql.NullString
var botName sql.NullString
err := rows.Scan(&msg.ID, &msg.ChannelID, &msg.AuthorID, &msg.AuthorName, &msg.DisplayName, &msg.Content, &replyTo, &editedAt, &msg.Pinned, &createdAt, &botID, &botName)
if err != nil {
continue
}
@@ -544,6 +551,13 @@ func (h *Handler) List(w http.ResponseWriter, r *http.Request) {
if editedAt.Valid {
msg.EditedAt = &editedAt.String
}
if botID.Valid {
msg.BotID = &botID.String
msg.AuthorBot = true
}
if botName.Valid {
msg.BotName = &botName.String
}
msg.CreatedAt = createdAt.String
messages = append(messages, msg)
}
@@ -802,9 +816,10 @@ func (h *Handler) Search(w http.ResponseWriter, r *http.Request) {
rows, err := h.db.QueryContext(r.Context(), `
SELECT m.id, m.channel_id, m.author_id, u.username, u.display_name, m.content, m.reply_to::text, m.edited_at::text, m.pinned, m.created_at::text,
ts_rank(m.search_vector, plainto_tsquery('english', $2)) AS rank
ts_rank(m.search_vector, plainto_tsquery('english', $2)) AS rank, m.bot_id, b.name
FROM messages m
JOIN users u ON m.author_id = u.id
LEFT JOIN bots b ON m.bot_id = b.id
WHERE m.channel_id = $1 AND m.search_vector @@ plainto_tsquery('english', $2)
ORDER BY rank DESC, m.created_at DESC
LIMIT $3
@@ -822,7 +837,9 @@ func (h *Handler) Search(w http.ResponseWriter, r *http.Request) {
var createdAt sql.NullString
var replyTo sql.NullString
var rank float64
err := rows.Scan(&msg.ID, &msg.ChannelID, &msg.AuthorID, &msg.AuthorName, &msg.DisplayName, &msg.Content, &replyTo, &editedAt, &msg.Pinned, &createdAt, &rank)
var botID sql.NullString
var botName sql.NullString
err := rows.Scan(&msg.ID, &msg.ChannelID, &msg.AuthorID, &msg.AuthorName, &msg.DisplayName, &msg.Content, &replyTo, &editedAt, &msg.Pinned, &createdAt, &rank, &botID, &botName)
if err != nil {
continue
}
@@ -832,6 +849,13 @@ func (h *Handler) Search(w http.ResponseWriter, r *http.Request) {
if editedAt.Valid {
msg.EditedAt = &editedAt.String
}
if botID.Valid {
msg.BotID = &botID.String
msg.AuthorBot = true
}
if botName.Valid {
msg.BotName = &botName.String
}
msg.CreatedAt = createdAt.String
messages = append(messages, msg)
}
@@ -1059,9 +1083,10 @@ func (h *Handler) ListPinned(w http.ResponseWriter, r *http.Request) {
}
rows, err := h.db.QueryContext(r.Context(), `
SELECT m.id, m.channel_id, m.author_id, u.username, u.display_name, m.content, m.reply_to::text, m.edited_at::text, m.pinned, m.created_at::text
SELECT m.id, m.channel_id, m.author_id, u.username, u.display_name, m.content, m.reply_to::text, m.edited_at::text, m.pinned, m.created_at::text, m.bot_id, b.name
FROM messages m
JOIN users u ON m.author_id = u.id
LEFT JOIN bots b ON m.bot_id = b.id
WHERE m.channel_id = $1 AND m.pinned = TRUE
ORDER BY m.created_at DESC
`, channelID)
@@ -1077,7 +1102,9 @@ func (h *Handler) ListPinned(w http.ResponseWriter, r *http.Request) {
var editedAt sql.NullString
var createdAt sql.NullString
var replyTo sql.NullString
err := rows.Scan(&msg.ID, &msg.ChannelID, &msg.AuthorID, &msg.AuthorName, &msg.DisplayName, &msg.Content, &replyTo, &editedAt, &msg.Pinned, &createdAt)
var botID sql.NullString
var botName sql.NullString
err := rows.Scan(&msg.ID, &msg.ChannelID, &msg.AuthorID, &msg.AuthorName, &msg.DisplayName, &msg.Content, &replyTo, &editedAt, &msg.Pinned, &createdAt, &botID, &botName)
if err != nil {
continue
}
@@ -1087,6 +1114,13 @@ func (h *Handler) ListPinned(w http.ResponseWriter, r *http.Request) {
if editedAt.Valid {
msg.EditedAt = &editedAt.String
}
if botID.Valid {
msg.BotID = &botID.String
msg.AuthorBot = true
}
if botName.Valid {
msg.BotName = &botName.String
}
msg.CreatedAt = createdAt.String
messages = append(messages, msg)
}