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:
@@ -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.
|
||||
|
||||
@@ -72,7 +72,22 @@ func (s *Server) HandleWebSocket(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// BulkGetPresence implements the gRPC PusherService.
|
||||
// When channel_ids is empty, returns all connected humanIDs across all channels
|
||||
// under the key "_all" — useful for checking overall online status.
|
||||
func (s *Server) BulkGetPresence(ctx context.Context, req *pbpusher.BulkGetPresenceRequest) (*pbpusher.BulkGetPresenceResponse, error) {
|
||||
// Empty channel_ids → return all connected humans
|
||||
if len(req.ChannelIds) == 0 {
|
||||
humanIDs, err := s.bridge.GetAllConnectedHumanIDs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbpusher.BulkGetPresenceResponse{
|
||||
Presences: map[string]*pbpusher.ChannelPresence{
|
||||
"_all": {HumanIds: humanIDs},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
presence, err := s.bridge.GetPresence(ctx, req.ChannelIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user