From 053fd7fb3435526cbbbe68a1bf03643a6327d206 Mon Sep 17 00:00:00 2001
From: Sergey Matveev <stargrave@stargrave.org>
Date: Sun, 12 Mar 2023 17:42:35 +0300
Subject: [PATCH] User statuses

---
 cmd/mmc/main.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++---
 cmd/start       | 10 +++++---
 doc/arch.texi   |  2 ++
 doc/usage.texi  |  2 +-
 4 files changed, 71 insertions(+), 9 deletions(-)

diff --git a/cmd/mmc/main.go b/cmd/mmc/main.go
index b288a8d..12eb7df 100644
--- a/cmd/mmc/main.go
+++ b/cmd/mmc/main.go
@@ -30,6 +30,7 @@ import (
 	"os/exec"
 	"os/signal"
 	"path"
+	"sort"
 	"strings"
 	"syscall"
 	"time"
@@ -43,7 +44,7 @@ const CmdFile = "/FILE "
 
 var (
 	Newwin    = flag.String("newwin", "cmd/newwin", "Path to newwin command")
-	DebugFifo = flag.String("debug", "debug", "Path to debug FIFO to be created")
+	DebugFifo = flag.String("debug", "", "Path to debug FIFO to be created")
 	DebugFd   *os.File
 	UmaskCur  int
 )
@@ -61,6 +62,7 @@ func main() {
 	entrypoint := flag.String("entrypoint", mmc.GetEntrypoint(), "Entrypoint")
 	notifyCmd := flag.String("notify", "cmd/notify", "Path to notification handler")
 	heartbeatCh := flag.String("heartbeat-ch", "town-square", "Channel for heartbeating")
+	userStatusFifo := flag.String("user-status", "", "Path to FIFO for user statuses")
 	flag.Parse()
 	log.SetFlags(log.Lshortfile)
 	log.SetOutput(os.Stdout)
@@ -116,8 +118,11 @@ func main() {
 	if err != nil {
 		log.Fatalln(err)
 	}
+
 	UsersDC := make(map[string]*model.Channel, len(Users))
+	userIds := make([]string, 0, len(Users))
 	for _, u := range Users {
+		userIds = append(userIds, u.Id)
 		pth := path.Join("users", strings.ReplaceAll(u.Username, ".", "_"))
 		os.MkdirAll(pth, 0777)
 		rewriteIfChanged(
@@ -224,6 +229,52 @@ func main() {
 		}(u)
 	}
 
+	UserStatus := make(map[string]string)
+	if *userStatusFifo != "" {
+		statuses, resp, err := c.GetUsersStatusesByIds(userIds)
+		if err != nil {
+			if DebugFd != nil {
+				spew.Fdump(DebugFd, resp)
+			}
+			log.Fatalln(err)
+		}
+		if DebugFd != nil {
+			spew.Fdump(DebugFd, teams)
+		}
+		userIds = nil
+		for _, s := range statuses {
+			UserStatus[Users[s.UserId].Username] = s.Status
+		}
+		statuses = nil
+		go func() {
+			for {
+				time.Sleep(mmc.SleepTime)
+				fd, err := os.OpenFile(
+					*userStatusFifo, os.O_WRONLY|os.O_APPEND, os.FileMode(0666),
+				)
+				if err != nil {
+					log.Println("OpenFile:", *userStatusFifo, err)
+					continue
+				}
+				var onlines []string
+				var aways []string
+				for name, status := range UserStatus {
+					switch status {
+					case "online":
+						onlines = append(onlines, name)
+					case "away":
+						aways = append(aways, name)
+					}
+				}
+				sort.Strings(onlines)
+				sort.Strings(aways)
+				fmt.Fprintln(fd, "O:", strings.Join(onlines, " "))
+				fmt.Fprintln(fd, "A:", strings.Join(aways, " "))
+				fd.Close()
+			}
+		}()
+	}
+
 	Chans := make(map[string]*model.Channel)
 	time.Sleep(mmc.SleepTime)
 	page, resp, err := c.GetChannelsForTeamForUser(Team.Id, me.Id, false, "")
@@ -501,11 +552,18 @@ func main() {
 						log.Fatalln(err)
 					}
 				case model.WebsocketEventStatusChange:
-					log.Println("status change:", user.Username, "->", data["status"].(string))
+					status := data["status"].(string)
+					switch status {
+					case "online":
+					case "offline":
+					case "away":
+					default:
+						log.Println(user.Username, "unknown status:", status)
+					}
+					UserStatus[user.Username] = status
 					if *notifyCmd != "" {
 						exec.Command(*notifyCmd, fmt.Sprintf(
-							"status: %s -> %s",
-							user.Username, data["status"].(string),
+							"status: %s -> %s", user.Username, status,
 						)).Run()
 					}
 				case model.WebsocketEventHello:
diff --git a/cmd/start b/cmd/start
index ade0962..173aef1 100755
--- a/cmd/start
+++ b/cmd/start
@@ -1,12 +1,14 @@
-#!/bin/sh
+#!/bin/sh -x
 
 cmd="$(dirname "$(realpath -- "$0")")"
 unset TMUX
 TMUX="tmux -S tmux.sock"
 $TMUX has-session -t mmc 2>/dev/null && exit
-rm -f debug
-mkfifo debug
+mkdir -p users
+rm -f debug users/status
+mkfifo debug users/status
 [ -s tmux.conf ] || sed "s#NEWWIN#$cmd/newwin#" < "$cmd"/tmux.conf > tmux.conf
 $TMUX -f tmux.conf new-session -d -n ROOT -s mmc "cat debug | tai64n | tai64nlocal"
-$TMUX split-window -h "$cmd/mmc/mmc -newwin $cmd/newwin -notify $cmd/notify | tai64n | tai64nlocal"
+$TMUX split-window -h 'while : ; do cat users/status | spc -e grn,"^O:.*" -e cya,"^A:.*" ; sleep 5 ; clear ; done'
+$TMUX split-window -v "$cmd/mmc/mmc -debug debug -user-status users/status -newwin $cmd/newwin -notify $cmd/notify | tai64n | tai64nlocal"
 $TMUX attach-session
diff --git a/doc/arch.texi b/doc/arch.texi
index ba2fe4f..6ae56be 100644
--- a/doc/arch.texi
+++ b/doc/arch.texi
@@ -10,8 +10,10 @@ cmd/start
   F tmux.conf
   R tmux -f tmux.conf
     R tail -f debug | tai64n
+    R for { cat users/status ; sleep 5 }
     R fzf **(/) | cmd/newwin
     R cmd/mmc | tai64n
+     F users/status
      F users/.../{id,email,name,|in,out.rec,|status,last}
      F chans/.../{id,info,out.rec,|users,last}
      F file/{|get,|out}
diff --git a/doc/usage.texi b/doc/usage.texi
index b0f4a80..db261d6 100644
--- a/doc/usage.texi
+++ b/doc/usage.texi
@@ -21,7 +21,7 @@ default). For example
 @item Set @env{$MMC_ENTRYPOINT} environment variable to your entrypoint.
 
 @item Run @command{cmd/start} and you should see started @command{tmux}
-with split window and running @command{cmd/mmc}.
+with split window, running @command{cmd/mmc} and list of user statuses.
 
 @item By pressing @code{Prefix+c}, list of available users/channels is
 shown inside @command{fzf}. Choose the desired one.
-- 
2.51.0