Adding terminal messenger

This is a golang based application that connects to the basic messenger
This commit is contained in:
Peter Morton 2025-11-01 22:46:34 -05:00
parent 97ac50cb0a
commit fc9b17bcec
4 changed files with 376 additions and 0 deletions

View File

@ -0,0 +1,166 @@
# Terminal Messenger
A simple, colorful terminal-based messenger application written in Go that connects to a remote messaging endpoint.
## Features
- 🎨 **Colorized output** - Blue for your input, green for assistant responses
- ⌨️ **Input history** - Navigate previous messages with arrow keys
- 💬 **Conversation context** - Maintains conversation continuity across messages
- 🚀 **Simple and fast** - Minimal setup, quick startup
- 📝 **Line editing** - Full cursor movement and text editing support
## Prerequisites
- Go 1.16 or higher
- Internet connection
## Installation
1. **Clone or download the project**
2. **Initialize Go module**
```bash
go mod init messenger
```
3. **Install dependencies**
```bash
go get github.com/chzyer/readline
```
4. **Build the application (optional)**
```bash
go build -o messenger main.go
```
## Usage
### Running the application
**Using go run:**
```bash
go run main.go
```
**Or if you built the binary:**
```bash
./messenger
```
### Getting started
1. When prompted, enter a model name (or press Enter for "default")
2. Start typing your messages
3. The assistant will respond to each message
4. Use arrow keys to navigate through your input history
5. Type `quit` or `exit` to end the session
### Keyboard shortcuts
- **↑ (Up arrow)** - Navigate to previous input
- **↓ (Down arrow)** - Navigate to next input
- **←/→ (Left/Right arrows)** - Move cursor within current input
- **Ctrl+A** - Jump to beginning of line
- **Ctrl+E** - Jump to end of line
- **Ctrl+C** - Interrupt/exit application
## Configuration
The application connects to:
```
https://router.ivastudio.verint.live/ProxyScript/run/67bca862210071627d32ef12/current/basic_messenger
```
To change the endpoint, modify the `apiURL` constant in `main.go`.
### Request format
The application sends POST requests with the following JSON structure:
```json
{
"input": "your message here",
"model": "model name",
"previous_response_id": "previous response ID for context",
"metadata": {
"channel": "text"
}
}
```
### Response format
Expected response structure:
```json
{
"id": "response-id",
"object": "response",
"created_at": 1234567890,
"status": "completed",
"model": "Model Name",
"output": [
{
"type": "message",
"id": "message-id",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Assistant response here",
"annotations": []
}
]
}
]
}
```
## Project Structure
```
.
├── main.go # Main application file
├── go.mod # Go module file
└── README.md # This file
```
## Troubleshooting
### Connection errors
If you encounter connection errors, check:
- Your internet connection
- The endpoint URL is accessible
- Firewall settings aren't blocking the connection
### Module errors
If you get "module not found" errors:
```bash
go mod tidy
```
### History file permissions
The app creates a history file at `/tmp/messenger_history.tmp`. If you encounter permission errors, ensure you have write access to `/tmp/`.
## Dependencies
- [github.com/chzyer/readline](https://github.com/chzyer/readline) - For input history and line editing
## License
This project is provided as-is for educational and development purposes.
## Contributing
Feel free to submit issues, fork the repository, and create pull requests for any improvements.

View File

@ -0,0 +1,8 @@
module messenger
go 1.24.6
require (
github.com/chzyer/readline v1.5.1 // indirect
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
)

View File

@ -0,0 +1,6 @@
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -0,0 +1,196 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/chzyer/readline"
)
const (
apiURL = "https://router.ivastudio.verint.live/ProxyScript/run/67bca862210071627d32ef12/current/basic_messenger"
colorReset = "\033[0m"
colorBlue = "\033[34m"
colorGreen = "\033[32m"
)
type Metadata struct {
Channel string `json:"channel"`
}
type Message struct {
Input string `json:"input"`
Model string `json:"model"`
PreviousResponseID string `json:"previous_response_id,omitempty"`
Metadata Metadata `json:"metadata"`
}
type ContentItem struct {
Type string `json:"type"`
Text string `json:"text"`
Annotations []string `json:"annotations"`
}
type OutputMessage struct {
Type string `json:"type"`
ID string `json:"id"`
Role string `json:"role"`
Content []ContentItem `json:"content"`
}
type Response struct {
ID string `json:"id"`
Object string `json:"object"`
CreatedAt int64 `json:"created_at"`
Status string `json:"status"`
Model string `json:"model"`
Output []OutputMessage `json:"output"`
}
type Messenger struct {
client *http.Client
model string
previousResponseID string
}
func NewMessenger(model string) *Messenger {
return &Messenger{
client: &http.Client{
Timeout: 10 * time.Second,
},
model: model,
}
}
func (m *Messenger) sendRequest(input string) (*Response, error) {
msg := Message{
Input: input,
Model: m.model,
PreviousResponseID: m.previousResponseID,
Metadata: Metadata{
Channel: "text",
},
}
jsonData, err := json.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("failed to marshal JSON: %w", err)
}
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := m.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
var response Response
if err := json.Unmarshal(body, &response); err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
// Update previous response ID for conversation continuity
if response.ID != "" {
m.previousResponseID = response.ID
}
return &response, nil
}
func (m *Messenger) SendMessage(text string) (string, error) {
resp, err := m.sendRequest(text)
if err != nil {
return "", err
}
if resp.Status != "completed" {
return "", fmt.Errorf("response status: %s", resp.Status)
}
// Extract text from the response output
if len(resp.Output) > 0 && len(resp.Output[0].Content) > 0 {
return resp.Output[0].Content[0].Text, nil
}
return "", fmt.Errorf("no output received")
}
func printHeader() {
fmt.Println("=== Terminal Messenger ===")
fmt.Println("Type your message (or 'quit' to exit)")
fmt.Println("Use ↑/↓ arrows to navigate history")
fmt.Println("==========================")
fmt.Println()
}
func main() {
// Get model
fmt.Print("Enter model name (default: 'default'): ")
var model string
fmt.Scanln(&model)
if model == "" {
model = "default"
}
messenger := NewMessenger(model)
printHeader()
// Setup readline for input history and line editing
rl, err := readline.NewEx(&readline.Config{
Prompt: colorBlue + "You: " + colorReset,
HistoryFile: "/tmp/messenger_history.tmp",
InterruptPrompt: "^C",
EOFPrompt: "exit",
})
if err != nil {
fmt.Printf("Error setting up readline: %v\n", err)
return
}
defer rl.Close()
// Main message loop
for {
input, err := rl.Readline()
if err != nil { // io.EOF or readline.ErrInterrupt
break
}
input = strings.TrimSpace(input)
if input == "quit" || input == "exit" {
break
}
if input == "" {
continue
}
response, err := messenger.SendMessage(input)
if err != nil {
fmt.Printf("Error: %v\n", err)
continue
}
fmt.Printf(colorGreen+"Assistant: %s"+colorReset+"\n\n", response)
}
fmt.Println("\nGoodbye!")
}