~taiite/senpai

ref: a532aae30446d7d76c74780bebaf0a40c775694e senpai/irc/typing.go -rw-r--r-- 1.9 KiB
a532aae3Hubert Hirtz Fix history not being loaded on channel re-JOIN 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package irc

import (
	"sync"
	"time"

	"golang.org/x/time/rate"
)

// Typing is an event of Name actively typing in Target.
type Typing struct {
	Target string
	Name   string
}

// Typings keeps track of typing notification timeouts.
type Typings struct {
	l        sync.Mutex
	targets  map[Typing]time.Time // @+typing TAGMSG timestamps.
	timeouts chan Typing          // transmits unfiltered timeout notifications.
	stops    chan Typing          // transmits filtered timeout notifications.
}

// NewTypings initializes the Typings structures and filtering coroutine.
func NewTypings() *Typings {
	ts := &Typings{
		targets:  map[Typing]time.Time{},
		timeouts: make(chan Typing, 16),
		stops:    make(chan Typing, 16),
	}
	go func() {
		for t := range ts.timeouts {
			now := time.Now()
			ts.l.Lock()
			oldT, ok := ts.targets[t]
			if ok && 6.0 < now.Sub(oldT).Seconds() {
				delete(ts.targets, t)
				ts.l.Unlock()
				ts.stops <- t
			} else {
				ts.l.Unlock()
			}
		}
	}()
	return ts
}

// Stop cleanly closes all channels and stops all coroutines.
func (ts *Typings) Stop() {
	close(ts.timeouts)
	close(ts.stops)
}

// Stops is a channel that transmits typing timeouts.
func (ts *Typings) Stops() <-chan Typing {
	return ts.stops
}

// Active should be called when a user is typing to some target.
func (ts *Typings) Active(target, name string) {
	ts.l.Lock()
	t := Typing{target, name}
	ts.targets[t] = time.Now()
	ts.l.Unlock()

	go func() {
		time.Sleep(6 * time.Second)
		ts.timeouts <- t
	}()
}

// Done should be called when a user is done typing to some target.
func (ts *Typings) Done(target, name string) {
	ts.l.Lock()
	delete(ts.targets, Typing{target, name})
	ts.l.Unlock()
}

func (ts *Typings) List(target string) []string {
	ts.l.Lock()
	defer ts.l.Unlock()

	var res []string
	for t := range ts.targets {
		if target == t.Target {
			res = append(res, t.Name)
		}
	}
	return res
}

type typingStamp struct {
	Last  time.Time
	Type  int
	Limit *rate.Limiter
}