Skip to content

Commit 065575b

Browse files
committed
Update to be simpler for re-inclusion into seabird
1 parent c836d3b commit 065575b

File tree

4 files changed

+82
-234
lines changed

4 files changed

+82
-234
lines changed

client.go

+66-205
Original file line numberDiff line numberDiff line change
@@ -9,257 +9,118 @@ import (
99
"net"
1010
"strconv"
1111
"strings"
12-
"sync"
1312
"time"
14-
15-
tomb "gopkg.in/tomb.v2"
1613
)
1714

1815
type Client struct {
16+
// Logger for messages. By defaut this will be a NilLogger
1917
Logger Logger
2018

21-
handler Handler
22-
connected bool
19+
// Internal things
2320
currentNick string
24-
nick string
25-
user string
26-
name string
27-
password string
28-
lock *sync.Mutex
2921
conn io.ReadWriteCloser
30-
t *tomb.Tomb
31-
write chan string
22+
in *bufio.Reader
3223
}
3324

34-
func NewClient(handler Handler, nick string, user string, name string, pass string) *Client {
35-
// Create the client
36-
c := &Client{
37-
nil,
38-
handler,
39-
false,
40-
nick,
41-
nick,
42-
user,
43-
name,
44-
pass,
45-
&sync.Mutex{},
46-
nil,
47-
&tomb.Tomb{},
48-
make(chan string),
25+
func Dial(addr string, nick, user, name, pass string) (*Client, error) {
26+
conn, err := net.Dial("tcp", addr)
27+
if err != nil {
28+
return nil, err
4929
}
5030

51-
return c
52-
}
53-
54-
func (c *Client) CurrentNick() string {
55-
return c.currentNick
31+
return NewClient(conn, nick, user, name, pass), nil
5632
}
5733

58-
func (c *Client) Dial(host string) error {
59-
c.lock.Lock()
60-
defer c.lock.Unlock()
61-
62-
if c.connected {
63-
return errors.New("Already connected")
64-
}
65-
66-
var err error
67-
c.conn, err = net.Dial("tcp", host)
34+
func DialTLS(addr string, c *tls.Config, nick, user, name, pass string) (*Client, error) {
35+
conn, err := tls.Dial("tcp", addr, c)
6836
if err != nil {
69-
return err
37+
return nil, err
7038
}
7139

72-
c.connected = true
73-
74-
return c.start()
40+
return NewClient(conn, nick, user, name, pass), nil
7541
}
7642

77-
func (c *Client) DialTLS(host string, conf *tls.Config) error {
78-
c.lock.Lock()
79-
defer c.lock.Unlock()
80-
81-
if c.connected {
82-
return errors.New("Already connected")
43+
func NewClient(rwc io.ReadWriteCloser, nick, user, name, pass string) *Client {
44+
// Create the client
45+
c := &Client{
46+
&NilLogger{},
47+
nick,
48+
rwc,
49+
bufio.NewReader(rwc),
8350
}
8451

85-
var err error
86-
c.conn, err = tls.Dial("tcp", host, conf)
87-
if err != nil {
88-
return err
52+
// Send the info we need to
53+
if len(pass) > 0 {
54+
c.Writef("PASS %s", pass)
8955
}
9056

91-
c.connected = true
57+
c.Writef("NICK %s", nick)
58+
c.Writef("USER %s 0.0.0.0 0.0.0.0 :%s", user, name)
9259

93-
return c.start()
60+
return c
61+
}
62+
63+
func (c *Client) CurrentNick() string {
64+
return c.currentNick
9465
}
9566

9667
func (c *Client) Write(line string) {
97-
// Try to write it to the writer. Fall back to waiting until the bot dies.
98-
select {
99-
case c.write <- line:
100-
case <-c.t.Dying():
68+
if c.Logger != nil {
69+
c.Logger.Debug("-->", line)
10170
}
71+
c.conn.Write([]byte(line))
72+
c.conn.Write([]byte("\r\n"))
10273
}
10374

10475
func (c *Client) Writef(format string, args ...interface{}) {
10576
c.Write(fmt.Sprintf(format, args...))
10677
}
10778

108-
func (c *Client) start() error {
109-
// Start up the tomb with all our loops.
110-
//
111-
// Note that it's only safe to call tomb.Go from inside
112-
// other functions that have been started the same way,
113-
// so we make a quick closure to take care of that.
114-
c.t.Go(func() error {
115-
// Ping Loop
116-
c.t.Go(c.pingLoop)
117-
118-
// Read Loop
119-
c.t.Go(c.readLoop)
120-
121-
// Write Loop
122-
c.t.Go(c.writeLoop)
123-
124-
// Cleanup Loop
125-
c.t.Go(c.cleanupLoop)
126-
127-
// Actually connect
128-
if len(c.password) > 0 {
129-
c.Writef("PASS %s", c.password)
130-
}
131-
132-
c.Writef("NICK %s", c.nick)
133-
c.Writef("USER %s 0.0.0.0 0.0.0.0 :%s", c.user, c.name)
79+
func (c *Client) ReadEvent() (*Event, error) {
80+
line, err := c.in.ReadString('\n')
81+
if err != nil {
82+
return nil, err
83+
}
13484

135-
return nil
136-
})
85+
if c.Logger != nil {
86+
c.Logger.Debug("<--", strings.TrimRight(line, "\r\n"))
87+
}
13788

138-
// This will wait until all goroutines in the Tomb die
139-
return c.t.Wait()
140-
}
89+
// Parse the event from our line
90+
e := ParseEvent(line)
14191

142-
func (c *Client) pingLoop() error {
143-
// Tick every 2 minutes
144-
t := time.NewTicker(2 * time.Minute)
145-
defer t.Stop()
146-
for {
147-
select {
148-
case <-t.C:
149-
c.Writef("PING :%d", time.Now().UnixNano())
150-
case <-c.t.Dying():
151-
return nil
152-
}
153-
}
154-
}
92+
// Now that we have the event parsed, do some preprocessing on it
93+
lastArg := e.Trailing()
15594

156-
func (c *Client) readLoop() error {
157-
in := bufio.NewReader(c.conn)
158-
for {
159-
// If we're dying exit out
160-
select {
161-
case <-c.t.Dying():
162-
return nil
163-
default:
164-
}
95+
// Clean up CTCP stuff so everyone
96+
// doesn't have to parse it manually
97+
if e.Command == "PRIVMSG" && len(lastArg) > 0 && lastArg[0] == '\x01' {
98+
e.Command = "CTCP"
16599

166-
line, err := in.ReadString('\n')
167-
if err != nil {
168-
return err
100+
if i := strings.LastIndex(lastArg, "\x01"); i > -1 {
101+
e.Args[len(e.Args)-1] = lastArg[1:i]
169102
}
103+
} else if e.Command == "PING" {
104+
c.Writef("PONG :%s", lastArg)
105+
} else if e.Command == "PONG" {
106+
ns, _ := strconv.ParseInt(lastArg, 10, 64)
107+
delta := time.Duration(time.Now().UnixNano() - ns)
170108

171109
if c.Logger != nil {
172-
c.Logger.Debug("<--", strings.TrimRight(line, "\r\n"))
110+
c.Logger.Info("!!! Lag:", delta)
173111
}
174-
175-
// Parse the event from our line
176-
e := ParseEvent(line)
177-
178-
// Now that we have the event parsed, do some preprocessing on it
179-
lastArg := e.Trailing()
180-
181-
// Clean up CTCP stuff so everyone
182-
// doesn't have to parse it manually
183-
if e.Command == "PRIVMSG" && len(lastArg) > 0 && lastArg[0] == '\x01' {
184-
e.Command = "CTCP"
185-
186-
if i := strings.LastIndex(lastArg, "\x01"); i > -1 {
187-
e.Args[len(e.Args)-1] = lastArg[1:i]
188-
}
189-
} else if e.Command == "PING" {
190-
c.Writef("PONG :%s", lastArg)
191-
} else if e.Command == "PONG" {
192-
ns, _ := strconv.ParseInt(lastArg, 10, 64)
193-
delta := time.Duration(time.Now().UnixNano() - ns)
194-
195-
if c.Logger != nil {
196-
c.Logger.Info("!!! Lag:", delta)
197-
}
198-
} else if e.Command == "NICK" {
199-
if e.Identity.Nick == c.currentNick && len(e.Args) > 0 {
200-
c.currentNick = e.Args[0]
201-
}
202-
} else if e.Command == "001" {
112+
} else if e.Command == "NICK" {
113+
if e.Identity.Nick == c.currentNick && len(e.Args) > 0 {
203114
c.currentNick = e.Args[0]
204-
} else if e.Command == "437" || e.Command == "433" {
205-
c.currentNick = c.currentNick + "_"
206-
c.Writef("NICK %s", c.currentNick)
207-
}
208-
209-
c.handler.HandleEvent(c, e)
210-
}
211-
}
212-
213-
func (c *Client) writeLoop() error {
214-
// Set up a rate limiter
215-
// Based on https://code.google.com/p/go-wiki/wiki/RateLimiting
216-
// 2 lps with a burst of 5
217-
throttle := make(chan time.Time, 5)
218-
ticker := time.NewTicker(500 * time.Millisecond)
219-
defer ticker.Stop()
220-
221-
go func() {
222-
for ns := range ticker.C {
223-
select {
224-
case throttle <- ns:
225-
default:
226-
}
227-
}
228-
}()
229-
230-
for {
231-
select {
232-
case line := <-c.write:
233-
select {
234-
case <-throttle:
235-
if c.Logger != nil {
236-
c.Logger.Debug("-->", line)
237-
}
238-
c.conn.Write([]byte(line))
239-
c.conn.Write([]byte("\r\n"))
240-
case <-c.t.Dying():
241-
}
242-
case <-c.t.Dying():
243-
return nil
244115
}
116+
} else if e.Command == "001" {
117+
c.currentNick = e.Args[0]
118+
} else if e.Command == "437" || e.Command == "433" {
119+
c.currentNick = c.currentNick + "_"
120+
c.Writef("NICK %s", c.currentNick)
245121
}
246-
}
247122

248-
func (c *Client) cleanupLoop() error {
249-
select {
250-
case <-c.t.Dying():
251-
c.conn.Close()
252-
}
253-
return nil
254-
}
255-
256-
func prepend(e interface{}, v []interface{}) []interface{} {
257-
var vc []interface{}
258-
259-
vc = append(vc, e)
260-
vc = append(vc, v...)
261-
262-
return vc
123+
return e, nil
263124
}
264125

265126
func (c *Client) MentionReply(e *Event, format string, v ...interface{}) error {
@@ -270,14 +131,14 @@ func (c *Client) MentionReply(e *Event, format string, v ...interface{}) error {
270131

271132
if e.FromChannel() {
272133
format = "%s: " + format
273-
274134
v = prepend(e.Identity.Nick, v)
275135
}
276136

277137
return c.Reply(e, format, v...)
278138
}
279139

280140
func (c *Client) CTCPReply(e *Event, format string, v ...interface{}) error {
141+
// Sanity check
281142
if len(e.Args) < 1 || len(e.Args[0]) < 1 {
282143
return errors.New("Invalid IRC event")
283144
}

0 commit comments

Comments
 (0)