From ec67a610e3062d3e60891332f33ace4ad5a448bd Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Sun, 16 Feb 2020 16:26:55 +0100 Subject: First commit with working stub of IRC bridge --- connector/irc/irc.go | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 connector/irc/irc.go (limited to 'connector/irc') diff --git a/connector/irc/irc.go b/connector/irc/irc.go new file mode 100644 index 0000000..02f82eb --- /dev/null +++ b/connector/irc/irc.go @@ -0,0 +1,255 @@ +package irc + +import ( + "time" + "os" + "strings" + "fmt" + + . "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector" + + "github.com/lrstanley/girc" +) + +// User id format: nickname@server +// Room id format: #room_name@server + +type IRC struct { + handler Handler + config Configuration + + connected bool + timeout int + + nick string + name string + server string + conn *girc.Client +} + +func (irc *IRC) SetHandler(h Handler) { + irc.handler = h +} + +func(irc *IRC) Protocol() string { + return "irc" +} + +func (irc *IRC) Configure(c Configuration) error { + irc.config = c + + irc.nick = c.GetString("nick") + irc.server = c.GetString("server") + + port, err := c.GetInt("port") + if err != nil { + return err + } + if port == 0 { + port = 6667 + } + + client := girc.New(girc.Config{ + Server: irc.server, + Port: port, + Nick: irc.nick, + User: irc.nick, + Debug: os.Stderr, + SSL: true, + }) + + client.Handlers.Add(girc.CONNECTED, irc.ircConnected) + //client.Handlers.Add(girc.DISCONNECTED, irc.ircDisconnected) + //client.Handlers.Add(girc.NICK, irc.ircNick) + client.Handlers.Add(girc.PRIVMSG, irc.ircPrivmsg) + client.Handlers.Add(girc.JOIN, irc.ircJoin) + client.Handlers.Add(girc.PART, irc.ircPart) + client.Handlers.Add(girc.RPL_NAMREPLY, irc.ircNamreply) + client.Handlers.Add(girc.RPL_TOPIC, irc.ircTopic) + + irc.conn = client + go irc.connectLoop(client) + + for i := 0; i < 42; i++ { + time.Sleep(time.Duration(1)*time.Second) + if irc.conn != client { + break + } + if irc.connected { + return nil + } + } + return fmt.Errorf("Failed to conncect after 42s attempting") +} + +func (irc *IRC) User() UserID { + return UserID(irc.nick + "@" + irc.server) +} + +func (irc *IRC) checkRoomId(id RoomID) (string, error) { + x := strings.Split(string(id), "@") + if len(x) != 2 || x[1] != irc.server || x[0][0] != '#' { + return "", fmt.Errorf("Invalid room ID: %s", id) + } + return x[0], nil +} + +func (irc *IRC) checkUserId(id UserID) (string, error) { + x := strings.Split(string(id), "@") + if len(x) != 2 || x[1] != irc.server || x[0][0] == '#' { + return "", fmt.Errorf("Invalid user ID: %s", id) + } + return x[0], nil +} + +func (irc *IRC) SetUserInfo(info *UserInfo) error { + return fmt.Errorf("Not implemented") +} + +func (irc *IRC) SetRoomInfo(roomId RoomID, info *RoomInfo) error { + ch, err := irc.checkRoomId(roomId) + if err != nil { + return err + } + + if info.Name != "" && info.Name != ch { + return fmt.Errorf("May not change IRC room name to other than %s", ch) + } + if info.Picture != nil { + return fmt.Errorf("Room picture not supported on IRC") + } + irc.conn.Cmd.Topic(ch, info.Description) + return nil +} + +func (irc *IRC) Join(roomId RoomID) error { + ch, err := irc.checkRoomId(roomId) + if err != nil { + return err + } + + irc.conn.Cmd.Join(ch) + return nil +} + +func (irc *IRC) Leave(roomId RoomID) { + ch, err := irc.checkRoomId(roomId) + if err != nil { + return + } + + irc.conn.Cmd.Part(ch) +} + +func (irc *IRC) Send(event *Event) error { + dest := "" + if event.Room != "" { + ch, err := irc.checkRoomId(event.Room) + if err != nil { + return err + } + dest = ch + } else if event.Recipient != "" { + ui, err := irc.checkUserId(event.Recipient) + if err != nil { + return err + } + dest = ui + } else { + return fmt.Errorf("Invalid target") + } + + if event.Attachements != nil && len(event.Attachements) > 0 { + // TODO find a way to send them using some hosing of some kind + return fmt.Errorf("Attachements not supported on IRC") + } + + if event.Type == EVENT_MESSAGE { + irc.conn.Cmd.Message(dest, event.Message) + } else if event.Type == EVENT_ACTION { + irc.conn.Cmd.Action(dest, event.Message) + } else { + return fmt.Errorf("Invalid event type") + } + return nil +} + +func (irc *IRC) Close() { + irc.conn.Close() + irc.conn = nil +} + +func (irc *IRC) connectLoop(c *girc.Client) { + irc.timeout = 10 + for { + if irc.conn != c { + return + } + if err := c.Connect(); err != nil { + irc.connected = false + fmt.Printf("IRC failed to connect / disconnected: %s", err) + fmt.Printf("Retrying in %ds", irc.timeout) + time.Sleep(time.Duration(irc.timeout) * time.Second) + irc.timeout *= 2 + } else { + return + } + } +} + +func (irc *IRC) ircConnected(c *girc.Client, e girc.Event) { + fmt.Printf("ircConnected ^^^^\n") + irc.timeout = 10 + irc.connected = true +} + +func (irc *IRC) ircPrivmsg(c *girc.Client, e girc.Event) { + ev := &Event{ + Type: EVENT_MESSAGE, + Author: UserID(e.Source.Name + "@" + irc.server), + Message: e.Last(), + } + if e.IsFromChannel() { + ev.Room = RoomID(e.Params[0] + "@" + irc.server) + } + if e.IsAction() { + ev.Type = EVENT_ACTION + } + irc.handler.Event(ev) +} + +func (irc *IRC) ircJoin(c *girc.Client, e girc.Event) { + room := RoomID(e.Params[0] + "@" + irc.server) + if e.Source.Name == irc.nick { + irc.handler.Joined(room) + } else { + ev := &Event{ + Type: EVENT_JOIN, + Author: UserID(e.Source.Name + "@" + irc.server), + Room: room, + } + irc.handler.Event(ev) + } +} + +func (irc *IRC) ircPart(c *girc.Client, e girc.Event) { + room := RoomID(e.Params[0] + "@" + irc.server) + if e.Source.Name == irc.nick { + irc.handler.Left(room) + } else { + ev := &Event{ + Type: EVENT_LEAVE, + Author: UserID(e.Source.Name + "@" + irc.server), + Room: room, + } + irc.handler.Event(ev) + } +} + +func (irc *IRC) ircNamreply(c *girc.Client, e girc.Event) { + fmt.Printf("TODO namreply params: %#v", e.Params) +} + +func (irc *IRC) ircTopic(c *girc.Client, e girc.Event) { + fmt.Printf("TODO topic params: %#v", e.Params) +} -- cgit v1.2.3