aboutsummaryrefslogtreecommitdiff
path: root/connector/irc
diff options
context:
space:
mode:
Diffstat (limited to 'connector/irc')
-rw-r--r--connector/irc/irc.go255
1 files changed, 255 insertions, 0 deletions
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)
+}