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
97
98
99
100
101
102
103
104
105
106
107
|
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
closed bool // whether Close has been called
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
}
// Close cleanly closes all channels and stops all goroutines.
func (ts *Typings) Close() {
ts.l.Lock()
defer ts.l.Unlock()
close(ts.timeouts)
close(ts.stops)
ts.closed = true
}
// 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.l.Lock()
defer ts.l.Unlock()
if !ts.closed {
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
}
|