Skip to content

Commit

Permalink
start heartbeat impl. support new api handshake
Browse files Browse the repository at this point in the history
  • Loading branch information
Ole Andre Birkedal committed May 2, 2021
1 parent 01c817d commit 130a6f7
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 37 deletions.
7 changes: 7 additions & 0 deletions events/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ type oobInclude struct {
Url string
}

// {"84415":{"4440297":1605131885388611}}}
type BidToEid map[string]int
// cid -> bid -> eid
type Seen map[string]BidToEid

type eventData struct {
Type string
Time int64 `json:"eid"`
Expand All @@ -43,6 +48,8 @@ type eventData struct {
Name string `json:"name"`
Archived bool `json:"archived"`
Created int64 `json:"created"`
LastEid int `json:"last_seen_eid"`
SeenEids Seen `json:"seenEids"`
Data []byte
}

Expand Down
11 changes: 8 additions & 3 deletions events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import (
type eventHandler struct {
Queue chan eventData
SessionToken string
APIUrl string
Window *ui.View
}

func NewHandler(token string, w *ui.View) *eventHandler {
func NewHandler(APIUrl, token string, w *ui.View) *eventHandler {
handler := &eventHandler{
Queue: make(chan eventData, 8),
SessionToken: token,
APIUrl: APIUrl,
Window: w,
}

Expand All @@ -44,13 +46,13 @@ func (e *eventHandler) Enqueue(msg []byte) {
}

func (e *eventHandler) handleBacklog(url string) {
backlogResponse := requests.GetBacklog(e.SessionToken, url)
backlogResponse := requests.GetBacklog(e.APIUrl, e.SessionToken, url)
backlogData := parseBacklog(backlogResponse)

// First we initialize all channels
for _, event := range backlogData {
if event.Type == "channel_init" {
userStr := []string{}
var userStr []string
for _, userString := range event.Members {
userStr = append(userStr, userString.Nick)
}
Expand All @@ -76,6 +78,9 @@ func (e *eventHandler) handle(curr eventData, backlogEvent bool) {
case "oob_include":
e.backlog(curr)

case "heartbeat_echo":
e.Heartbeat(curr.SeenEids)

case "channel_init":
e.channels(curr, backlogEvent)

Expand Down
19 changes: 17 additions & 2 deletions events/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,24 @@ func (e *eventHandler) backlog(event eventData) {
}
}

func (e *eventHandler) Heartbeat(seen Seen) {
//for cid, bidToEid := range seen {
//fmt.Printf("%s -> ", cid)
//for bid, eid := range bidToEid {
//fmt.Printf("%s -> %d\n", bid, eid)
//}
//}
}

func (e *eventHandler) channels(event eventData, backlogEvent bool) {
if !backlogEvent {
userStrings := []string{}
for _, userString := range event.Members {
userStrings = append(userStrings, userString.Nick)
}
topic := getTopicName(event.Topic)
e.Window.AddChannel(event.Chan, topic, event.Cid, event.Bid, userStrings)
newChan := e.Window.AddChannel(event.Chan, topic, event.Cid, event.Bid, userStrings)
newChan.SetEid(event.LastEid)
}
}

Expand All @@ -33,7 +43,7 @@ func (e *eventHandler) parted(event eventData, backlogEvent bool) {

func (e *eventHandler) msg(event eventData) {
if e.Window.HasChannel(event.Chan) {
e.Window.Activity.RegisterActivity(event.Chan, event.Msg, e.Window)
e.Window.Activity.RegisterActivity(event.Chan, event.Msg, event.LastEid, e.Window)
e.Window.AddBufferMsg(event.Chan, event.From, event.Msg, event.Time, event.Bid)
}
}
Expand Down Expand Up @@ -65,6 +75,11 @@ func (e *eventHandler) conversation(event eventData) {
header := fmt.Sprintf("Chatting since: %s", unixtimeToDate(event.Created))
e.Window.AddChannel(event.Name, header, event.Cid, event.Bid, []string{})
}

channel := e.Window.GetChannel(event.Name)
if channel != nil {
channel.SetEid(event.LastEid)
}
}

func (e *eventHandler) meMsg(event eventData) {
Expand Down
7 changes: 4 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ func main() {
tview.Styles.PrimitiveBackgroundColor = tcell.ColorDefault

conf := config.Parse()
session, err := requests.GetSessionToken(conf.Username, conf.Password)
sessionData, err := requests.GetSessionToken(conf.Username, conf.Password)

if err != nil {
log.Print(err)
return
}

wsConn := requests.NewConnection(session)
wsConn := requests.NewConnection(sessionData)
view := ui.NewView(wsConn, conf.Triggers, conf.LastChan)

defer func() {
Expand All @@ -32,7 +32,8 @@ func main() {
view.Stop()
}()

eventHandler := events.NewHandler(session, view)
eventHandler := events.NewHandler(sessionData.APIHost,
sessionData.Session, view)

go func() {
for {
Expand Down
28 changes: 16 additions & 12 deletions requests/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ const formtokenUrl = "https://www.irccloud.com/chat/auth-formtoken"
const sessionUrl = "https://www.irccloud.com/chat/login"

type sessionReply struct {
Success bool
Session string
Uid uint32
Success bool `json:"success"`
Session string `json:"session"`
Uid uint32 `json:"uid"`
APIHost string `json:"api_host"`
WSHost string `json:"websocket_host"`
WSPath string `json:"websocket_path"`
URL string `json:"url"`
}

type formtokenReply struct {
Expand All @@ -25,8 +29,8 @@ type formtokenReply struct {
Token string
}

func GetBacklog(token, endpoint string) *http.Response {
path := fmt.Sprintf("https://api.irccloud.com%s", endpoint)
func GetBacklog(apiUrl, token, endpoint string) *http.Response {
path := fmt.Sprintf("%s%s", apiUrl, endpoint)
client := http.Client{}

req, _ := http.NewRequest("GET", path, nil)
Expand All @@ -43,15 +47,15 @@ func GetBacklog(token, endpoint string) *http.Response {
return resp
}

func GetSessionToken(user, pass string) (string, error) {
func GetSessionToken(user, pass string) (sessionReply, error) {
httpClient := &http.Client{
Timeout: 10 * time.Second,
}

formToken, err := getFormtoken(httpClient)

if err != nil {
return "", fmt.Errorf("error getting session token: %v", err)
return sessionReply{}, fmt.Errorf("error getting session token: %v", err)
}

form := url.Values{}
Expand All @@ -67,27 +71,27 @@ func GetSessionToken(user, pass string) (string, error) {

if err != nil {
log.Print(err)
return "", err
return sessionReply{}, err
}

defer resp.Body.Close()
return parseSession(resp)
}

func parseSession(response *http.Response) (string, error) {
func parseSession(response *http.Response) (sessionReply, error) {
decoder := json.NewDecoder(response.Body)
rep := &sessionReply{}
err := decoder.Decode(&rep)

if err != nil {
return "", fmt.Errorf("error parsing auth reply: %w", err)
return sessionReply{}, fmt.Errorf("error parsing auth reply: %w", err)
}

if !rep.Success {
return "", fmt.Errorf("invalid login: %w", err)
return sessionReply{}, fmt.Errorf("invalid login: %w", err)
}

return rep.Session, nil
return *rep, nil
}

func getFormtoken(client *http.Client) (string, error) {
Expand Down
31 changes: 26 additions & 5 deletions requests/ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ type sayMessage struct {
Msg string `json:"msg"`
}

func NewConnection(token string) *Connection {
address := url.URL{Scheme: "wss", Host: "api.irccloud.com", Path: "/"}
type heartbeatMessage struct {
Method string `json:"_method"`
SelectedBuffer int `json:"selectedBuffer"`
SeenEids string `json:"seenEids"`
}

func NewConnection(data sessionReply) *Connection {
address := url.URL{Scheme: "wss", Host: data.WSHost, Path: data.WSPath}

headers := http.Header{}
headers.Add("User-Agent", "irccloud-cli")
headers.Add("Origin", "https://api.irccloud.com")
headers.Add("Cookie", fmt.Sprintf("session=%s", token))
headers.Add("Cookie", fmt.Sprintf("session=%s", data.Session))

conn, _, err := websocket.DefaultDialer.Dial(address.String(), headers)

Expand All @@ -39,6 +45,17 @@ func NewConnection(token string) *Connection {
}
}

func (c *Connection) SendHeartbeat(selected, cid, bid, eid int) {
msg := &heartbeatMessage{
Method: "heartbeat",
SelectedBuffer: selected,
SeenEids: makeSeenEids(cid, bid, eid),
}

data, _ := json.Marshal(msg)
_ = c.writeMessage(data)
}

func (c *Connection) SendMessage(cid int, channel, message string) {
msg := &sayMessage{
Method: "say",
Expand All @@ -48,8 +65,7 @@ func (c *Connection) SendMessage(cid int, channel, message string) {
}

data, _ := json.Marshal(msg)

_ = c.writeMessage([]byte(data))
_ = c.writeMessage(data)
}

func (c *Connection) writeMessage(message []byte) error {
Expand All @@ -61,3 +77,8 @@ func (c *Connection) ReadMessage() ([]byte, error) {

return msg, err
}

// "{\\"3\":{\\"4\\":1343825583263721}}"
func makeSeenEids(cid, bid, eid int) string {
return fmt.Sprintf(`{"%d":{"%d":%d}}`, cid, bid, eid)
}
7 changes: 6 additions & 1 deletion ui/activity_bar.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,12 @@ func (b *activityBar) GetLatestActiveChannel() (string, error) {
return b.sorted[0].displayName, nil
}

func (b *activityBar) RegisterActivity(buffer, msg string, view *View) {
func (b *activityBar) RegisterActivity(buffer, msg string, eid int, view *View) {
channel := view.GetChannel(buffer)
if channel != nil {
channel.SetEid(eid)
}

// Do not register activity if it's in our current active channel
if view.GetCurrentChannel() == buffer {
return
Expand Down
34 changes: 23 additions & 11 deletions ui/channels.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ import (
)

type channel struct {
layout *tview.Grid
name string
chat *tview.TextView
users *tview.List
input *tview.InputField
info *tview.TextView
cid int
bid int
msgs chan string
buffer []byte
layout *tview.Grid
name string
chat *tview.TextView
users *tview.List
input *tview.InputField
info *tview.TextView
cid int
bid int
msgs chan string
lastEid int
buffer []byte
}

type channelList []channel
Expand All @@ -44,6 +45,10 @@ func (c *channel) Scroll(amount int) {
c.chat.ScrollTo(row+amount, column)
}

func (c *channel) SetEid(eid int) {
c.lastEid = eid
}

func (v *View) getChannelByName(name string) (int, *channel) {
v.channelLock.Lock()
defer v.channelLock.Unlock()
Expand Down Expand Up @@ -88,7 +93,7 @@ func (v *View) ChangeTopic(channel, author, newTopic string, time int64, bid int
}
}

func (v *View) AddChannel(name, topic string, cid, bid int, userList []string) {
func (v *View) AddChannel(name, topic string, cid, bid int, userList []string) *channel {
newChan := channel{
layout: tview.NewGrid().
SetRows(1, 0, 1).
Expand Down Expand Up @@ -177,13 +182,20 @@ func (v *View) AddChannel(name, topic string, cid, bid int, userList []string) {

v.app.SetFocus(newChan.input)
})

return &newChan
}

func remove(s []channel, i int) []channel {
s[i] = s[len(s)-1]
return s[:len(s)-1]
}

func (v *View) GetChannel(name string) *channel {
_, channel := v.getChannelByName(name)
return channel
}

func (v *View) RemoveChannel(name string) {
v.app.QueueUpdateDraw(func() {
v.pages.RemovePage(name)
Expand Down

0 comments on commit 130a6f7

Please sign in to comment.