func (s *Sender) Run(ctx context.Context) error {
	ticker := time.NewTicker(s.pollInterval)
	globalRateLimiter := rate.NewLimiter(rate.Every(time.Hour), s.globalRateLimitPerHour)
	for {
		select {
		case <-ctx.Done():
			ticker.Stop()
			return fmt.Errorf("context done")
		case <-ticker.C:
			_ = s.poll(ctx, globalRateLimiter)
		}
	}
}
Run blocks while running receive loop and returns when the context expires, or
when an unrecoverable error happens.
package gateway
import (
	"context"
	"fmt"
	"time"
	"github.com/regnull/easyecc"
	"github.com/regnull/ubikom/mail"
	"github.com/regnull/ubikom/pb"
	"github.com/regnull/ubikom/protoutil"
	"github.com/regnull/ubikom/util"
	"github.com/rs/zerolog/log"
	"golang.org/x/time/rate"
	"google.golang.org/grpc/codes"
)
type SenderOptions struct {
	PrivateKey             *easyecc.PrivateKey
	LookupClient           pb.LookupServiceClient
	DumpClient             pb.DMSDumpServiceClient
	GlobalRateLimitPerHour int
	PollInterval           time.Duration
	ExternalSender         ExternalSender
}
type Sender struct {
	privateKey             *easyecc.PrivateKey
	lookupClient           pb.LookupServiceClient
	dumpClient             pb.DMSDumpServiceClient
	globalRateLimitPerHour int
	pollInterval           time.Duration
	externalSender         ExternalSender
}
func NewSender(opts *SenderOptions) *Sender {
	return &Sender{
		privateKey:             opts.PrivateKey,
		lookupClient:           opts.LookupClient,
		dumpClient:             opts.DumpClient,
		globalRateLimitPerHour: opts.GlobalRateLimitPerHour,
		pollInterval:           opts.PollInterval,
		externalSender:         opts.ExternalSender}
}