package discord import ( "context" "math/rand" "time" "github.com/rs/zerolog" ) type Heartbeat interface { Start(ctx context.Context, interval uint64) ForceHeartbeat() Ack() } var _ Heartbeat = &HeartbeatImpl{} type HeartbeatImpl struct { ctx context.Context logger *zerolog.Logger gateway Gateway } func NewHeartbeat(logger *zerolog.Logger, gateway Gateway) *HeartbeatImpl { return &HeartbeatImpl{ ctx: nil, logger: logger, gateway: gateway, } } func (h *HeartbeatImpl) Start(ctx context.Context, interval uint64) { h.ctx = ctx go h.heartbeatRoutine(interval) } func (h *HeartbeatImpl) ForceHeartbeat() { h.gateway.Heartbeat() } func (h *HeartbeatImpl) Ack() { // What do we do here? h.logger.Debug().Msg("received heartbeat ack") } func (h *HeartbeatImpl) heartbeatRoutine(interval uint64) { // REF: heartbeat_interval * jitter jitter := rand.Intn(int(interval)) time.Sleep(time.Duration(jitter) * time.Millisecond) ticker := time.NewTicker(time.Duration(interval) * time.Millisecond) defer ticker.Stop() for { select { case <-h.ctx.Done(): return case <-ticker.C: h.logger.Debug().Msg("sending heartbeat") if err := h.gateway.Heartbeat(); err != nil { h.logger.Error().Err(err).Msg("failed to send heartbeat") } } } }