easybridge/connector/irc/irc.go

256 lines
5.2 KiB
Go

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)
}