From 01c27150b773b578a24096caf5fa618ae3b19aa6 Mon Sep 17 00:00:00 2001 From: delthas Date: Mon, 12 Jul 2021 11:43:22 +0200 Subject: Ratelimit +typing and send +typing=done once only Fixes: #8 Fixes: #41 --- irc/session.go | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'irc/session.go') diff --git a/irc/session.go b/irc/session.go index dabec03..376015f 100644 --- a/irc/session.go +++ b/irc/session.go @@ -11,6 +11,8 @@ import ( "time" "unicode" "unicode/utf8" + + "golang.org/x/time/rate" ) type SASLClient interface { @@ -102,8 +104,8 @@ type Session struct { out chan<- Message closed bool registered bool - typings *Typings // incoming typing notifications. - typingStamps map[string]time.Time // user typing instants. + typings *Typings // incoming typing notifications. + typingStamps map[string]typingStamp // user typing instants. nick string nickCf string // casemapped nickname. @@ -134,7 +136,7 @@ func NewSession(out chan<- Message, params SessionParams) *Session { s := &Session{ out: out, typings: NewTypings(), - typingStamps: map[string]time.Time{}, + typingStamps: map[string]typingStamp{}, nick: params.Nickname, nickCf: CasemapASCII(params.Nickname), user: params.Username, @@ -342,10 +344,19 @@ func (s *Session) Typing(target string) { } targetCf := s.casemap(target) now := time.Now() - if t, ok := s.typingStamps[targetCf]; ok && now.Sub(t).Seconds() < 3.0 { + t, ok := s.typingStamps[targetCf] + if ok && ((t.Type == TypingActive && now.Sub(t.Last).Seconds() < 3.0) || !t.Limit.Allow()) { return } - s.typingStamps[targetCf] = now + if !ok { + t.Limit = rate.NewLimiter(rate.Limit(1.0/3.0), 5) + t.Limit.Reserve() // will always be OK + } + s.typingStamps[targetCf] = typingStamp{ + Last: now, + Type: TypingActive, + Limit: t.Limit, + } s.out <- NewMessage("TAGMSG", target).WithTag("+typing", "active") } @@ -353,6 +364,22 @@ func (s *Session) TypingStop(target string) { if !s.HasCapability("message-tags") { return } + targetCf := s.casemap(target) + now := time.Now() + t, ok := s.typingStamps[targetCf] + if ok && (t.Type == TypingDone || !t.Limit.Allow()) { + // don't send a +typing=done again if the last typing we sent was a +typing=done + return + } + if !ok { + t.Limit = rate.NewLimiter(rate.Limit(1), 5) + t.Limit.Reserve() // will always be OK + } + s.typingStamps[targetCf] = typingStamp{ + Last: now, + Type: TypingDone, + Limit: t.Limit, + } s.out <- NewMessage("TAGMSG", target).WithTag("+typing", "done") } -- cgit v1.2.3