Alex Auvolat
38a3f1bdb1
Mattermost assigns its own IDs to messages, thus when sending a message to Mattermost the event_seen key that has to be written must take into account that ID and not the one that we put in the event (which was the Matrix event ID) Note that for XMPP anything can be used as an ID, so using the Matrix event ID there worked, but it's actually not so good.
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_PROTOCOL
|
|
}
|
|
|
|
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) (string, 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,
|
|
})
|
|
}
|