366 lines
9.1 KiB
Go
366 lines
9.1 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
"time"
|
|
"syscall"
|
|
gosocketio "github.com/ambelovsky/gosf-socketio"
|
|
"github.com/ambelovsky/gosf-socketio/transport"
|
|
)
|
|
|
|
var conn *loungeConnection
|
|
|
|
type loungeAuth struct {
|
|
User string `json:"user"`
|
|
Pass string `json:"password"`
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
type loungeChannel struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Id int `json:"id"`
|
|
Topic string `json:"topic"`
|
|
Messages []loungeMessage `json:"messages"`
|
|
Filtered []loungeMessage
|
|
}
|
|
|
|
type loungeNetwork struct {
|
|
Uuid string `json:"uuid"`
|
|
Name string `json:"name"`
|
|
Channels []loungeChannel `json:"channels"`
|
|
}
|
|
|
|
type loungeInit struct {
|
|
Token string `json:"token"`
|
|
Networks []loungeNetwork `json:"networks"`
|
|
}
|
|
|
|
type loungeJoin struct {
|
|
Network string `json:"network"`
|
|
Channel loungeChannel `json:"chan"`
|
|
Index int `json:"index"`
|
|
}
|
|
|
|
type loungePart struct {
|
|
Channel int `json:"chan"`
|
|
}
|
|
|
|
type loungeFrom struct {
|
|
Mode string `json:"mode"`
|
|
Nick string `json:"nick"`
|
|
}
|
|
|
|
type loungeMessageType string
|
|
|
|
const (
|
|
messageMessage loungeMessageType = "message"
|
|
messageAction loungeMessageType = "action"
|
|
messageMode loungeMessageType = "mode"
|
|
messageTopic loungeMessageType = "topic"
|
|
messageNotice loungeMessageType = "notice"
|
|
messageError loungeMessageType = "error"
|
|
)
|
|
|
|
type loungeMessage struct {
|
|
From loungeFrom `json:"from"`
|
|
Highlight bool `json:"highlight"`
|
|
Id int `json:"id"`
|
|
Self bool `json:"self"`
|
|
Text string `json:"text"`
|
|
Time string `json:"time"`
|
|
Type loungeMessageType `json:"type"`
|
|
}
|
|
|
|
type loungeNewMessage struct {
|
|
Channel int `json:"chan"`
|
|
Message loungeMessage `json:"msg"`
|
|
Unread int `json:"unread"`
|
|
}
|
|
|
|
type loungeMoreRequest struct {
|
|
Target int `json:"target"`
|
|
LastId int `json:"lastId"`
|
|
Condensed bool `json:"condensed"`
|
|
}
|
|
|
|
type loungeMore struct {
|
|
Channel int `json:"chan"`
|
|
Messages []loungeMessage `json:"messages"`
|
|
TotalMessages int `json:"totalMessages"`
|
|
}
|
|
|
|
type loungeInput struct {
|
|
Target int `json:"target"`
|
|
Text string `json:"text"`
|
|
}
|
|
|
|
type loungeActionType int
|
|
|
|
const (
|
|
actionNone loungeActionType = 0
|
|
actionConnected loungeActionType = 1
|
|
actionDisconnected loungeActionType = 2
|
|
actionAuthSuccess loungeActionType = 3
|
|
actionAuthFailed loungeActionType = 4
|
|
actionInit loungeActionType = 5
|
|
actionJoin loungeActionType = 6
|
|
actionPart loungeActionType = 7
|
|
actionOpen loungeActionType = 8
|
|
actionMore loungeActionType = 9
|
|
actionNewMessage loungeActionType = 10
|
|
)
|
|
|
|
type loungeConnection struct {
|
|
Error string
|
|
Client *gosocketio.Client
|
|
Host string
|
|
Path string
|
|
User string
|
|
Pass string
|
|
Token string
|
|
Channel *loungeChannel
|
|
Networks []loungeNetwork
|
|
Action loungeActionType
|
|
}
|
|
|
|
func loungeFindChannel(conn *loungeConnection, channel int) *loungeChannel {
|
|
for i := 0; i < len(conn.Networks); i++ {
|
|
net := conn.Networks[i]
|
|
for i := 0; i < len(net.Channels); i++ {
|
|
if net.Channels[i].Id == channel {
|
|
return &net.Channels[i]
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loungeAuthenticate(conn *loungeConnection) {
|
|
var err error
|
|
if conn.Pass != "" {
|
|
err = conn.Client.Emit("auth:perform", loungeAuth{User: conn.User, Pass: conn.Pass})
|
|
} else {
|
|
err = conn.Client.Emit("auth:perform", loungeAuth{User: conn.User, Token: conn.Token})
|
|
}
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func loungeMessageFilter(message loungeMessage) bool {
|
|
if message.Type == messageMessage || message.Type == messageAction || message.Type == messageNotice || message.Type == messageError {
|
|
return true
|
|
}
|
|
if message.Type == messageTopic && message.From.Nick != "" {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func loungeMessageHandle(channel *loungeChannel, message loungeMessage) {
|
|
channel.Messages = append(channel.Messages, message)
|
|
if loungeMessageFilter(message) {
|
|
channel.Filtered = append(channel.Filtered, message)
|
|
}
|
|
if message.Type == messageTopic {
|
|
channel.Topic = message.Text
|
|
}
|
|
}
|
|
|
|
func loungeMessagesFilter(messages []loungeMessage) []loungeMessage {
|
|
filtered := []loungeMessage{}
|
|
for _, msg := range messages {
|
|
if loungeMessageFilter(msg) {
|
|
filtered = append(filtered, msg)
|
|
}
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
func loungeMessagesUpdate(conn *loungeConnection) {
|
|
lastMessage := conn.Channel.Messages[len(conn.Channel.Messages) - 1]
|
|
err := conn.Client.Emit("more", loungeMoreRequest{conn.Channel.Id, lastMessage.Id, true})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func loungeChannelOpen(conn *loungeConnection, channel *loungeChannel) {
|
|
conn.Channel = channel
|
|
err := conn.Client.Emit("open", channel.Id)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if len(channel.Filtered) == 0 {
|
|
channel.Filtered = loungeMessagesFilter(channel.Messages)
|
|
}
|
|
if len(channel.Messages) == 1 {
|
|
loungeMessagesUpdate(conn)
|
|
}
|
|
}
|
|
|
|
func loungeChannelSend(conn *loungeConnection, channel int, text string) {
|
|
err := conn.Client.Emit("input", loungeInput{channel, text})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func loungeRegisterEvents(conn *loungeConnection) {
|
|
var err error;
|
|
err = conn.Client.On("auth:start", func(h *gosocketio.Channel, serverHash int) {
|
|
loungeAuthenticate(conn)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On("auth:success", func(h *gosocketio.Channel) {
|
|
conn.Error = "Authentication succeeded"
|
|
connActionTrigger(conn, actionAuthSuccess)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On("auth:failed", func(h *gosocketio.Channel) {
|
|
conn.Error = "Authentication failed"
|
|
connActionTrigger(conn, actionAuthFailed)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On("init", func(h *gosocketio.Channel, init loungeInit) {
|
|
conn.Token = init.Token
|
|
conn.Networks = init.Networks
|
|
connActionTrigger(conn, actionInit)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On("join", func(h *gosocketio.Channel, join loungeJoin) {
|
|
for i := 0; i < len(conn.Networks); i++ {
|
|
if conn.Networks[i].Uuid == join.Network {
|
|
conn.Networks[i].Channels = append(conn.Networks[i].Channels, join.Channel)
|
|
}
|
|
}
|
|
connActionTrigger(conn, actionJoin)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On("part", func(h *gosocketio.Channel, part loungePart) {
|
|
if conn.Channel != nil && conn.Channel.Id == part.Channel {
|
|
conn.Channel = nil
|
|
}
|
|
for i := 0; i < len(conn.Networks); i++ {
|
|
net := conn.Networks[i]
|
|
for i := 0; i < len(net.Channels); i++ {
|
|
if net.Channels[i].Id == part.Channel {
|
|
channels := net.Channels
|
|
channels[i] = channels[len(channels)-1]
|
|
net.Channels = channels[:len(channels)-1]
|
|
}
|
|
}
|
|
conn.Networks[i] = net
|
|
}
|
|
connActionTrigger(conn, actionPart)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On("open", func(h *gosocketio.Channel) {
|
|
connActionTrigger(conn, actionOpen)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On("more", func(h *gosocketio.Channel, more loungeMore) {
|
|
channel := loungeFindChannel(conn, more.Channel)
|
|
currTime, moreTime := time.Now(), time.Now()
|
|
if channel != nil && len(channel.Messages) != 0 && len(more.Messages) != 0 {
|
|
currTime, _ = time.Parse(time.RFC3339, channel.Messages[0].Time)
|
|
moreTime, _ = time.Parse(time.RFC3339, more.Messages[0].Time)
|
|
}
|
|
if moreTime.Unix() > currTime.Unix() {
|
|
channel.Messages = append(channel.Messages, more.Messages...)
|
|
channel.Filtered = append(channel.Filtered, loungeMessagesFilter(more.Messages)...)
|
|
} else {
|
|
channel.Messages = append(more.Messages, channel.Messages...)
|
|
channel.Filtered = append(loungeMessagesFilter(more.Messages), channel.Filtered...)
|
|
}
|
|
connActionTrigger(conn, actionMore)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On("msg", func(h *gosocketio.Channel, msg loungeNewMessage) {
|
|
channel := loungeFindChannel(conn, msg.Channel)
|
|
if (channel != nil) {
|
|
loungeMessageHandle(channel, msg.Message)
|
|
}
|
|
if conn.Channel != nil && msg.Channel == conn.Channel.Id {
|
|
connActionTrigger(conn, actionNewMessage)
|
|
}
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On(gosocketio.OnConnection, func(h *gosocketio.Channel) {
|
|
conn.Error = "Connected"
|
|
connActionTrigger(conn, actionConnected)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
err = conn.Client.On(gosocketio.OnDisconnection, func(h *gosocketio.Channel) {
|
|
conn.Error = "Disconnected"
|
|
connActionTrigger(conn, actionDisconnected)
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func loungeConnect(conn *loungeConnection) {
|
|
var err error
|
|
c, err := gosocketio.Dial(
|
|
"wss://"+conn.Host+conn.Path+"/socket.io/?EIO=3&transport=websocket",
|
|
transport.GetDefaultWebsocketTransport())
|
|
if err != nil {
|
|
conn.Error = err.Error()
|
|
return
|
|
}
|
|
conn.Client = c
|
|
loungeRegisterEvents(conn)
|
|
}
|
|
|
|
func loungeDisconnect(c *gosocketio.Client) {
|
|
c.Close()
|
|
}
|
|
|
|
func connActionTrigger(conn *loungeConnection, action loungeActionType) {
|
|
// TODO: Subscribe to network events in a sane way.
|
|
conn.Action = action
|
|
syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
|
|
}
|
|
|
|
func connActionHandle(m mainModel) mainModel {
|
|
switch m.conn.Action {
|
|
case actionAuthFailed:
|
|
m.view = viewLogin
|
|
configWrite(m.conf)
|
|
case actionInit:
|
|
if m.conn.Token != "" {
|
|
m.conf.Token = m.conn.Token
|
|
configWrite(m.conf)
|
|
}
|
|
m.view = viewChannels
|
|
case actionPart:
|
|
if m.view == viewChat && m.conn.Channel == nil {
|
|
m.view = viewChannels
|
|
}
|
|
}
|
|
m.conn.Action = actionNone
|
|
return m
|
|
}
|