371 lines
7.5 KiB
Go
371 lines
7.5 KiB
Go
package irc
|
|
|
|
import (
|
|
"fmt"
|
|
_ "os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/lrstanley/girc"
|
|
|
|
. "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
|
|
)
|
|
|
|
// User id format: nickname@server
|
|
|
|
// Room id format: #room_name@server
|
|
|
|
type IRC struct {
|
|
handler Handler
|
|
|
|
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 {
|
|
if irc.conn != nil {
|
|
irc.Close()
|
|
}
|
|
|
|
var err error
|
|
|
|
irc.nick, err = c.GetString("nick")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
irc.server, err = c.GetString("server")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
port, err := c.GetInt("port", 6667)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ssl, err := c.GetBool("ssl", false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
client := girc.New(girc.Config{
|
|
Server: irc.server,
|
|
Port: port,
|
|
Nick: irc.nick,
|
|
User: irc.nick,
|
|
//Out: os.Stderr,
|
|
SSL: ssl,
|
|
})
|
|
|
|
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.TOPIC, irc.ircTopic)
|
|
client.Handlers.Add(girc.RPL_TOPIC, irc.ircRplTopic)
|
|
|
|
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 connect 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) == 1 {
|
|
return "", fmt.Errorf("Please write whole room ID with server: %s@%s", id, irc.server)
|
|
}
|
|
if x[0][0] != '#' || len(x) != 2 || x[1] != irc.server {
|
|
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) == 1 {
|
|
return "", fmt.Errorf("Please write whole user ID with server: %s@%s", id, irc.server)
|
|
}
|
|
if x[0][0] == '#' || len(x) != 2 || x[1] != irc.server {
|
|
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 {
|
|
if irc.conn == nil {
|
|
return fmt.Errorf("Not connected")
|
|
}
|
|
|
|
ch, err := irc.checkRoomId(roomId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if info.Topic != "" {
|
|
irc.conn.Cmd.Topic(ch, info.Topic)
|
|
}
|
|
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")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (irc *IRC) Join(roomId RoomID) error {
|
|
if irc.conn == nil {
|
|
return fmt.Errorf("Not connected")
|
|
}
|
|
|
|
ch, err := irc.checkRoomId(roomId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
irc.conn.Cmd.Join(ch)
|
|
return nil
|
|
}
|
|
|
|
func (irc *IRC) Invite(userId UserID, roomId RoomID) error {
|
|
if irc.conn == nil {
|
|
return fmt.Errorf("Not connected")
|
|
}
|
|
|
|
who, err := irc.checkUserId(userId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if roomId == "" {
|
|
return nil
|
|
}
|
|
|
|
ch, err := irc.checkRoomId(roomId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
irc.conn.Cmd.Invite(ch, who)
|
|
return nil
|
|
}
|
|
|
|
func (irc *IRC) Leave(roomId RoomID) {
|
|
if irc.conn == nil {
|
|
return
|
|
}
|
|
|
|
ch, err := irc.checkRoomId(roomId)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
irc.conn.Cmd.Part(ch)
|
|
}
|
|
|
|
func (irc *IRC) Send(event *Event) error {
|
|
if irc.conn == nil {
|
|
return fmt.Errorf("Not connected")
|
|
}
|
|
|
|
// Workaround girc bug
|
|
if event.Text[0] == ':' {
|
|
event.Text = " " + event.Text
|
|
}
|
|
|
|
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.Attachments != nil && len(event.Attachments) > 0 {
|
|
for _, at := range event.Attachments {
|
|
url := at.URL()
|
|
if url == "" {
|
|
// TODO find a way to send them using some hosing of some kind
|
|
return fmt.Errorf("Attachment without URL sent to IRC")
|
|
} else {
|
|
irc.conn.Cmd.Message(dest, fmt.Sprintf("%s (%s, %dkb)",
|
|
url, at.Mimetype(), at.Size()/1024))
|
|
}
|
|
}
|
|
}
|
|
|
|
if event.Type == EVENT_MESSAGE {
|
|
irc.conn.Cmd.Message(dest, event.Text)
|
|
} else if event.Type == EVENT_ACTION {
|
|
irc.conn.Cmd.Action(dest, event.Text)
|
|
} else {
|
|
return fmt.Errorf("Invalid event type")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (irc *IRC) Close() {
|
|
conn := irc.conn
|
|
irc.conn = nil
|
|
irc.connected = false
|
|
if conn != nil {
|
|
conn.Close()
|
|
}
|
|
}
|
|
|
|
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\n", err)
|
|
fmt.Printf("Retrying in %ds\n", irc.timeout)
|
|
time.Sleep(time.Duration(irc.timeout) * time.Second)
|
|
irc.timeout *= 2
|
|
if irc.timeout > 600 {
|
|
irc.timeout = 600
|
|
}
|
|
} 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),
|
|
Text: 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 {
|
|
user := UserID(e.Source.Name + "@" + irc.server)
|
|
ev := &Event{
|
|
Type: EVENT_JOIN,
|
|
Author: user,
|
|
Room: room,
|
|
}
|
|
irc.handler.Event(ev)
|
|
irc.handler.UserInfoUpdated(user, &UserInfo{
|
|
DisplayName: e.Source.Name,
|
|
})
|
|
}
|
|
}
|
|
|
|
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 {
|
|
user := UserID(e.Source.Name + "@" + irc.server)
|
|
ev := &Event{
|
|
Type: EVENT_LEAVE,
|
|
Author: user,
|
|
Room: room,
|
|
}
|
|
irc.handler.Event(ev)
|
|
irc.handler.UserInfoUpdated(user, &UserInfo{
|
|
DisplayName: e.Source.Name,
|
|
})
|
|
}
|
|
}
|
|
|
|
func (irc *IRC) ircNamreply(c *girc.Client, e girc.Event) {
|
|
room := RoomID(e.Params[2] + "@" + irc.server)
|
|
names := strings.Split(e.Last(), " ")
|
|
for _, name := range names {
|
|
if name[0] == '@' {
|
|
name = name[1:]
|
|
}
|
|
src := girc.ParseSource(name)
|
|
if src.Name != irc.nick {
|
|
irc.handler.Event(&Event{
|
|
Type: EVENT_JOIN,
|
|
Author: UserID(src.Name + "@" + irc.server),
|
|
Room: room,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func (irc *IRC) ircTopic(c *girc.Client, e girc.Event) {
|
|
source := UserID(e.Source.Name + "@" + irc.server)
|
|
room := RoomID(e.Params[0] + "@" + irc.server)
|
|
topic := e.Last()
|
|
irc.handler.RoomInfoUpdated(room, source, &RoomInfo{
|
|
Topic: topic,
|
|
})
|
|
}
|
|
|
|
func (irc *IRC) ircRplTopic(c *girc.Client, e girc.Event) {
|
|
room := RoomID(e.Params[1] + "@" + irc.server)
|
|
topic := e.Last()
|
|
irc.handler.RoomInfoUpdated(room, "", &RoomInfo{
|
|
Topic: topic,
|
|
})
|
|
}
|