flewkey
/
linger
Archived
1
0
Fork 0
TUI for The Lounge
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
linger/thelounge.go

365 lines
9.1 KiB

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
}