Noah Petherbridge
fd82a463f3
* Deadlock detection: the chatbot handlers will spin off a background goroutine to ping DMs at itself and test for responsiveness. If the echoes don't return for a minute, issue a /api/shutdown command to the HTTP server to force a reboot. * New admin API endpoint: /api/shutdown, equivalent to the operator '/shutdown' command sent in chat. Requires your AdminAPIKey to call it. Used by the chatbot as part of deadlock detection. * Adjust some uses of mutexes to hopefully mitigate deadlocks a bit. * Do Not Disturb: if users opt to "Ignore unsolicited DMs" they will set a DND status on the server which will grey-out their DM icon for other chatters. * Bring back an option for ChatServer to notify you when somebody begins watching your camera (on by default). * Automatically focus the message entry box when changing channels. * Lower webcam resolution hints to 480p to test performance implications.
96 lines
2.3 KiB
Go
96 lines
2.3 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.kirsle.net/apps/barertc/client/config"
|
|
"git.kirsle.net/apps/barertc/pkg/log"
|
|
"git.kirsle.net/apps/barertc/pkg/messages"
|
|
)
|
|
|
|
const deadlockTTL = time.Minute
|
|
|
|
/*
|
|
Deadlock detection for the chat server.
|
|
|
|
Part of the chatbot handlers. The bot will send DMs to itself on an interval
|
|
and test whether the server is responsive; if it goes down, it will issue the
|
|
/api/shutdown command to reboot the server automatically.
|
|
|
|
This function is a goroutine spawned in the background.
|
|
*/
|
|
func (h *BotHandlers) watchForDeadlock() {
|
|
log.Info("Deadlock monitor engaged!")
|
|
h.deadlockLastOK = time.Now()
|
|
|
|
for {
|
|
time.Sleep(15 * time.Second)
|
|
h.client.Send(messages.Message{
|
|
Action: messages.ActionMessage,
|
|
Channel: "@" + h.client.Username(),
|
|
Message: "deadlock ping",
|
|
})
|
|
|
|
// Has it been a while since our last ping?
|
|
if time.Since(h.deadlockLastOK) > deadlockTTL {
|
|
log.Error("Deadlock detected! Rebooting the chat server!")
|
|
h.deadlockLastOK = time.Now()
|
|
h.rebootChatServer()
|
|
}
|
|
}
|
|
}
|
|
|
|
// onMessageFromSelf handles DMs sent to ourself, e.g. for deadlock detection.
|
|
func (h *BotHandlers) onMessageFromSelf(msg messages.Message) {
|
|
// If it is our own DM channel thread, it's for deadlock detection.
|
|
if msg.Channel == "@"+h.client.Username() {
|
|
h.deadlockLastOK = time.Now()
|
|
}
|
|
}
|
|
|
|
// Reboot the chat server via web API, in case of deadlock.
|
|
func (h *BotHandlers) rebootChatServer() error {
|
|
// API request struct for BareRTC /api/shutdown endpoint.
|
|
var request = struct {
|
|
APIKey string
|
|
}{
|
|
APIKey: config.Current.BareRTC.AdminAPIKey,
|
|
}
|
|
|
|
// JSON request body.
|
|
jsonStr, err := json.Marshal(request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Make the API request to BareRTC.
|
|
var url = strings.TrimSuffix(config.Current.BareRTC.URL, "/") + "/api/shutdown"
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, _ := io.ReadAll(resp.Body)
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("RebootChatServer: error posting to BareRTC: status %d body %s", resp.StatusCode, body)
|
|
}
|
|
|
|
return nil
|
|
}
|