Hi - MQTT Chat Client
π Overview
Hi is a terminal-based MQTT chat room that combines the MQTT protocol with Charmbracelet Bubbles to create a beautiful and interactive CLI chat tool.
π Installation
Method 1: Using Go Install
go install github.com/longkeyy/hi@latest
Method 2: Using Homebrew (macOS/Linux)
brew install hi
Method 3: Using Package Managers
Debian/Ubuntu (.deb):
# Download from GitHub Releases
wget https://github.com/longkeyy/hi/releases/download/vX.X.X/hi_X.X.X_linux_amd64.deb
sudo dpkg -i hi_X.X.X_linux_amd64.deb
Fedora/RHEL/CentOS (.rpm):
# Download from GitHub Releases
wget https://github.com/longkeyy/hi/releases/download/vX.X.X/hi_X.X.X_linux_amd64.rpm
sudo rpm -i hi_X.X.X_linux_amd64.rpm
Alpine (.apk):
# Download from GitHub Releases
wget https://github.com/longkeyy/hi/releases/download/vX.X.X/hi_X.X.X_linux_amd64.apk
sudo apk add --allow-untrusted hi_X.X.X_linux_amd64.apk
Method 4: Build from Source
git clone https://github.com/longkeyy/hi.git
cd hi
go build -o hi
π‘ Usage
# Use default parameters (username Bell-xxxx, room hi)
hi
# Specify username and room
hi --name alice --room lobby
# Specify MQTT broker
hi --host mqtt://broker.hivemq.com:1883 --name bob
# Show version
hi --version
π― Features
| Feature |
Description |
| β
MQTT Connection |
Connect to any MQTT broker via --host parameter, defaults to mqtt.eclipse.org:1883 |
| β
Custom Username |
Set username with --name, used as message topic identifier |
| β
Chat Rooms |
Each room has a dedicated topic prefix like chatroom/room1/xxx |
| β
Send Messages |
Publish messages to chatroom/<room>/<name> |
| β
Receive Messages |
Subscribe to chatroom/<room>/# to receive all messages |
| β
Beautiful UI |
Message list + input box UI built with Bubbles |
| β
Sender Distinction |
Visual differentiation between your messages and others' |
π Project Structure
hi/
βββ go.mod # Go module file
βββ cmd/hi/main.go # CLI entry point
βββ hi.go # Core package API (extensible)
βββ mqtt.go # MQTT client (locked)
βββ model.go # TUI model
βββ styles.go # Style definitions
βββ utils.go # Utility functions
βββ examples/ # Example projects
β βββ headless/ # Headless mode example
β βββ with_keyword_replace/ # Keyword filter example
β βββ plugins/ # Example middleware plugins
βββ README.md # Project documentation
π Extensibility
The Hi package provides middleware hooks for customizing message processing:
MessageMiddleware Interface
type MessageMiddleware interface {
// ProcessIncoming processes received messages before displaying
ProcessIncoming(sender, content string) (string, bool)
// ProcessOutgoing processes messages before sending
ProcessOutgoing(content string) (string, bool)
}
Locked Components (Cannot be modified)
- β Topic structure:
chatroom/<room>/<user> (locked)
- β Username format: Must end with
-XXXX (4 digits, locked)
- β MQTT connection logic (locked)
Extensible Components (Can be customized)
- β
Message processing before sending
- β
Message processing after receiving
- β
Keyword filtering/replacement
- β
Encryption/Decryption (for internal development)
- β
Custom message handlers
π» Development Examples
Example 1: Basic CLI Usage
package main
import (
"log"
"github.com/longkeyy/hi"
)
func main() {
config := hi.Config{
Host: "mqtt://test.mosquitto.org:1883",
Username: "alice",
Room: "lobby",
}
client, err := hi.NewClient(config)
if err != nil {
log.Fatal(err)
}
if err := client.Run(); err != nil {
log.Fatal(err)
}
}
Example 2: Keyword Filtering
package main
import (
"log"
"github.com/longkeyy/hi"
"github.com/longkeyy/hi/examples/plugins"
)
func main() {
// Create keyword replacer
replacer := plugins.NewKeywordReplacer(map[string]string{
"bad": "***",
"ugly": "***",
})
config := hi.Config{
Host: "mqtt://test.mosquitto.org:1883",
Username: "alice",
Room: "lobby",
Middleware: []hi.MessageMiddleware{replacer},
}
client, err := hi.NewClient(config)
if err != nil {
log.Fatal(err)
}
client.Run()
}
Example 3: Headless Mode (No TUI)
package main
import (
"fmt"
"log"
"github.com/longkeyy/hi"
)
func main() {
config := hi.Config{
Host: "mqtt://test.mosquitto.org:1883",
Username: "bot",
Room: "lobby",
}
client, err := hi.NewClient(config)
if err != nil {
log.Fatal(err)
}
// Run without TUI
err = client.RunHeadless(func(sender, content string) {
fmt.Printf("[%s]: %s\n", sender, content)
})
if err != nil {
log.Fatal(err)
}
// Send messages programmatically
client.SendMessage("Hello from bot!")
}
Example 4: Custom Encryption Middleware (Internal)
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"github.com/longkeyy/hi"
)
type EncryptionMiddleware struct {
cipher cipher.Block
}
func (e *EncryptionMiddleware) ProcessIncoming(sender, content string) (string, bool) {
// Decrypt received messages
decrypted, _ := decrypt(e.cipher, content)
return decrypted, true
}
func (e *EncryptionMiddleware) ProcessOutgoing(content string) (string, bool) {
// Encrypt before sending
encrypted, _ := encrypt(e.cipher, content)
return encrypted, true
}
func main() {
key := []byte("my-secret-key-32") // 32 bytes for AES-256
cipher, _ := aes.NewCipher(key)
config := hi.Config{
Host: "mqtt://test.mosquitto.org:1883",
Username: "secure-user",
Room: "secure-room",
Middleware: []hi.MessageMiddleware{
&EncryptionMiddleware{cipher: cipher},
},
}
client, _ := hi.NewClient(config)
client.Run()
}
βοΈ Configuration
| Parameter |
Type |
Default |
Description |
--host |
string |
mqtt://test.mosquitto.org:1883 |
MQTT broker address |
--name |
string |
bell-<4 random digits> |
Username (must end with 4 digits), used as message topic |
--room |
string |
hi |
Chat room name, used as topic prefix |
--version |
bool |
false |
Show version information |
π MQTT Topic Structure
MQTT uses the following topic model for chat communication:
chatroom/<room>/<user>
- All users subscribe to:
chatroom/<room>/#
- Each user publishes to:
chatroom/<room>/<your_name>
Example:
chatroom/room1/alice
chatroom/room1/bob
The sender can be extracted from msg.Topic().
π¨ UI Design (Bubbles + Lipgloss)
Layout
βββββββββββββββββββββββββββββββββ
β MQTT Chat Room β <- Status bar (optional)
β ββββββββββββββββββββββββββββββββ£
β π¬ alice: Hello! β <- Message history
β π¬ bob: Hi there~ β
β π¬ me: OK let's go β <- Your messages (green/right-aligned)
β ... β
β ββββββββββββββββββββββββββββββββ£
β > Type message here... β <- textinput.Model
βββββββββββββββββββββββββββββββββ
UI Features
| Element |
Description |
| Input Box |
Implemented with bubbles/textinput |
| Message List |
Scrollable display of recent messages, max N cached |
| Your Messages |
Green color or right-aligned |
| Others' Messages |
Blue/gray color, left-aligned |
| Status Bar (optional) |
Shows username, room, connection status |
π¦ Dependencies
github.com/charmbracelet/bubbletea
github.com/charmbracelet/bubbles
github.com/charmbracelet/lipgloss
github.com/eclipse/paho.mqtt.golang
π Message Flow
sequenceDiagram
participant User as Your Terminal
participant MQTT as MQTT Broker
participant Other as Other Users
User->>MQTT: CONNECT to mqtt.eclipse.org
User->>MQTT: SUBSCRIBE chatroom/room1/#
loop Chat loop
User->>MQTT: PUBLISH chatroom/room1/<name> with msg
MQTT-->>Other: DELIVER msg via chatroom/room1/<name>
MQTT-->>User: ECHO msg (if subscribed)
end
π’ Publishing to Homebrew
After creating a GitHub release with version tag (e.g., v1.0.0):
- Download the release checksums from GitHub
- Update
homebrew-formula/hi.rb with:
- Version number
- SHA256 checksums for each platform
- Submit a PR to homebrew-core
- Follow the Homebrew Formula Cookbook
π License
MIT