Skip to content

Commit 70d01a3

Browse files
committed
hotfix(#4): tracking history messages for auto reply with silence
1 parent 3267fbe commit 70d01a3

File tree

2 files changed

+73
-9
lines changed

2 files changed

+73
-9
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ rules:
111111
enabled: true
112112
```
113113
114-
**Note:** The silence tracking is session-based (in-memory). After a restart, the first message from a sender won't trigger the auto-reply. This is intentional to avoid sending auto-replies when you've already responded via the web client or your phone.
114+
**Note:** The silence tracking is session-based (in-memory) and tracks BOTH incoming and outgoing messages. When you reply to someone, it resets the silence timer. After a restart, the router attempts to query the server for message history. If the query fails (server doesn't support the endpoint), it will skip the first message and start tracking from there to avoid sending unwanted auto-replies.
115115
116116
### Rule Properties
117117

main.go

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type MessageInfo struct {
6666
PersonID string `json:"personId"`
6767
Attachments []interface{} `json:"attachments"`
6868
Type string `json:"type"`
69+
Date int64 `json:"date,omitempty"` // Unix timestamp in milliseconds
6970
}
7071

7172
type OutgoingMessage struct {
@@ -75,6 +76,13 @@ type OutgoingMessage struct {
7576
Attachments []interface{} `json:"attachments"`
7677
}
7778

79+
// Message history structures
80+
type HistoricalMessage struct {
81+
Date int64 `json:"date"` // Unix timestamp in milliseconds
82+
Sender int `json:"sender"` // 0 = received, 1 = sent by you
83+
Text string `json:"text"`
84+
}
85+
7886
var (
7987
conn *websocket.Conn
8088
config Config
@@ -321,20 +329,28 @@ func handleAutoReplyAfterSilence(msg MessageInfo, rule Rule, senderID string) {
321329
return
322330
}
323331

324-
// Get last conversation time from in-memory map
325-
// Note: This only tracks messages seen during this session
326-
// For cross-restart tracking, the server would need to be queried for message history
332+
// Try to get last conversation time from in-memory map first
327333
lastTime, exists := lastMessageTimeMap[senderID]
334+
328335
if !exists {
329-
// First message from this sender in this session, don't auto-reply yet
330-
log.Printf("First message from %s in this session, tracking conversation time", msg.Author)
331-
return
336+
// Not in memory, try to fetch from server
337+
log.Printf("First message from %s in this session, fetching message history from server", msg.Author)
338+
serverLastTime, err := getLastMessageTimeFromServer(msg.ChatID)
339+
if err != nil {
340+
log.Printf("Warning: Could not fetch message history: %v", err)
341+
log.Printf("Skipping auto-reply for first message (cannot verify conversation history)")
342+
// Don't send auto-reply if we can't verify the silence period
343+
// This prevents sending unwanted auto-replies after restart
344+
return
345+
}
346+
lastTime = serverLastTime
347+
log.Printf("Retrieved last message time from server: %s", lastTime.Format("2006-01-02 15:04:05"))
332348
}
333-
349+
334350
// Calculate time since last conversation
335351
timeSinceLastMsg := time.Since(lastTime)
336-
requiredSilence := time.Duration(rule.SilenceDurationSecs) * time.Second
337352

353+
requiredSilence := time.Duration(rule.SilenceDurationSecs) * time.Second
338354
log.Printf("Time since last message from %s: %v (required: %v)",
339355
msg.Author, timeSinceLastMsg, requiredSilence)
340356

@@ -358,6 +374,54 @@ func handleAutoReplyAfterSilence(msg MessageInfo, rule Rule, senderID string) {
358374
}
359375
}
360376

377+
func getLastMessageTimeFromServer(chatID string) (time.Time, error) {
378+
protocol := "http"
379+
if config.SSL {
380+
protocol = "https"
381+
}
382+
383+
// Try to get messages for this chat - limit to last few messages to find the most recent
384+
url := fmt.Sprintf("%s://%s:%d/getMessages?chatId=%s&limit=10", protocol, config.ServerIP, config.ServerPort, chatID)
385+
386+
req, err := http.NewRequest("GET", url, nil)
387+
if err != nil {
388+
return time.Time{}, fmt.Errorf("create request: %w", err)
389+
}
390+
391+
req.Header.Set("Authorization", config.Password)
392+
393+
client := &http.Client{Timeout: 10 * time.Second}
394+
resp, err := client.Do(req)
395+
if err != nil {
396+
return time.Time{}, fmt.Errorf("http request: %w", err)
397+
}
398+
defer resp.Body.Close()
399+
400+
if resp.StatusCode != http.StatusOK {
401+
return time.Time{}, fmt.Errorf("server returned status: %d", resp.StatusCode)
402+
}
403+
404+
var messages []HistoricalMessage
405+
if err := json.NewDecoder(resp.Body).Decode(&messages); err != nil {
406+
return time.Time{}, fmt.Errorf("decode response: %w", err)
407+
}
408+
409+
if len(messages) == 0 {
410+
return time.Time{}, fmt.Errorf("no messages found for chat")
411+
}
412+
413+
// Find the most recent message (highest timestamp)
414+
var mostRecent int64 = 0
415+
for _, msg := range messages {
416+
if msg.Date > mostRecent {
417+
mostRecent = msg.Date
418+
}
419+
}
420+
421+
// Convert from milliseconds to time.Time
422+
return time.Unix(0, mostRecent*int64(time.Millisecond)), nil
423+
}
424+
361425
func sendMessage(msg OutgoingMessage) {
362426
// Use HTTP POST like the web client does
363427
protocol := "http"

0 commit comments

Comments
 (0)