From eef7454c0f49601cc8869a51ab209cb4e403117a Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 15 May 2026 20:56:46 -0400 Subject: [PATCH] Notifications for recording. --- README.md | 7 ++++- barnard.go | 2 ++ extras/barnard-sound.sh | 58 ++++++++++++++++++++++++++++++++++++----- recording_control.go | 28 +++++++++++++++++++- ui.go | 20 ++++++++++++++ 5 files changed, 107 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 16fbb2c..7eaabe7 100644 --- a/README.md +++ b/README.md @@ -111,8 +111,13 @@ Each event has the following parameters: - disconnect: you have disconnected from a server - msg: the channel you are currently connected to has received a message - pm: you have received a private message + - recordstart: you have started recording + - recordstop: you have stopped recording + - recorderror: recording could not start or stopped with an error + - userrecordstart: another user has started recording + - userrecordstop: another user has stopped recording * who: the person causing initiation of the event ("me" for self-generated events) -* what: the body of the event as applicable (message, channel name, etc) +* what: the body of the event as applicable (message, channel name, recording path, error, etc) Warning: Keep in mind that Barnard opens an Alsa sound device when starting. diff --git a/barnard.go b/barnard.go index 3bb3bcd..f1f65dc 100644 --- a/barnard.go +++ b/barnard.go @@ -40,6 +40,8 @@ type Barnard struct { UiInputStatus uiterm.Label SelectedChannel *gumble.Channel selectedUser *gumble.User + statusText string + statusNotice bool notifyChannel chan []string diff --git a/extras/barnard-sound.sh b/extras/barnard-sound.sh index 76812a6..a94698a 100755 --- a/extras/barnard-sound.sh +++ b/extras/barnard-sound.sh @@ -22,6 +22,7 @@ # #--code-- +# shellcheck disable=SC2329 # 1 is off, 0 is on notify=0 sound=0 @@ -68,17 +69,42 @@ msg() { [[ $notify ]] && notify "$1 from $2: $3" } -notify() { -if [[ "$notifyType" == "notify-send" ]]; then - notify-send "$@" -else +notify_fenrir() { + local message="$1" + local socatFile="" + if ! command -v socat > /dev/null 2>&1; then + return 1 + fi if [[ -e "/tmp/fenrirscreenreader-deamon.sock" ]]; then socatFile="/tmp/fenrirscreenreader-deamon.sock" else socatFile="$(find /tmp/ -maxdepth 1 -type s -name 'fenrirscreenreader-*.sock' | head -1)" fi - echo "command say $@" | socat - UNIX-CLIENT:$socatFile -fi + if [[ -z "$socatFile" ]]; then + return 1 + fi + printf 'command say %s\n' "$message" | socat - "UNIX-CLIENT:$socatFile" > /dev/null 2>&1 +} + +notify_speech() { + local message="$1" + if command -v spd-say > /dev/null 2>&1; then + spd-say "$message" > /dev/null 2>&1 && return 0 + fi + if command -v espeak-ng > /dev/null 2>&1; then + espeak-ng "$message" > /dev/null 2>&1 && return 0 + fi + return 1 +} + +notify() { + local message="$*" + if [[ "$notifyType" == "notify-send" ]]; then + command -v notify-send > /dev/null 2>&1 && notify-send "$@" && return 0 + else + notify_fenrir "$message" && return 0 + fi + notify_speech "$message" || true } pm() { @@ -86,6 +112,26 @@ pm() { [[ $notify ]] && notify "$1 from $2: $3" } +recordstart() { + [[ $notify ]] && notify "Recording started." +} + +recordstop() { + [[ $notify ]] && notify "Recording stopped." +} + +recorderror() { + [[ $notify ]] && notify "Recording error: $3" +} + +userrecordstart() { + [[ $notify ]] && notify "$2 started recording." +} + +userrecordstop() { + [[ $notify ]] && notify "$2 stopped recording." +} + status() { # If transmitting: Payphone coin drop tones (1700+2200 Hz simultaneous, like quarter = 5 tones) # If not transmitting: Special Information Tone (SIT) - call failed (913.8, 1370.6, 1776.7 Hz) diff --git a/recording_control.go b/recording_control.go index 45c1a69..6e20f16 100644 --- a/recording_control.go +++ b/recording_control.go @@ -42,12 +42,14 @@ func (b *Barnard) ToggleRecording() { func (b *Barnard) StartRecording() { if b.Client == nil || b.Client.Self == nil { b.AddOutputLine("Recording requires an active server connection") + b.Notify("recorderror", "me", "Recording requires an active server connection") return } b.RecordingMutex.Lock() if b.recordingAllowed != nil && !*b.recordingAllowed { b.RecordingMutex.Unlock() b.AddOutputLine("Recording is not allowed by this server.") + b.Notify("recorderror", "me", "Recording is not allowed by this server.") return } if b.Recorder != nil || b.recordingStarting { @@ -60,6 +62,7 @@ func (b *Barnard) StartRecording() { b.Client.Self.SetRecording(true) b.AddOutputLine("Recording start requested") + b.renderGeneralStatus() } func (b *Barnard) StopRecording(notifyServer bool) { @@ -70,20 +73,25 @@ func (b *Barnard) StopRecording(notifyServer bool) { } if wasPending { b.AddOutputLine("Recording start cancelled") + b.Notify("recordstop", "me", "Recording start cancelled") } else { b.AddOutputLine("Recording is not active") } + b.renderGeneralStatus() return } if err := recorder.Stop(); err != nil { b.AddOutputLine(fmt.Sprintf("Recording stopped with error: %s", err)) + b.Notify("recorderror", "me", err.Error()) } else { b.AddOutputLine(fmt.Sprintf("Recording saved: %s", path)) + b.Notify("recordstop", "me", path) } if notifyServer && b.Client != nil && b.Client.Self != nil { b.Client.Self.SetRecording(false) } + b.renderGeneralStatus() } func (b *Barnard) StopRecordingIfActive(notifyServer bool) { @@ -114,8 +122,10 @@ func (b *Barnard) HandleRecordingChange(e *gumble.UserChangeEvent) { } if e.User.Recording { b.AddOutputLine(fmt.Sprintf("%s started recording", e.User.Name)) + b.Notify("userrecordstart", e.User.Name, "") } else { b.AddOutputLine(fmt.Sprintf("%s stopped recording", e.User.Name)) + b.Notify("userrecordstop", e.User.Name, "") } } @@ -130,6 +140,7 @@ func (b *Barnard) HandleRecordingAllowed(allowed *bool) { b.RecordingMutex.Unlock() if !value && active { b.AddOutputLine("Recording is not allowed by this server.") + b.Notify("recorderror", "me", "Recording is not allowed by this server.") b.StopRecording(true) } } @@ -154,7 +165,9 @@ func (b *Barnard) finishRecordingStart() { b.recordingStarting = false b.RecordingMutex.Unlock() b.AddOutputLine(fmt.Sprintf("Could not start recording: %s", err)) + b.Notify("recorderror", "me", err.Error()) b.Client.Self.SetRecording(false) + b.renderGeneralStatus() return } @@ -171,6 +184,14 @@ func (b *Barnard) finishRecordingStart() { b.Stream.SetRecorder(recorder) } b.AddOutputLine(fmt.Sprintf("Recording started: %s", recorder.Path())) + b.Notify("recordstart", "me", recorder.Path()) + b.renderGeneralStatus() +} + +func (b *Barnard) isRecordingActive() bool { + b.RecordingMutex.Lock() + defer b.RecordingMutex.Unlock() + return b.Recorder != nil || b.recordingStarting } func (b *Barnard) detachRecorder() (*recording.Recorder, string, bool) { @@ -191,10 +212,15 @@ func (b *Barnard) detachRecorder() (*recording.Recorder, string, bool) { } func (b *Barnard) stopRecordingForDisconnect() { - recorder, _, _ := b.detachRecorder() + recorder, path, _ := b.detachRecorder() if recorder != nil { if err := recorder.Stop(); err != nil { b.AddOutputLine(fmt.Sprintf("Recording stopped with error: %s", err)) + b.Notify("recorderror", "me", err.Error()) + } else { + b.AddOutputLine(fmt.Sprintf("Recording saved: %s", path)) + b.Notify("recordstop", "me", path) } } + b.renderGeneralStatus() } diff --git a/ui.go b/ui.go index deee74a..a179f02 100644 --- a/ui.go +++ b/ui.go @@ -115,6 +115,14 @@ func (b *Barnard) OnVoiceEffectCycle(ui *uiterm.Ui, key uiterm.Key) { } func (b *Barnard) UpdateGeneralStatus(text string, notice bool) { + b.statusText = text + b.statusNotice = notice + b.renderGeneralStatus() +} + +func (b *Barnard) renderGeneralStatus() { + text := b.statusText + notice := b.statusNotice if notice { b.UiStatus.Fg = uiterm.ColorWhite | uiterm.AttrBold b.UiStatus.Bg = uiterm.ColorRed @@ -122,6 +130,16 @@ func (b *Barnard) UpdateGeneralStatus(text string, notice bool) { b.UiStatus.Fg = uiterm.ColorBlack b.UiStatus.Bg = uiterm.ColorWhite } + if b.isRecordingActive() { + switch strings.TrimSpace(text) { + case "Idle": + text = " Idle Rec " + case "Tx": + text = " Tx Rec " + case "File": + text = " File Rec " + } + } b.UiStatus.Text = text b.Ui.Refresh() } @@ -423,6 +441,8 @@ func (b *Barnard) OnUiInitialize(ui *uiterm.Ui) { Fg: uiterm.ColorBlack, Bg: uiterm.ColorWhite, } + b.statusText = b.UiStatus.Text + b.statusNotice = false ui.Add(uiViewStatus, &b.UiStatus) b.UiInput = uiterm.Textbox{