package tgerr import ( "context" "time" "github.com/gotd/td/clock" ) // ErrFloodWait is error type of "FLOOD_WAIT" error. const ErrFloodWait = "FLOOD_WAIT" // AsFloodWait returns wait duration and true boolean if err is // the "FLOOD_WAIT" error. // // Client should wait for that duration before issuing new requests with // same method. func AsFloodWait(err error) (d time.Duration, ok bool) { if rpcErr, ok := AsType(err, ErrFloodWait); ok { return time.Second * time.Duration(rpcErr.Argument), true } return 0, false } type floodWaitOptions struct { clock clock.Clock } // FloodWaitOption configures flood wait. type FloodWaitOption interface { apply(o *floodWaitOptions) } type floodWaitOptionFunc func(o *floodWaitOptions) func (f floodWaitOptionFunc) apply(o *floodWaitOptions) { f(o) } // FloodWaitWithClock sets time source for flood wait. func FloodWaitWithClock(c clock.Clock) FloodWaitOption { return floodWaitOptionFunc(func(o *floodWaitOptions) { o.clock = c }) } // FloodWait sleeps required duration and returns true if err is FLOOD_WAIT // or false and context or original error otherwise. func FloodWait(ctx context.Context, err error, opts ...FloodWaitOption) (bool, error) { opt := &floodWaitOptions{ clock: clock.System, } for _, o := range opts { o.apply(opt) } if d, ok := AsFloodWait(err); ok { timer := opt.clock.Timer(d + 1*time.Second) defer clock.StopTimer(timer) select { case <-timer.C(): return true, err case <-ctx.Done(): return false, ctx.Err() } } return false, err }