feat: send email notifications for missed messages

- New cron job in cluster
- Handle presence and other concerns
- Toggle in app to disable email notifications
- Handles other edge cases such as cooldown period
- Simple html email with simple message

Closes #117
This commit is contained in:
talksik
2026-04-09 14:15:34 -07:00
parent 6fe5af8d8d
commit 51c4c6c822
25 changed files with 725 additions and 29 deletions
+35
View File
@@ -143,6 +143,41 @@ func (rb *RedisBridge) GetPresence(ctx context.Context, channelIDs []string) (ma
return result, nil
}
// GetAllConnectedHumanIDs scans all channel connection hashes in Redis and returns
// the deduplicated set of all humanIDs that have at least one active connection.
func (rb *RedisBridge) GetAllConnectedHumanIDs(ctx context.Context) ([]string, error) {
allHumanIDs := make(map[string]bool)
var cursor uint64
for {
keys, nextCursor, err := rb.client.Scan(ctx, cursor, channelConnsPrefix+"*"+channelConnsSuffix, 100).Result()
if err != nil {
return nil, fmt.Errorf("failed to scan channel keys: %w", err)
}
for _, key := range keys {
members, err := rb.client.HVals(ctx, key).Result()
if err != nil && err != redis.Nil {
continue
}
for _, humanID := range members {
allHumanIDs[humanID] = true
}
}
cursor = nextCursor
if cursor == 0 {
break
}
}
result := make([]string, 0, len(allHumanIDs))
for id := range allHumanIDs {
result = append(result, id)
}
return result, nil
}
// --- Pub/Sub listener (runs in its own goroutine) ---
// Listen subscribes to Redis Pub/Sub and forwards events to the local hub.