Add !votes command, fix vote tally timing, and improve Kosmi stability
- Add !votes command (IRC + Kosmi) showing per-session and all-time vote
breakdowns for the current game via new Jackbox API endpoints
(GET sessions/{id}/games, sessions/{id}/votes, games/{id})
- Fix vote tally broadcasting: remove debounce timer, announce tallies
only at game transitions or session end instead of after every vote
- Add !kreconnect IRC command to manually trigger Kosmi reconnection
- Add WebSocket ping/pong keepalive and write mutex to Kosmi client
for connection stability
- Add watchConnection() auto-reconnect on unexpected Kosmi disconnects
- Remove old 2025-10-31 chat summaries; add votes command design doc
Made-with: Cursor
This commit is contained in:
@@ -22,25 +22,25 @@ type KosmiClient interface {
|
||||
SendMessage(text string) error
|
||||
OnMessage(callback func(*NewMessagePayload))
|
||||
IsConnected() bool
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
|
||||
// Bkosmi represents the Kosmi bridge
|
||||
type Bkosmi struct {
|
||||
*bridge.Config
|
||||
client KosmiClient
|
||||
roomID string
|
||||
roomURL string
|
||||
connected bool
|
||||
authDone bool // Signals that authentication is complete (like IRC bridge)
|
||||
msgChannel chan config.Message
|
||||
jackboxClient *jackbox.Client
|
||||
client KosmiClient
|
||||
roomID string
|
||||
roomURL string
|
||||
connected bool
|
||||
intentionalDisconnect bool
|
||||
authDone bool // Signals that authentication is complete (like IRC bridge)
|
||||
jackboxClient *jackbox.Client
|
||||
}
|
||||
|
||||
// New creates a new Kosmi bridge instance
|
||||
func New(cfg *bridge.Config) bridge.Bridger {
|
||||
b := &Bkosmi{
|
||||
Config: cfg,
|
||||
msgChannel: make(chan config.Message, 100),
|
||||
Config: cfg,
|
||||
}
|
||||
|
||||
return b
|
||||
@@ -110,9 +110,12 @@ func (b *Bkosmi) Connect() error {
|
||||
}
|
||||
|
||||
b.connected = true
|
||||
b.intentionalDisconnect = false
|
||||
b.authDone = true // Signal that authentication is complete
|
||||
b.Log.Info("Successfully connected to Kosmi")
|
||||
|
||||
go b.watchConnection()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -120,15 +123,15 @@ func (b *Bkosmi) Connect() error {
|
||||
func (b *Bkosmi) Disconnect() error {
|
||||
b.Log.Info("Disconnecting from Kosmi")
|
||||
|
||||
b.intentionalDisconnect = true
|
||||
b.connected = false
|
||||
|
||||
if b.client != nil {
|
||||
if err := b.client.Disconnect(); err != nil {
|
||||
b.Log.Errorf("Error closing Kosmi client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
close(b.msgChannel)
|
||||
b.connected = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -216,6 +219,19 @@ func (b *Bkosmi) handleIncomingMessage(payload *NewMessagePayload) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle !votes command: query current game vote tally
|
||||
if strings.TrimSpace(body) == "!votes" {
|
||||
b.Log.Infof("!votes command from %s", username)
|
||||
b.Remote <- config.Message{
|
||||
Username: "system",
|
||||
Text: "votes",
|
||||
Channel: "main",
|
||||
Account: b.Account,
|
||||
Event: config.EventVotesQuery,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create Matterbridge message
|
||||
// Use "main" as the channel name for gateway matching
|
||||
// Don't add prefix here - let the gateway's RemoteNickFormat handle it
|
||||
@@ -240,6 +256,30 @@ func (b *Bkosmi) handleIncomingMessage(payload *NewMessagePayload) {
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
|
||||
// watchConnection monitors the WebSocket client and sends EventFailure
|
||||
// to the gateway when an unexpected disconnect occurs, triggering automatic
|
||||
// reconnection via the gateway's reconnectBridge() mechanism.
|
||||
func (b *Bkosmi) watchConnection() {
|
||||
<-b.client.Done()
|
||||
|
||||
if b.intentionalDisconnect {
|
||||
return
|
||||
}
|
||||
|
||||
b.Log.Warn("Kosmi connection lost unexpectedly, requesting reconnection")
|
||||
b.connected = false
|
||||
|
||||
if b.Remote != nil {
|
||||
b.Remote <- config.Message{
|
||||
Username: "system",
|
||||
Text: "reconnect",
|
||||
Channel: "",
|
||||
Account: b.Account,
|
||||
Event: config.EventFailure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extractRoomID extracts the room ID from a Kosmi room URL
|
||||
// Supports formats:
|
||||
// - https://app.kosmi.io/room/@roomname
|
||||
|
||||
Reference in New Issue
Block a user