mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-13 07:29:30 +01:00
cleanup and reorganization
This commit is contained in:
parent
2dba5f4c47
commit
55f7c89468
@ -11,3 +11,9 @@ I wanted to learn Go.
|
|||||||
### What's with the name?
|
### What's with the name?
|
||||||
|
|
||||||
"Ergonomadic" is an anagram of "Go IRC Daemon".
|
"Ergonomadic" is an anagram of "Go IRC Daemon".
|
||||||
|
|
||||||
|
### Helpful Documentation
|
||||||
|
|
||||||
|
- [IRC Channel Management](http://tools.ietf.org/html/rfc2811)
|
||||||
|
- [IRC Client Protocol](http://tools.ietf.org/html/rfc2812)
|
||||||
|
- [IRC Server Protocol](http://tools.ietf.org/html/rfc2813)
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
name string
|
name string
|
||||||
key string
|
key string
|
||||||
@ -12,10 +16,19 @@ type Channel struct {
|
|||||||
|
|
||||||
type ChannelSet map[*Channel]bool
|
type ChannelSet map[*Channel]bool
|
||||||
|
|
||||||
|
// NewChannel creates a new channel from a `Server` and a `name` string, which
|
||||||
|
// must be unique on the server.
|
||||||
func NewChannel(s *Server, name string) *Channel {
|
func NewChannel(s *Server, name string) *Channel {
|
||||||
return &Channel{name: name, members: make(ClientSet), invites: make(map[string]bool), server: s}
|
return &Channel{
|
||||||
|
name: name,
|
||||||
|
members: make(ClientSet),
|
||||||
|
invites: make(map[string]bool),
|
||||||
|
server: s,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send a `Reply` to all `Client`s of the `Channel`. Skip `fromClient`, if it is
|
||||||
|
// provided.
|
||||||
func (ch *Channel) Send(reply Reply, fromClient *Client) {
|
func (ch *Channel) Send(reply Reply, fromClient *Client) {
|
||||||
for client := range ch.members {
|
for client := range ch.members {
|
||||||
if client != fromClient {
|
if client != fromClient {
|
||||||
@ -24,7 +37,20 @@ func (ch *Channel) Send(reply Reply, fromClient *Client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ch *Channel) Nicks() []string {
|
||||||
|
nicks := make([]string, len(ch.members))
|
||||||
|
i := 0
|
||||||
|
for member := range ch.members {
|
||||||
|
nicks[i] = member.Nick()
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Strings(nicks)
|
||||||
|
return nicks
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
// channel functionality
|
// channel functionality
|
||||||
|
//
|
||||||
|
|
||||||
func (ch *Channel) Join(cl *Client, key string) {
|
func (ch *Channel) Join(cl *Client, key string) {
|
||||||
if ch.key != key {
|
if ch.key != key {
|
||||||
@ -42,10 +68,7 @@ func (ch *Channel) Join(cl *Client, key string) {
|
|||||||
|
|
||||||
ch.Send(RplJoin(ch, cl), nil)
|
ch.Send(RplJoin(ch, cl), nil)
|
||||||
ch.GetTopic(cl)
|
ch.GetTopic(cl)
|
||||||
|
cl.send <- RplNamReply(ch)
|
||||||
for member := range ch.members {
|
|
||||||
cl.send <- RplNamReply(ch, member)
|
|
||||||
}
|
|
||||||
cl.send <- RplEndOfNames(ch.server)
|
cl.send <- RplEndOfNames(ch.server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,12 +16,19 @@ type Client struct {
|
|||||||
registered bool
|
registered bool
|
||||||
invisible bool
|
invisible bool
|
||||||
channels ChannelSet
|
channels ChannelSet
|
||||||
|
server *Server
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientSet map[*Client]bool
|
type ClientSet map[*Client]bool
|
||||||
|
|
||||||
func NewClient(conn net.Conn) *Client {
|
func NewClient(server *Server, conn net.Conn) *Client {
|
||||||
client := &Client{conn: conn, recv: StringReadChan(conn), channels: make(ChannelSet), hostname: LookupHostname(conn.RemoteAddr())}
|
client := &Client{
|
||||||
|
channels: make(ChannelSet),
|
||||||
|
conn: conn,
|
||||||
|
hostname: LookupHostname(conn.RemoteAddr()),
|
||||||
|
recv: StringReadChan(conn),
|
||||||
|
server: server,
|
||||||
|
}
|
||||||
client.SetReplyToStringChan()
|
client.SetReplyToStringChan()
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
@ -38,12 +45,14 @@ func (c *Client) SetReplyToStringChan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Adapt `chan string` to a `chan Message`.
|
// Adapt `chan string` to a `chan Message`.
|
||||||
func (c *Client) Communicate(server *Server) {
|
func (c *Client) Communicate() {
|
||||||
for str := range c.recv {
|
for str := range c.recv {
|
||||||
m := ParseMessage(str)
|
m, err := ParseMessage(str)
|
||||||
if m != nil {
|
if err != nil {
|
||||||
server.recv <- &ClientMessage{c, m}
|
// TODO handle error
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
c.server.recv <- &ClientMessage{c, m}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,30 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type Message interface {
|
type Message interface {
|
||||||
Handle(s *Server, c *Client)
|
Handle(s *Server, c *Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotEnoughArgs = errors.New("not enough arguments")
|
||||||
|
ErrUModeUnknownFlag = errors.New("unknown umode flag")
|
||||||
|
)
|
||||||
|
|
||||||
// unknown
|
// unknown
|
||||||
|
|
||||||
type UnknownMessage struct {
|
type UnknownMessage struct {
|
||||||
command string
|
command string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NB: no constructor, created on demand in parser for invalid messages.
|
||||||
|
|
||||||
func (m *UnknownMessage) Handle(s *Server, c *Client) {
|
func (m *UnknownMessage) Handle(s *Server, c *Client) {
|
||||||
c.send <- ErrUnknownCommand(s, m.command)
|
c.send <- ErrUnknownCommand(s, m.command)
|
||||||
}
|
}
|
||||||
@ -21,6 +36,17 @@ type PingMessage struct {
|
|||||||
server2 string
|
server2 string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPingMessage(args []string) (Message, error) {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return nil, ErrNotEnoughArgs
|
||||||
|
}
|
||||||
|
msg := &PingMessage{server: args[0]}
|
||||||
|
if len(args) > 1 {
|
||||||
|
msg.server2 = args[1]
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *PingMessage) Handle(s *Server, c *Client) {
|
func (m *PingMessage) Handle(s *Server, c *Client) {
|
||||||
c.send <- RplPong(s)
|
c.send <- RplPong(s)
|
||||||
}
|
}
|
||||||
@ -32,8 +58,19 @@ type PongMessage struct {
|
|||||||
server2 string
|
server2 string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPongMessage(args []string) (Message, error) {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return nil, ErrNotEnoughArgs
|
||||||
|
}
|
||||||
|
message := &PongMessage{server1: args[0]}
|
||||||
|
if len(args) > 1 {
|
||||||
|
message.server2 = args[1]
|
||||||
|
}
|
||||||
|
return message, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *PongMessage) Handle(s *Server, c *Client) {
|
func (m *PongMessage) Handle(s *Server, c *Client) {
|
||||||
// TODO update client atime
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
// NICK
|
// NICK
|
||||||
@ -42,6 +79,13 @@ type NickMessage struct {
|
|||||||
nickname string
|
nickname string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewNickMessage(args []string) (Message, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrNotEnoughArgs
|
||||||
|
}
|
||||||
|
return &NickMessage{args[0]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *NickMessage) Handle(s *Server, c *Client) {
|
func (m *NickMessage) Handle(s *Server, c *Client) {
|
||||||
s.ChangeNick(c, m.nickname)
|
s.ChangeNick(c, m.nickname)
|
||||||
}
|
}
|
||||||
@ -55,8 +99,24 @@ type UserMessage struct {
|
|||||||
realname string
|
realname string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewUserMessage(args []string) (Message, error) {
|
||||||
|
if len(args) != 4 {
|
||||||
|
return nil, ErrNotEnoughArgs
|
||||||
|
}
|
||||||
|
msg := &UserMessage{
|
||||||
|
user: args[0],
|
||||||
|
unused: args[2],
|
||||||
|
realname: args[3],
|
||||||
|
}
|
||||||
|
mode, err := strconv.ParseUint(args[1], 10, 8)
|
||||||
|
if err == nil {
|
||||||
|
msg.mode = uint8(mode)
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *UserMessage) Handle(s *Server, c *Client) {
|
func (m *UserMessage) Handle(s *Server, c *Client) {
|
||||||
s.Register(c, m.user, m.realname)
|
s.UserLogin(c, m.user, m.realname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QUIT
|
// QUIT
|
||||||
@ -65,6 +125,14 @@ type QuitMessage struct {
|
|||||||
message string
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewQuitMessage(args []string) (Message, error) {
|
||||||
|
msg := &QuitMessage{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
msg.message = args[0]
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *QuitMessage) Handle(s *Server, c *Client) {
|
func (m *QuitMessage) Handle(s *Server, c *Client) {
|
||||||
s.Quit(c, m.message)
|
s.Quit(c, m.message)
|
||||||
}
|
}
|
||||||
@ -76,6 +144,28 @@ type ModeMessage struct {
|
|||||||
modes []string
|
modes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var MODE_RE = regexp.MustCompile("^[-+][a-zA-Z]+$")
|
||||||
|
|
||||||
|
func NewModeMessage(args []string) (Message, error) {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return nil, ErrNotEnoughArgs
|
||||||
|
}
|
||||||
|
msg := &ModeMessage{
|
||||||
|
nickname: args[0],
|
||||||
|
}
|
||||||
|
for _, arg := range args[1:] {
|
||||||
|
if !MODE_RE.MatchString(arg) {
|
||||||
|
return nil, ErrUModeUnknownFlag
|
||||||
|
}
|
||||||
|
prefix := arg[0]
|
||||||
|
for _, c := range arg[1:] {
|
||||||
|
mode := fmt.Sprintf("%c%c", prefix, c)
|
||||||
|
msg.modes = append(msg.modes, mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *ModeMessage) Handle(s *Server, c *Client) {
|
func (m *ModeMessage) Handle(s *Server, c *Client) {
|
||||||
if m.nickname != c.nick {
|
if m.nickname != c.nick {
|
||||||
c.send <- ErrUsersDontMatch(s)
|
c.send <- ErrUsersDontMatch(s)
|
||||||
@ -92,6 +182,22 @@ type JoinMessage struct {
|
|||||||
zero bool
|
zero bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewJoinMessage(args []string) (Message, error) {
|
||||||
|
msg := &JoinMessage{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
if args[0] == "0" {
|
||||||
|
msg.zero = true
|
||||||
|
} else {
|
||||||
|
msg.channels = strings.Split(args[0], ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
|
msg.keys = strings.Split(args[1], ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *JoinMessage) Handle(s *Server, c *Client) {
|
func (m *JoinMessage) Handle(s *Server, c *Client) {
|
||||||
if m.zero {
|
if m.zero {
|
||||||
for channel := range c.channels {
|
for channel := range c.channels {
|
||||||
@ -116,6 +222,17 @@ type PartMessage struct {
|
|||||||
message string
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPartMessage(args []string) (Message, error) {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return nil, ErrNotEnoughArgs
|
||||||
|
}
|
||||||
|
msg := &PartMessage{channels: strings.Split(args[0], ",")}
|
||||||
|
if len(args) > 1 {
|
||||||
|
msg.message = args[1]
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *PartMessage) Handle(s *Server, c *Client) {
|
func (m *PartMessage) Handle(s *Server, c *Client) {
|
||||||
for _, chname := range m.channels {
|
for _, chname := range m.channels {
|
||||||
channel := s.channels[chname]
|
channel := s.channels[chname]
|
||||||
@ -136,6 +253,16 @@ type PrivMsgMessage struct {
|
|||||||
message string
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPrivMsgMessage(args []string) (Message, error) {
|
||||||
|
if len(args) < 2 {
|
||||||
|
return nil, ErrNotEnoughArgs
|
||||||
|
}
|
||||||
|
return &PrivMsgMessage{
|
||||||
|
target: args[0],
|
||||||
|
message: args[1],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *PrivMsgMessage) TargetIsChannel() bool {
|
func (m *PrivMsgMessage) TargetIsChannel() bool {
|
||||||
switch m.target[0] {
|
switch m.target[0] {
|
||||||
case '&', '#', '+', '!':
|
case '&', '#', '+', '!':
|
||||||
@ -169,6 +296,17 @@ type TopicMessage struct {
|
|||||||
topic string
|
topic string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewTopicMessage(args []string) (Message, error) {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return nil, ErrNotEnoughArgs
|
||||||
|
}
|
||||||
|
msg := &TopicMessage{channel: args[0]}
|
||||||
|
if len(args) > 1 {
|
||||||
|
msg.topic = args[1]
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m *TopicMessage) Handle(s *Server, c *Client) {
|
func (m *TopicMessage) Handle(s *Server, c *Client) {
|
||||||
channel := s.channels[m.channel]
|
channel := s.channels[m.channel]
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
// channel management: http://tools.ietf.org/html/rfc2811
|
|
||||||
// client protocol: http://tools.ietf.org/html/rfc2812
|
|
||||||
// server protocol: http://tools.ietf.org/html/rfc2813
|
|
||||||
package irc
|
package irc
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -8,16 +5,19 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RPL_WELCOME = "001"
|
// # numeric codes
|
||||||
RPL_YOURHOST = "002"
|
// ## reply codes
|
||||||
RPL_CREATED = "003"
|
RPL_WELCOME = "001"
|
||||||
RPL_MYINFO = "004"
|
RPL_YOURHOST = "002"
|
||||||
RPL_UMODEIS = "221"
|
RPL_CREATED = "003"
|
||||||
RPL_NOTOPIC = "331"
|
RPL_MYINFO = "004"
|
||||||
RPL_TOPIC = "332"
|
RPL_UMODEIS = "221"
|
||||||
RPL_NAMREPLY = "353"
|
RPL_NOTOPIC = "331"
|
||||||
RPL_ENDOFNAMES = "366"
|
RPL_TOPIC = "332"
|
||||||
RPL_INFO = "371"
|
RPL_NAMREPLY = "353"
|
||||||
|
RPL_ENDOFNAMES = "366"
|
||||||
|
RPL_INFO = "371"
|
||||||
|
// ## error codes
|
||||||
ERR_NOSUCHNICK = "401"
|
ERR_NOSUCHNICK = "401"
|
||||||
ERR_NOSUCHSERVER = "402"
|
ERR_NOSUCHSERVER = "402"
|
||||||
ERR_NOSUCHCHANNEL = "403"
|
ERR_NOSUCHCHANNEL = "403"
|
||||||
@ -29,9 +29,10 @@ const (
|
|||||||
ERR_INVITEONLYCHANNEL = "473"
|
ERR_INVITEONLYCHANNEL = "473"
|
||||||
ERR_BADCHANNELKEY = "475"
|
ERR_BADCHANNELKEY = "475"
|
||||||
ERR_USERSDONTMATCH = "502"
|
ERR_USERSDONTMATCH = "502"
|
||||||
RPL_JOIN = "JOIN"
|
// # message codes
|
||||||
RPL_NICK = "NICK"
|
RPL_JOIN = "JOIN"
|
||||||
RPL_PART = "PART"
|
RPL_NICK = "NICK"
|
||||||
RPL_PONG = "PONG"
|
RPL_PART = "PART"
|
||||||
RPL_PRIVMSG = "PRIVMSG"
|
RPL_PONG = "PONG"
|
||||||
|
RPL_PRIVMSG = "PRIVMSG"
|
||||||
)
|
)
|
||||||
|
178
src/irc/parse.go
178
src/irc/parse.go
@ -1,13 +1,11 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var commands = map[string]func([]string) Message{
|
var commands = map[string]func([]string) (Message, error){
|
||||||
"JOIN": NewJoinMessage,
|
"JOIN": NewJoinMessage,
|
||||||
"MODE": NewModeMessage,
|
"MODE": NewModeMessage,
|
||||||
"NICK": NewNickMessage,
|
"NICK": NewNickMessage,
|
||||||
@ -20,169 +18,43 @@ var commands = map[string]func([]string) Message{
|
|||||||
"USER": NewUserMessage,
|
"USER": NewUserMessage,
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseMessage(line string) Message {
|
var (
|
||||||
|
ErrParseMessage = errors.New("failed to parse message")
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseMessage(line string) (msg Message, err error) {
|
||||||
command, args := parseLine(line)
|
command, args := parseLine(line)
|
||||||
constructor, ok := commands[command]
|
constructor, ok := commands[command]
|
||||||
var msg Message
|
if !ok {
|
||||||
if ok {
|
|
||||||
msg = constructor(args)
|
|
||||||
}
|
|
||||||
if msg == nil {
|
|
||||||
msg = &UnknownMessage{command}
|
msg = &UnknownMessage{command}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return msg
|
msg, err = constructor(args)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseArg(line string) (string, string) {
|
func parseArg(line string) (arg string, rest string) {
|
||||||
if line == "" {
|
if line == "" {
|
||||||
return "", ""
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(line, ":") {
|
if strings.HasPrefix(line, ":") {
|
||||||
return line[1:], ""
|
arg = line[1:]
|
||||||
|
} else {
|
||||||
|
parts := strings.SplitN(line, " ", 2)
|
||||||
|
arg = parts[0]
|
||||||
|
if len(parts) > 1 {
|
||||||
|
rest = parts[1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
parts := strings.SplitN(line, " ", 2)
|
|
||||||
arg := parts[0]
|
|
||||||
rest := ""
|
|
||||||
if len(parts) > 1 {
|
|
||||||
rest = parts[1]
|
|
||||||
}
|
|
||||||
return arg, rest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLine(line string) (string, []string) {
|
func parseLine(line string) (command string, args []string) {
|
||||||
args := make([]string, 0)
|
args = make([]string, 0)
|
||||||
for arg, rest := parseArg(line); arg != ""; arg, rest = parseArg(rest) {
|
for arg, rest := parseArg(line); arg != ""; arg, rest = parseArg(rest) {
|
||||||
args = append(args, arg)
|
args = append(args, arg)
|
||||||
}
|
}
|
||||||
return args[0], args[1:]
|
command, args = args[0], args[1:]
|
||||||
}
|
return
|
||||||
|
|
||||||
// []string => Message constructors
|
|
||||||
|
|
||||||
func NewNickMessage(args []string) Message {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &NickMessage{args[0]}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPingMessage(args []string) Message {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
message := &PingMessage{server: args[0]}
|
|
||||||
if len(args) > 1 {
|
|
||||||
message.server2 = args[1]
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPongMessage(args []string) Message {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
message := &PongMessage{server1: args[0]}
|
|
||||||
if len(args) > 1 {
|
|
||||||
message.server2 = args[1]
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewQuitMessage(args []string) Message {
|
|
||||||
msg := QuitMessage{}
|
|
||||||
if len(args) > 0 {
|
|
||||||
msg.message = args[0]
|
|
||||||
}
|
|
||||||
return &msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewUserMessage(args []string) Message {
|
|
||||||
if len(args) != 4 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
msg := new(UserMessage)
|
|
||||||
msg.user = args[0]
|
|
||||||
mode, err := strconv.ParseUint(args[1], 10, 8)
|
|
||||||
if err == nil {
|
|
||||||
msg.mode = uint8(mode)
|
|
||||||
}
|
|
||||||
msg.unused = args[2]
|
|
||||||
msg.realname = args[3]
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
var MODE_RE = regexp.MustCompile("^[-+][a-zA-Z]+$")
|
|
||||||
|
|
||||||
func NewModeMessage(args []string) Message {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
msg := new(ModeMessage)
|
|
||||||
msg.nickname = args[0]
|
|
||||||
for _, arg := range args[1:] {
|
|
||||||
if !MODE_RE.MatchString(arg) {
|
|
||||||
// TODO invalid args
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
prefix := arg[0]
|
|
||||||
for _, c := range arg[1:] {
|
|
||||||
mode := fmt.Sprintf("%c%c", prefix, c)
|
|
||||||
msg.modes = append(msg.modes, mode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewJoinMessage(args []string) Message {
|
|
||||||
msg := new(JoinMessage)
|
|
||||||
|
|
||||||
if len(args) > 0 {
|
|
||||||
if args[0] == "0" {
|
|
||||||
msg.zero = true
|
|
||||||
} else {
|
|
||||||
msg.channels = strings.Split(args[0], ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) > 1 {
|
|
||||||
msg.keys = strings.Split(args[1], ",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPartMessage(args []string) Message {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
msg := new(PartMessage)
|
|
||||||
msg.channels = strings.Split(args[0], ",")
|
|
||||||
|
|
||||||
if len(args) > 1 {
|
|
||||||
msg.message = args[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPrivMsgMessage(args []string) Message {
|
|
||||||
if len(args) < 2 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &PrivMsgMessage{target: args[0], message: args[1]}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTopicMessage(args []string) Message {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
message := &TopicMessage{channel: args[0]}
|
|
||||||
if len(args) > 1 {
|
|
||||||
message.topic = args[1]
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ func RplCreated(server *Server) Reply {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RplMyInfo(server *Server) Reply {
|
func RplMyInfo(server *Server) Reply {
|
||||||
return NewReply(server, RPL_MYINFO, server.name+" i ik")
|
return NewReply(server, RPL_MYINFO, fmt.Sprintf("%s %s i ik", server.name, VERSION))
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplUModeIs(server *Server, client *Client) Reply {
|
func RplUModeIs(server *Server, client *Client) Reply {
|
||||||
@ -92,9 +93,9 @@ func RplTopic(channel *Channel) Reply {
|
|||||||
return &ChannelReply{NewReply(channel.server, RPL_TOPIC, fmt.Sprintf("%s :%s", channel.name, channel.topic)), channel}
|
return &ChannelReply{NewReply(channel.server, RPL_TOPIC, fmt.Sprintf("%s :%s", channel.name, channel.topic)), channel}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplNamReply(channel *Channel, client *Client) Reply {
|
func RplNamReply(channel *Channel) Reply {
|
||||||
// TODO multiple names and splitting based on message size
|
// TODO multiple names and splitting based on message size
|
||||||
return NewReply(channel.server, RPL_NAMREPLY, fmt.Sprintf("=%s :+%s", channel.name, client.Nick()))
|
return NewReply(channel.server, RPL_NAMREPLY, fmt.Sprintf("= %s :%s", channel.name, strings.Join(channel.Nicks(), " ")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplEndOfNames(source Identifier) Reply {
|
func RplEndOfNames(source Identifier) Reply {
|
||||||
|
@ -22,7 +22,13 @@ type ClientMessage struct {
|
|||||||
|
|
||||||
func NewServer(name string) *Server {
|
func NewServer(name string) *Server {
|
||||||
recv := make(chan *ClientMessage)
|
recv := make(chan *ClientMessage)
|
||||||
server := &Server{ctime: time.Now(), name: name, recv: recv, nicks: make(map[string]*Client), channels: make(map[string]*Channel)}
|
server := &Server{
|
||||||
|
ctime: time.Now(),
|
||||||
|
name: name,
|
||||||
|
recv: recv,
|
||||||
|
nicks: make(map[string]*Client),
|
||||||
|
channels: make(map[string]*Channel),
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
for m := range recv {
|
for m := range recv {
|
||||||
m.message.Handle(server, m.client)
|
m.message.Handle(server, m.client)
|
||||||
@ -47,7 +53,7 @@ func (s *Server) Listen(addr string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Print("Server.Listen: accepted ", conn.RemoteAddr())
|
log.Print("Server.Listen: accepted ", conn.RemoteAddr())
|
||||||
go NewClient(conn).Communicate(s)
|
go NewClient(s, conn).Communicate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +71,7 @@ func (s *Server) GetOrMakeChannel(name string) *Channel {
|
|||||||
// Send a message to clients of channels fromClient is a member.
|
// Send a message to clients of channels fromClient is a member.
|
||||||
func (s *Server) SendToInterestedClients(fromClient *Client, reply Reply) {
|
func (s *Server) SendToInterestedClients(fromClient *Client, reply Reply) {
|
||||||
clients := make(map[*Client]bool)
|
clients := make(map[*Client]bool)
|
||||||
|
clients[fromClient] = true
|
||||||
for channel := range fromClient.channels {
|
for channel := range fromClient.channels {
|
||||||
for client := range channel.members {
|
for client := range channel.members {
|
||||||
clients[client] = true
|
clients[client] = true
|
||||||
@ -84,28 +91,29 @@ func (s *Server) ChangeNick(c *Client, newNick string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.SendToInterestedClients(c, RplNick(c, newNick))
|
|
||||||
|
|
||||||
if c.nick != "" {
|
if c.nick != "" {
|
||||||
delete(s.nicks, c.nick)
|
delete(s.nicks, c.nick)
|
||||||
}
|
}
|
||||||
c.nick = newNick
|
|
||||||
s.nicks[c.nick] = c
|
s.nicks[c.nick] = c
|
||||||
|
|
||||||
s.TryRegister(c)
|
s.SendToInterestedClients(c, RplNick(c, newNick))
|
||||||
|
|
||||||
|
c.nick = newNick
|
||||||
|
|
||||||
|
s.tryRegister(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Register(c *Client, user string, realName string) {
|
func (s *Server) UserLogin(c *Client, user string, realName string) {
|
||||||
if c.username != "" {
|
if c.username != "" {
|
||||||
c.send <- ErrAlreadyRegistered(s)
|
c.send <- ErrAlreadyRegistered(s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.username, c.realname = user, realName
|
c.username, c.realname = user, realName
|
||||||
s.TryRegister(c)
|
s.tryRegister(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) TryRegister(c *Client) {
|
func (s *Server) tryRegister(c *Client) {
|
||||||
if !c.registered && c.HasNick() && c.HasUser() {
|
if !c.registered && c.HasNick() && c.HasUser() {
|
||||||
c.registered = true
|
c.registered = true
|
||||||
c.send <- RplWelcome(s, c)
|
c.send <- RplWelcome(s, c)
|
||||||
|
Loading…
Reference in New Issue
Block a user