Skip to content

Commit 87fcbcf

Browse files
authored
Merge pull request #4 from enzodjabali/3-implementing-an-auto-reply-after-some-time
3 implementing an auto reply after some time
2 parents 7b37fbe + 189af69 commit 87fcbcf

5 files changed

Lines changed: 134 additions & 10 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* @enzodjabali

.github/workflows/build.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Build and Publish to GitHub Packages on Tag
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*' # Matches any tag; adjust for specific patterns like 'v*'
7+
8+
jobs:
9+
build-and-publish:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
packages: write
14+
15+
steps:
16+
# Step 1: Checkout the code
17+
- name: Checkout code
18+
uses: actions/checkout@v3
19+
20+
# Step 2: Log in to GitHub Packages container registry
21+
- name: Log in to GitHub Container Registry
22+
uses: docker/login-action@v2
23+
with:
24+
registry: ghcr.io
25+
username: ${{ github.actor }}
26+
password: ${{ secrets.GITHUB_TOKEN }}
27+
28+
# Step 3: Build and tag the Docker image with version tag and 'latest'
29+
- name: Build and tag Docker image
30+
run: |
31+
TAG=$(echo ${GITHUB_REF#refs/tags/}) # Extract the tag
32+
IMAGE=ghcr.io/enzodjabali/iosmb-router
33+
docker build --build-arg VERSION=$TAG -t $IMAGE:$TAG .
34+
docker tag $IMAGE:$TAG $IMAGE:latest
35+
36+
# Step 4: Push the Docker image with version tag
37+
- name: Push Docker image with version tag
38+
run: |
39+
TAG=$(echo ${GITHUB_REF#refs/tags/}) # Extract the tag
40+
IMAGE=ghcr.io/enzodjabali/iosmb-router
41+
docker push $IMAGE:$TAG
42+
43+
# Step 5: Push the Docker image with 'latest' tag
44+
- name: Push Docker image with 'latest' tag
45+
run: |
46+
IMAGE=ghcr.io/enzodjabali/iosmb-router
47+
docker push $IMAGE:latest

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ go.work.sum
2727
# env file
2828
.env
2929

30+
# Rules file
31+
rules.yaml
32+
3033
# Editor/IDE
3134
# .idea/
3235
# .vscode/

main.go

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ type Rules struct {
3131
}
3232

3333
type Rule struct {
34-
Name string `yaml:"name"`
35-
Type string `yaml:"type"` // "redirect" or "auto_reply"
36-
FromSender string `yaml:"from_sender"`
37-
ToReceivers []string `yaml:"to_receivers,omitempty"`
38-
ReplyText string `yaml:"reply_text,omitempty"`
39-
Enabled bool `yaml:"enabled"`
34+
Name string `yaml:"name"`
35+
Type string `yaml:"type"` // "redirect", "auto_reply", or "auto_reply_after_silence"
36+
FromSender string `yaml:"from_sender"`
37+
ToReceivers []string `yaml:"to_receivers,omitempty"`
38+
ReplyText string `yaml:"reply_text,omitempty"`
39+
SilenceDurationSecs int `yaml:"silence_duration_secs,omitempty"` // Time in seconds before auto-reply triggers
40+
Enabled bool `yaml:"enabled"`
4041
}
4142

4243
// WebSocket message structures
@@ -75,14 +76,20 @@ type OutgoingMessage struct {
7576
}
7677

7778
var (
78-
conn *websocket.Conn
79-
config Config
80-
rules Rules
79+
conn *websocket.Conn
80+
config Config
81+
rules Rules
82+
lastConversationMap map[string]time.Time // Tracks last message time per sender
83+
autoReplyStatusMap map[string]bool // Tracks if auto-reply was already sent
8184
)
8285

8386
func main() {
8487
log.Println("iOSMB-Router starting...")
8588

89+
// Initialize tracking maps
90+
lastConversationMap = make(map[string]time.Time)
91+
autoReplyStatusMap = make(map[string]bool)
92+
8693
// Load configuration from environment or config file
8794
loadConfig()
8895

@@ -201,9 +208,21 @@ func processMessage(data interface{}) {
201208

202209
msg := msgData.Message[0]
203210

211+
// Get sender identifier (prefer ChatID, fallback to Author)
212+
senderID := msg.ChatID
213+
if senderID == "" {
214+
senderID = msg.Author
215+
}
216+
217+
// Update last conversation time for this sender
218+
currentTime := time.Now()
219+
lastConversationMap[senderID] = currentTime
220+
204221
// Skip messages sent by you (sender == 1)
205222
if msg.Sender == 1 {
206223
log.Printf("Skipping own message")
224+
// Reset auto-reply status when you send a message (conversation resumed)
225+
autoReplyStatusMap[senderID] = false
207226
return
208227
}
209228

@@ -231,6 +250,8 @@ func processMessage(data interface{}) {
231250
handleRedirect(msg, rule)
232251
case "auto_reply":
233252
handleAutoReply(msg, rule)
253+
case "auto_reply_after_silence":
254+
handleAutoReplyAfterSilence(msg, rule, senderID)
234255
default:
235256
log.Printf("Unknown rule type: %s", rule.Type)
236257
}
@@ -272,6 +293,48 @@ func handleAutoReply(msg MessageInfo, rule Rule) {
272293
sendMessage(outMsg)
273294
}
274295

296+
func handleAutoReplyAfterSilence(msg MessageInfo, rule Rule, senderID string) {
297+
// Check if auto-reply was already sent during this silence period
298+
if autoReplyStatusMap[senderID] {
299+
log.Printf("Auto-reply already sent to %s during this silence period", msg.Author)
300+
return
301+
}
302+
303+
// Get last conversation time
304+
lastTime, exists := lastConversationMap[senderID]
305+
if !exists {
306+
// First message from this sender, don't auto-reply yet
307+
log.Printf("First message from %s, tracking conversation time", msg.Author)
308+
return
309+
}
310+
311+
// Calculate time since last conversation
312+
timeSinceLastMsg := time.Since(lastTime)
313+
requiredSilence := time.Duration(rule.SilenceDurationSecs) * time.Second
314+
315+
log.Printf("Time since last message from %s: %v (required: %v)",
316+
msg.Author, timeSinceLastMsg, requiredSilence)
317+
318+
// Check if enough time has passed
319+
if timeSinceLastMsg >= requiredSilence {
320+
log.Printf("Auto-replying to %s after %v of silence", msg.Author, timeSinceLastMsg)
321+
322+
outMsg := OutgoingMessage{
323+
Address: msg.ChatID,
324+
Text: rule.ReplyText,
325+
Subject: "",
326+
Attachments: []interface{}{},
327+
}
328+
329+
sendMessage(outMsg)
330+
331+
// Mark that auto-reply was sent for this silence period
332+
autoReplyStatusMap[senderID] = true
333+
} else {
334+
log.Printf("Not enough silence time for %s, skipping auto-reply", msg.Author)
335+
}
336+
}
337+
275338
func sendMessage(msg OutgoingMessage) {
276339
// Use HTTP POST like the web client does
277340
protocol := "http"

rules.example.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,19 @@ rules:
1111
- "+33755442211"
1212
enabled: true
1313

14-
# Example: Auto-reply to a specific sender
14+
# Example: Auto-reply to a specific sender (immediate reply)
1515
- name: "Auto reply example"
1616
type: auto_reply
1717
from_sender: "+33233556699"
1818
reply_text: "I will answer you shortly"
1919
enabled: true
20+
21+
# Example: Auto-reply after silence period (e.g., 1 hour)
22+
# This will only reply if there hasn't been any conversation with the sender
23+
# for the specified duration. The reply is sent only once per silence period.
24+
- name: "Auto reply after 1 hour of silence"
25+
type: auto_reply_after_silence
26+
from_sender: "+33611223344"
27+
reply_text: "I will reply you soon"
28+
silence_duration_secs: 3600 # 1 hour = 3600 seconds (1h), 7200 = 2h, 1800 = 30mins
29+
enabled: true

0 commit comments

Comments
 (0)