diff options
-rw-r--r-- | cmd/irc/main.go | 40 | ||||
-rw-r--r-- | irc/events.go | 9 | ||||
-rw-r--r-- | irc/states.go | 82 | ||||
-rw-r--r-- | irc/tokens.go | 6 | ||||
-rw-r--r-- | ui/ui.go | 6 |
5 files changed, 114 insertions, 29 deletions
diff --git a/cmd/irc/main.go b/cmd/irc/main.go index 2e76c3a..61ee39f 100644 --- a/cmd/irc/main.go +++ b/cmd/irc/main.go @@ -59,6 +59,8 @@ func main() { app.AddLine("home", "Connected to the server", time.Now()) case irc.SelfJoinEvent: app.AddBuffer(ev.Channel) + case irc.SelfPartEvent: + app.RemoveBuffer(ev.Channel) case irc.ChannelMessageEvent: line := formatIRCMessage(ev.Nick, ev.Content) app.AddLine(ev.Channel, line, ev.Time) @@ -94,11 +96,14 @@ func main() { case tcell.KeyBackspace2: app.InputBackspace() case tcell.KeyEnter: - content := app.InputEnter() - handleInput(app, &s, content) + buffer := app.CurrentBuffer() + input := app.InputEnter() + handleInput(&s, buffer, input) case tcell.KeyRune: app.InputRune(ev.Rune()) - s.Typing(app.CurrentBuffer()) + if app.CurrentBuffer() != "home" && !strings.HasPrefix(app.Input(), "/") { + s.Typing(app.CurrentBuffer()) + } } } } @@ -117,35 +122,44 @@ func parseCommand(s string) (command, args string) { i := strings.IndexByte(s, ' ') if i < 0 { - i = len(s) - 1 + i = len(s) } command = strings.ToUpper(s[1:i]) - args = s[i+1:] + args = strings.TrimLeft(s[i:], " ") return } -func handleInput(app *ui.UI, s *irc.Session, content string) { +func handleInput(s *irc.Session, buffer, content string) { cmd, args := parseCommand(content) switch cmd { case "": - ch := app.CurrentBuffer() - if ch == "home" { + if buffer == "home" { + return + } + + s.PrivMsg(buffer, args) + case "J", "JOIN": + s.Join(args) + case "PART": + if buffer == "home" { return } - s.PrivMsg(ch, args) + if args == "" { + args = buffer + } + + s.Part(args) case "ME": - ch := app.CurrentBuffer() - if ch == "home" { + if buffer == "home" { return } line := fmt.Sprintf("\x01ACTION %s\x01", args) - s.PrivMsg(ch, line) - default: + s.PrivMsg(buffer, line) } } diff --git a/irc/events.go b/irc/events.go index 2cd5214..c2af056 100644 --- a/irc/events.go +++ b/irc/events.go @@ -34,6 +34,15 @@ type UserJoinEvent struct { ChannelEvent } +type SelfPartEvent struct { + ChannelEvent +} + +type UserPartEvent struct { + UserEvent + ChannelEvent +} + type SelfJoinEvent struct { ChannelEvent } diff --git a/irc/states.go b/irc/states.go index 177c68f..40ac308 100644 --- a/irc/states.go +++ b/irc/states.go @@ -84,16 +84,23 @@ type Channel struct { type action interface{} type ( + actionJoin struct { + Channel string + } + actionPart struct { + Channel string + } + actionPrivMsg struct { - To string + Channel string Content string } actionTyping struct { - To string + Channel string } actionTypingStop struct { - To string + Channel string } ) @@ -137,9 +144,9 @@ type Session struct { func NewSession(conn io.ReadWriteCloser, params SessionParams) (s Session, err error) { s = Session{ conn: conn, - msgs: make(chan Message, 10), - acts: make(chan action, 10), - evts: make(chan Event, 10), + msgs: make(chan Message, 128), + acts: make(chan action, 128), + evts: make(chan Event, 128), typingStamps: map[string]time.Time{}, nick: params.Nickname, lNick: strings.ToLower(params.Nickname), @@ -205,17 +212,35 @@ func (s *Session) IsChannel(name string) bool { return strings.IndexAny(name, "#&") == 0 // TODO compute CHANTYPES } -func (s *Session) PrivMsg(to, content string) { - s.acts <- actionPrivMsg{to, content} +func (s *Session) Join(channel string) { + s.acts <- actionJoin{channel} +} + +func (s *Session) join(act actionJoin) (err error) { + err = s.send("JOIN %s\r\n", act.Channel) + return +} + +func (s *Session) Part(channel string) { + s.acts <- actionPart{channel} +} + +func (s *Session) part(act actionPart) (err error) { + err = s.send("PART %s\r\n", act.Channel) + return +} + +func (s *Session) PrivMsg(channel, content string) { + s.acts <- actionPrivMsg{channel, content} } func (s *Session) privMsg(act actionPrivMsg) (err error) { - err = s.send("PRIVMSG %s :%s\r\n", act.To, act.Content) + err = s.send("PRIVMSG %s :%s\r\n", act.Channel, act.Content) return } -func (s *Session) Typing(to string) { - s.acts <- actionTyping{to} +func (s *Session) Typing(channel string) { + s.acts <- actionTyping{channel} } func (s *Session) typing(act actionTyping) (err error) { @@ -223,13 +248,16 @@ func (s *Session) typing(act actionTyping) (err error) { return } - to := strings.ToLower(act.To) + to := strings.ToLower(act.Channel) + now := time.Now() - if t, ok := s.typingStamps[to]; ok && time.Now().Sub(t) < 3 { + if t, ok := s.typingStamps[to]; ok && now.Sub(t).Seconds() < 3.0 { return } - err = s.send("@+typing=active TAGMSG %s\r\n", act.To) + s.typingStamps[to] = now + + err = s.send("@+typing=active TAGMSG %s\r\n", act.Channel) return } @@ -242,7 +270,7 @@ func (s *Session) typingStop(act actionTypingStop) (err error) { return } - err = s.send("@+typing=done TAGMSG %s\r\n", act.To) + err = s.send("@+typing=done TAGMSG %s\r\n", act.Channel) return } @@ -256,6 +284,10 @@ func (s *Session) run() { select { case act := <-s.acts: switch act := act.(type) { + case actionJoin: + err = s.join(act) + case actionPart: + err = s.part(act) case actionPrivMsg: err = s.privMsg(act) case actionTyping: @@ -497,6 +529,19 @@ func (s *Session) handle(msg Message) (ev Event, err error) { c.Members[lNick] = "" ev = UserJoinEvent{ChannelEvent: channelEv, UserEvent: UserEvent{Nick: nick}} } + case "PART": + nick, _, _ := FullMask(msg.Prefix) + lNick := strings.ToLower(nick) + channel := strings.ToLower(msg.Params[0]) + channelEv := ChannelEvent{Channel: msg.Params[0]} + + if lNick == s.lNick { + delete(s.channels, channel) + ev = SelfPartEvent{ChannelEvent: channelEv} + } else if c, ok := s.channels[channel]; ok { + delete(c.Members, lNick) + ev = UserPartEvent{ChannelEvent: channelEv, UserEvent: UserEvent{Nick: nick}} + } case "353": // RPL_NAMREPLY channel := strings.ToLower(msg.Params[2]) @@ -623,3 +668,10 @@ func (s *Session) send(format string, args ...interface{}) (err error) { } // */ + +/* +func (s *Session) send(format string, args ...interface{}) (err error) { + go fmt.Fprintf(s.conn, format, args...) + return +} +// */ diff --git a/irc/tokens.go b/irc/tokens.go index 976207d..bbe7e8b 100644 --- a/irc/tokens.go +++ b/irc/tokens.go @@ -201,6 +201,12 @@ func (msg *Message) Validate() (err error) { } else if msg.Prefix == "" { err = errNoPrefix } + case "PART": + if len(msg.Params) < 1 { + err = errNotEnoughParams + } else if msg.Prefix == "" { + err = errNoPrefix + } case "353": if len(msg.Params) < 4 { err = errNotEnoughParams @@ -34,7 +34,7 @@ func New() (ui *UI, err error) { ui.screen.Clear() ui.screen.ShowCursor(0, h-2) - ui.Events = make(chan tcell.Event) + ui.Events = make(chan tcell.Event, 128) go func() { for !ui.ShouldExit() { ui.Events <- ui.screen.PollEvent() @@ -125,6 +125,10 @@ func (ui *UI) AddLine(buffer string, line string, t time.Time) { } } +func (ui *UI) Input() string { + return string(ui.textInput) +} + func (ui *UI) InputRune(r rune) { ui.textInput = append(ui.textInput, r) ui.textCursor++ |