@@ -0,0 +1,74 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+The Mumbling Herald is a Bash script that monitors Mumble server logs and sends XMPP notifications when users join or leave specified channels. It acts as a "grumpy old herald" that announces comings and goings.
+
+## Core Architecture
+
+- **Single executable**: `mumblingherald` - Main Bash script containing all functionality
+- **Configuration**: Variables at the top of the script for paths, recipients, and monitored channels
+- **Dependencies**:
+ - `go-sendxmpp` for XMPP messaging
+ - `tail -F` for log monitoring
+ - Standard Bash utilities
+
+## Key Components
+
+- **Log parsing**: Uses regex to extract user movements and disconnections from Mumble server logs
+- **Message templates**: Arrays of varied join/leave messages with placeholder substitution, embodying a playfully grumpy herald persona
+- **Channel filtering**: Only monitors specific channels defined in `MONITORED_CHANNELS` array
+- **XMPP integration**: Sends notifications via `go-sendxmpp` to configured recipients
+
+## Configuration Variables
+
+Edit these variables in the `mumblingherald` script:
+
+- `MUMBLE_LOG_FILE`: Path to Mumble server log file
+- `XMPP_RECIPIENTS`: Target JID for notifications
+- `MONITORED_CHANNELS`: Array of channel names to watch
+
+## Development Commands
+
+Since this is a single Bash script, there are no build or test commands, just shellcheck.
+
+```bash
+# Make executable
+chmod +x mumblingherald
+
+# Test syntax
+bash -n mumblingherald
+
+# Run shellcheck
+shellcheck mumblingherald
+
+# Run directly
+./mumblingherald
+```
+
+## Deployment
+
+The script is designed to run as a systemd service under a dedicated user account. See README.md for full setup instructions including user creation, file permissions, and systemd service configuration.
+
+## Log Format Parsing
+
+The script parses specific Mumble server log formats:
+
+- Move events: `<W>YYYY-MM-DD HH:MM:SS.mmm N => <ID:Username(N)> Moved Username:ID(N) to ChannelName[ID:N]`
+- Disconnect events: `<W>YYYY-MM-DD HH:MM:SS.mmm N => <ID:Username(N)> Connection closed: reason`
+
+## Message Templates
+
+The herald's personality is expressed through varied message templates:
+
+- **Join messages**: Varied announcements ranging from formal to grumbling observations
+- **Leave messages**: Poetic farewells and wistful commentary
+- **Tone**: Playfully grumpy rather than mean-spirited - like a lovably cranky character
+- **Format**: Messages use `{USERNAME}` and `{CHANNEL}` placeholders for substitution
+- **Character actions**: Include physical actions like `*grumbles*`, `*adjusts spectacles*`, `*mutters*`
+
+## Development Best Practices
+
+- Use shellcheck after every edit.
@@ -41,6 +41,28 @@ JOIN_MESSAGES=(
"➡️ {USERNAME} finds their way to {CHANNEL}"
"➡️ Lo! {USERNAME} enters {CHANNEL}"
"➡️ {USERNAME} materializes in {CHANNEL}"
+ "➡️ *grumbles* Oh, look who's decided to grace {CHANNEL} with their presence... {USERNAME}"
+ "➡️ *mutters* {USERNAME} has arrived in {CHANNEL}. The halls seem livelier already..."
+ "➡️ *adjusts spectacles* Hmph! {USERNAME} wanders into {CHANNEL} with purpose unknown"
+ "➡️ *sighs heavily* Another soul wanders into {CHANNEL}... {USERNAME}, was it?"
+ "➡️ *coughs wheezily* {USERNAME} has deigned to visit {CHANNEL}. How... delightful."
+ "➡️ *chuckles dryly* {USERNAME} stumbles through the door of {CHANNEL} with characteristic grace"
+ "➡️ *squints curiously* {USERNAME} enters {CHANNEL}. Wonder what brings them here..."
+ "➡️ *waves vaguely* {USERNAME} arrives in {CHANNEL}. Mind the ancient furniture!"
+ "➡️ *mutters under breath* {USERNAME} finds their way to {CHANNEL}. Probably seeking wisdom..."
+ "➡️ *croaks* Ah, {USERNAME} graces {CHANNEL} with their interesting presence"
+ "➡️ *grumbles thoughtfully* {USERNAME} has somehow found their way to {CHANNEL}. Remarkable navigation."
+ "➡️ *peers over ancient tome* {USERNAME} joins the gathered souls in {CHANNEL}. Welcome, I suppose."
+ "➡️ *mutters* {USERNAME} makes their way to {CHANNEL}. The determination is... admirable"
+ "➡️ *nods grudgingly* {USERNAME} appears in {CHANNEL} like morning mist. Atmospheric."
+ "➡️ *shakes head* {USERNAME} wanders into {CHANNEL}. The sense of direction improves..."
+ "➡️ *grumbles* {USERNAME} shuffles into {CHANNEL} with their usual enthusiasm"
+ "➡️ *wheezes* {USERNAME} has emerged from elsewhere to visit {CHANNEL}"
+ "➡️ *mutters approvingly* {USERNAME} enters {CHANNEL} like they belong here. Perhaps they do..."
+ "➡️ *coughs* Behold! {USERNAME} graces {CHANNEL} with their notable presence"
+ "➡️ *adjusts robes* {USERNAME} ventures into {CHANNEL}. Mind the ancient cobwebs!"
+ "➡️ *grumbles* Another wanderer... {USERNAME} discovers {CHANNEL}"
+ "➡️ *sighs contentedly* {USERNAME} has arrived in {CHANNEL}. The company improves..."
)
# shellcheck disable=SC2034
@@ -53,6 +75,34 @@ LEAVE_MESSAGES=(
"⬅️ {USERNAME} has made their exit..."
"⬅️ {USERNAME} has fled our realm..."
"⬅️ *sighs* And thus {USERNAME} departs..."
+ "⬅️ *grumbles* Well, that was brief... {USERNAME} has wandered off to other adventures"
+ "⬅️ *mutters* {USERNAME} has departed from us. Just when things were getting interesting..."
+ "⬅️ *coughs* {USERNAME} has evaporated like morning mist. Until next time, I suppose..."
+ "⬅️ *shrugs* {USERNAME} has left the scene. Probably remembered important business elsewhere"
+ "⬅️ *waves hand thoughtfully* {USERNAME} vanishes with mysterious purpose and timing"
+ "⬅️ *mutters under breath* {USERNAME} has slipped away into the digital realm. How intriguing..."
+ "⬅️ *sighs deeply* {USERNAME} departs, leaving only fond memories and ancient wisdom"
+ "⬅️ *croaks* {USERNAME} has dissolved into the ether. Poof! Like morning fog..."
+ "⬅️ *grumbles* {USERNAME} has made their exit. Probably off to share tales elsewhere"
+ "⬅️ *adjusts spectacles* {USERNAME} retreats to their domain. The quiet has its merits..."
+ "⬅️ *wheezes* {USERNAME} has ghosted us. How very contemporary of them..."
+ "⬅️ *mutters thoughtfully* {USERNAME} slips away like mist at dawn. Quite poetic..."
+ "⬅️ *nods* {USERNAME} has disconnected from our realm. Until we meet again..."
+ "⬅️ *grumbles* {USERNAME} departs swiftly, like autumn leaves on the wind"
+ "⬅️ *sighs* {USERNAME} has vanished without fanfare. The modern way of things..."
+ "⬅️ *coughs* {USERNAME} evaporates like ancient ink on weathered parchment"
+ "⬅️ *mutters* {USERNAME} retreats to whatever cozy corner they call sanctuary"
+ "⬅️ *waves appreciatively* {USERNAME} fades like shadows at dawn. Rather elegant, really..."
+ "⬅️ *grumbles contentedly* {USERNAME} has departed. Probably remembered pressing matters"
+ "⬅️ *shakes head* {USERNAME} dissolves into the digital realm. How wonderfully modern..."
+ "⬅️ *mutters* {USERNAME} has pulled a vanishing act. The masters would be impressed"
+ "⬅️ *sighs wistfully* {USERNAME} departs, leaving only pleasant echoes behind"
+ "⬅️ *croaks* {USERNAME} has melted away like snow in spring. The natural order..."
+ "⬅️ *grumbles* {USERNAME} moves on faster than news travels in peaceful times"
+ "⬅️ *adjusts robes* {USERNAME} has retreated to their digital sanctuary. Journey well..."
+ "⬅️ *mutters approvingly* {USERNAME} vanishes like my enthusiasm for new technology"
+ "⬅️ *wheezes* {USERNAME} has departed this virtual realm... err, channel"
+ "⬅️ *sighs* {USERNAME} evaporates like morning dew on pleasant meadows"
)
# Function to get a random message from an array