-
- {{ targetLabel(item) }}
- {{ formatTime(item.created_at) }}
+
+
+
+
+
diff --git a/meshmap_frontend/src/types.ts b/meshmap_frontend/src/types.ts
index e84550e..ff1878a 100644
--- a/meshmap_frontend/src/types.ts
+++ b/meshmap_frontend/src/types.ts
@@ -143,6 +143,7 @@ export interface TextMessage {
packet_id: number | null
text: string | null
topic: string
+ channel_id: string | null
created_at: string
mqtt_remote_host: string | null
content_json: string
diff --git a/store_query.go b/store_query.go
index 6d6c6b9..50c53a3 100644
--- a/store_query.go
+++ b/store_query.go
@@ -9,15 +9,16 @@ import (
)
type listOptions struct {
- Limit int
- Offset int
- NodeID string
- Since *time.Time
- Until *time.Time
- MinLat *float64
- MaxLat *float64
- MinLng *float64
- MaxLng *float64
+ Limit int
+ Offset int
+ NodeID string
+ ChannelID string
+ Since *time.Time
+ Until *time.Time
+ MinLat *float64
+ MaxLat *float64
+ MinLng *float64
+ MaxLng *float64
}
type mapReportViewportOptions struct {
@@ -264,6 +265,27 @@ func (s *store) ListTextMessages(opts listOptions) ([]textMessageRecord, error)
return rows, s.listAppendRows(opts, &rows).Error
}
+func (s *store) ListBotDirectTextMessages(botNodeNum, targetNodeNum int64, opts listOptions) ([]textMessageRecord, error) {
+ opts = normalizeListOptions(opts)
+ var rows []textMessageRecord
+ q := s.db.Model(&textMessageRecord{}).
+ Where("(from_num = ? AND packet_to_num = ?) OR (from_num = ? AND packet_to_num = ?)", botNodeNum, targetNodeNum, targetNodeNum, botNodeNum).
+ Order("created_at DESC").
+ Order("id DESC").
+ Limit(opts.Limit).
+ Offset(opts.Offset)
+ if opts.ChannelID != "" {
+ q = q.Where("channel_id = ?", opts.ChannelID)
+ }
+ if opts.Since != nil {
+ q = q.Where("created_at >= ?", *opts.Since)
+ }
+ if opts.Until != nil {
+ q = q.Where("created_at <= ?", *opts.Until)
+ }
+ return rows, q.Find(&rows).Error
+}
+
func (s *store) ListDiscardDetails(opts listOptions) ([]discardDetailsRecord, error) {
opts = normalizeListOptions(opts)
var rows []discardDetailsRecord
@@ -328,6 +350,9 @@ func (s *store) listAppendRows(opts listOptions, dest any) *gorm.DB {
if opts.NodeID != "" {
q = q.Where("from_id = ?", opts.NodeID)
}
+ if opts.ChannelID != "" {
+ q = q.Where("channel_id = ?", opts.ChannelID)
+ }
if opts.Since != nil {
q = q.Where("created_at >= ?", *opts.Since)
}
diff --git a/web.go b/web.go
index b4bbfa5..c734a67 100644
--- a/web.go
+++ b/web.go
@@ -421,6 +421,7 @@ func parseListOptions(c *gin.Context) (listOptions, bool) {
if nodeID == "" {
nodeID = c.Query("from")
}
+ channelID := c.Query("channel_id")
var since, until *time.Time
if value := c.Query("since"); value != "" {
parsed, err := time.Parse(time.RFC3339, value)
@@ -438,7 +439,7 @@ func parseListOptions(c *gin.Context) (listOptions, bool) {
}
until = &parsed
}
- return normalizeListOptions(listOptions{Limit: limit, Offset: offset, NodeID: nodeID, Since: since, Until: until}), true
+ return normalizeListOptions(listOptions{Limit: limit, Offset: offset, NodeID: nodeID, ChannelID: channelID, Since: since, Until: until}), true
}
func parseMapReportListOptions(c *gin.Context) (listOptions, bool) {
@@ -599,7 +600,7 @@ func mapReportClusterDTO(row mapReportClusterRecord) gin.H {
}
func textMessageDTO(row textMessageRecord) gin.H {
- return gin.H{"id": row.ID, "from_id": row.FromID, "from_num": row.FromNum, "packet_id": ptrInt64(row.PacketID), "text": ptrString(row.Text), "topic": row.Topic, "created_at": row.CreatedAt, "mqtt_remote_host": ptrString(row.MQTTRemoteHost), "content_json": row.ContentJSON}
+ return gin.H{"id": row.ID, "from_id": row.FromID, "from_num": row.FromNum, "packet_id": ptrInt64(row.PacketID), "text": ptrString(row.Text), "topic": row.Topic, "channel_id": ptrString(row.ChannelID), "created_at": row.CreatedAt, "mqtt_remote_host": ptrString(row.MQTTRemoteHost), "content_json": row.ContentJSON}
}
func discardDetailsDTO(row discardDetailsRecord) gin.H {
+
+
+ 正在加载更早消息...
+ 没有更多历史消息
+ 当前频道暂无聊天消息
+
+
+
+
-
+ {{ senderName(item) }}
+ {{ formatTime(item.created_at) }}
- {{ statusText(item.status) }}
+
+ {{ item.text || '[binary]' }}
+ x{{ item.mergedCount }}
+
+ {{ item.topic }}
{{ item.text }}
-
- {{ item.channel_id }}
- #{{ item.packet_id }}
- {{ item.encrypted ? 'AES-CTR' : '明文' }}
-
- {{ item.error }}
- +
+
+
@@ -464,7 +607,7 @@ onMounted(() => {
diff --git a/meshmap_frontend/src/components/AdminBotDirect.vue b/meshmap_frontend/src/components/AdminBotDirect.vue
new file mode 100644
index 0000000..1400858
--- /dev/null
+++ b/meshmap_frontend/src/components/AdminBotDirect.vue
@@ -0,0 +1,315 @@
+
+
+
+
+
+
+
+
+
+
+ {{ sendTextBytes }} / {{ maxTextBytes }} bytes
+
+
+
+
+
+
+ Direct Bot Chat
+机器人私聊
+
+ 返回频道聊天
+
+
+ {{ error }}
+{{ notice }}
+ +
+
+
+
+
+
+
+
+
+ 正在加载更早消息...
+ 没有更多历史消息
+ 请选择机器人和目标节点,或当前会话暂无消息。
+
+
+
+
+ {{ senderName(item) }}{{ formatTime(item.created_at) }}
+ {{ item.text || '[binary]' }} x{{ item.mergedCount }}
+ {{ item.topic }}
+
+
+
+
+ {{ directTextBytes }} / {{ maxTextBytes }} bytes
+
+
+