From 55977d0b239224da786b5a26dcdb7c2d93d41f34 Mon Sep 17 00:00:00 2001 From: "Peter.Morton" Date: Tue, 17 Feb 2026 13:50:48 -0600 Subject: [PATCH] Moving terminal-messenger into its own repository --- terminal-messenger/application/README.md | 210 -------------------- terminal-messenger/application/go.mod | 8 - terminal-messenger/application/go.sum | 6 - terminal-messenger/application/main.go | 243 ----------------------- 4 files changed, 467 deletions(-) delete mode 100644 terminal-messenger/application/README.md delete mode 100644 terminal-messenger/application/go.mod delete mode 100644 terminal-messenger/application/go.sum delete mode 100644 terminal-messenger/application/main.go diff --git a/terminal-messenger/application/README.md b/terminal-messenger/application/README.md deleted file mode 100644 index 96d2f29..0000000 --- a/terminal-messenger/application/README.md +++ /dev/null @@ -1,210 +0,0 @@ -# Terminal Messenger - -A simple, colorful terminal-based messenger application written in Go that -connects to a remote messaging endpoint with conversation persistence and -history management. - -## Features - -- šŸŽØ **Colorized output** - Blue for your input, green for assistant - responses -- āŒØļø **Input history** - Navigate previous messages with arrow keys -- šŸ’¾ **Conversation persistence** - Resume previous conversations seamlessly -- šŸ“ **Full conversation logs** - Both user and assistant messages saved - with timestamps -- šŸ”„ **Auto-resume** - Lists your 5 most recent conversations on startup -- šŸ·ļø **Model tracking** - Automatically saves and loads the model used per - conversation -- šŸš€ **Simple and fast** - Minimal setup, quick startup - -## Prerequisites - -- Go 1.16 or higher -- Internet connection - -## Installation - -1. **Clone or download the project** - -2. **Install dependencies** - - ```bash - go get github.com/chzyer/readline - ``` - -3. **Build the application (optional)** - ```bash - go build -o messenger main.go - ``` - -## Usage - -### Starting the application - -**Using go run:** - -```bash -go run main.go -``` - -**Or if you built the binary:** - -```bash -./messenger -``` - -### First time usage - -1. Press 'n' or Enter when asked about continuing a conversation -2. Enter a model name (or press Enter for "default") -3. Start chatting with the assistant -4. Type `quit` or `exit` to end the session - -### Resuming a conversation - -1. When prompted, you'll see your recent conversations: - - ``` - Recent conversations: - 1. resp_d2925cd3-d99c-46f6-a70a-05b1453b8d4f (model: gpt-4) - 2. resp_a1234567-89ab-cdef-0123-456789abcdef (model: default) - - Continue? (enter number or 'n' for new): - ``` - -2. Enter the number (1-5) to resume that conversation -3. The last 10 message exchanges will be displayed -4. The model name is automatically loaded -5. Continue chatting from where you left off - -### 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** or type `quit`/`exit` - Exit application - -## How It Works - -### Conversation Files - -For each conversation, three files are created in `/tmp/`: - -1. **`messenger_{ID}.tmp`** - Readline history for arrow key navigation - (user inputs only) -2. **`messenger_{ID}_log.txt`** - Full conversation log with timestamps - (both user and assistant) -3. **`messenger_{ID}_meta.txt`** - Stores the model name for the - conversation - -### Conversation Continuity - -- Each response from the API includes a unique `id` -- This ID is sent as `previous_response_id` in subsequent requests -- This maintains conversation context across the entire session -- When resuming, the conversation ID is loaded and context is preserved - -## API 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 - -```json -{ - "input": "your message here", - "model": "model name", - "previous_response_id": "previous response ID for context", - "metadata": { - "channel": "text" - } -} -``` - -### Response format - -```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 (~160 lines) -ā”œā”€ā”€ 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 files in `/tmp/`. If you encounter permission errors, -ensure you have write access to `/tmp/`. - -### Cannot find previous conversations - -Conversations are stored in `/tmp/` which may be cleared on system -restart. For permanent storage, modify the file paths in `main.go` to use -a different directory like `~/.messenger/`. - -## 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.:> [!WARNING] - -> diff --git a/terminal-messenger/application/go.mod b/terminal-messenger/application/go.mod deleted file mode 100644 index 0bf7c21..0000000 --- a/terminal-messenger/application/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -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 -) diff --git a/terminal-messenger/application/go.sum b/terminal-messenger/application/go.sum deleted file mode 100644 index 2de3a80..0000000 --- a/terminal-messenger/application/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -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= diff --git a/terminal-messenger/application/main.go b/terminal-messenger/application/main.go deleted file mode 100644 index f61b6d7..0000000 --- a/terminal-messenger/application/main.go +++ /dev/null @@ -1,243 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "sort" - "strings" - "time" - - "github.com/chzyer/readline" -) - -const ( - apiURL = "https://router.ivastudio.verint.live/ProxyScript/run/67bca862210071627d32ef12/current/basic_messenger" - blue = "\033[34m" - green = "\033[32m" - reset = "\033[0m" -) - -type Request struct { - Input string `json:"input"` - Model string `json:"model"` - PreviousResponseID string `json:"previous_response_id,omitempty"` - Metadata map[string]string `json:"metadata"` -} - -type Response struct { - ID string `json:"id"` - Status string `json:"status"` - Output []struct { - Text string `json:"text"` - } `json:"output"` -} - -type Messenger struct { - client *http.Client - model string - prevID string -} - -func (m *Messenger) Send(input string) ([]string, error) { - data, err := json.Marshal(Request{input, m.model, m.prevID, map[string]string{"channel": "text"}}) - if err != nil { - return nil, fmt.Errorf("failed to marshal request: %w", err) - } - - resp, err := m.client.Post(apiURL, "application/json", bytes.NewBuffer(data)) - if err != nil { - return nil, fmt.Errorf("failed to send request: %w", err) - } - defer resp.Body.Close() - - // Check HTTP status code - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("HTTP %d: %s - Response body: %s", resp.StatusCode, resp.Status, string(body)) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) - } - - var r Response - if err := json.Unmarshal(body, &r); err != nil { - return nil, fmt.Errorf("failed to parse JSON response: %w - Raw body: %s", err, string(body)) - } - - m.prevID = r.ID - - // Detailed validation of response structure - if r.Status != "completed" { - return nil, fmt.Errorf("response status is '%s' (expected 'completed') - Response ID: %s, Raw body: %s", r.Status, r.ID, string(body)) - } - - if len(r.Output) == 0 { - return nil, fmt.Errorf("response has no output - Response ID: %s, Status: %s, Output length: %d, Raw body: %s", r.ID, r.Status, len(r.Output), string(body)) - } - - // Collect all Output Text values - var texts []string - for i, content := range r.Output { - if content.Text == "" { - fmt.Printf("Warning: Output[%d] has empty text field\n", i) - } - texts = append(texts, content.Text) - } - - if len(texts) == 0 { - return nil, fmt.Errorf("no text content found in response - Response ID: %s, Output items: %d", r.ID, len(r.Output)) - } - - return texts, nil -} - -func getConversations() []string { - files, _ := filepath.Glob("/tmp/messenger_*_log.txt") - type f struct { - id string - t time.Time - } - var list []f - for _, file := range files { - info, _ := os.Stat(file) - id := strings.TrimSuffix(strings.TrimPrefix(filepath.Base(file), "messenger_"), "_log.txt") - list = append(list, f{id, info.ModTime()}) - } - sort.Slice(list, func(i, j int) bool { return list[i].t.After(list[j].t) }) - - var ids []string - for i, item := range list { - if i >= 5 { - break - } - ids = append(ids, item.id) - } - return ids -} - -func readFile(path string) string { - data, _ := os.ReadFile(path) - return strings.TrimSpace(string(data)) -} - -func writeFile(path, content string) { - os.WriteFile(path, []byte(content), 0644) -} - -func appendFile(path, content string) { - f, _ := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - defer f.Close() - f.WriteString(content) -} - -func showHistory(id string) { - lines := strings.Split(readFile(fmt.Sprintf("/tmp/messenger_%s_log.txt", id)), "\n") - start := len(lines) - 20 - if start < 0 { - start = 0 - } - fmt.Println("\n--- Previous Messages ---") - for _, line := range lines[start:] { - if strings.Contains(line, "You: ") { - fmt.Printf(blue+"You: %s"+reset+"\n", strings.SplitN(line, "You: ", 2)[1]) - } else if strings.Contains(line, "Assistant: ") { - fmt.Printf(green+"Assistant: %s"+reset+"\n", strings.SplitN(line, "Assistant: ", 2)[1]) - } - } - fmt.Println("-------------------------\n") -} - -func main() { - var id, model string - - fmt.Println("\nā–‘ā–€ā–ˆā–€ā–‘ā–ˆā–‘ā–ˆā–‘ā–ˆā–€ā–ˆā–‘ā–‘ā–‘ā–ˆā–„ā–ˆā–‘ā–ˆā–€ā–€ā–‘ā–ˆā–€ā–€ā–‘ā–ˆā–€ā–€ā–‘ā–ˆā–€ā–€ā–‘ā–ˆā–€ā–ˆā–‘ā–ˆā–€ā–€ā–‘ā–ˆā–€ā–€ā–‘ā–ˆā–€ā–„\nā–‘ā–‘ā–ˆā–‘ā–‘ā–€ā–„ā–€ā–‘ā–ˆā–€ā–ˆā–‘ā–‘ā–‘ā–ˆā–‘ā–ˆā–‘ā–ˆā–€ā–€ā–‘ā–€ā–€ā–ˆā–‘ā–€ā–€ā–ˆā–‘ā–ˆā–€ā–€ā–‘ā–ˆā–‘ā–ˆā–‘ā–ˆā–‘ā–ˆā–‘ā–ˆā–€ā–€ā–‘ā–ˆā–€ā–„\n░▀▀▀░░▀░░▀░▀░░░▀░▀░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀░▀") - - // Check for recent conversations - convs := getConversations() - if len(convs) > 0 { - fmt.Println("Recent conversations:") - for i, cid := range convs { - m := readFile(fmt.Sprintf("/tmp/messenger_%s_meta.txt", cid)) - fmt.Printf("%d. %s (model: %s)\n", i+1, cid, m) - } - fmt.Print("\nContinue? (enter number or 'n' for new): ") - var choice string - fmt.Scanln(&choice) - var num int - if _, err := fmt.Sscanf(choice, "%d", &num); err == nil && num >= 1 && num <= len(convs) { - id = convs[num-1] - model = readFile(fmt.Sprintf("/tmp/messenger_%s_meta.txt", id)) - fmt.Printf("Continuing: %s (model: %s)\n", id, model) - showHistory(id) - } - } - - if model == "" { - fmt.Print("Enter model name (default: 'default'): ") - fmt.Scanln(&model) - if model == "" { - model = "default" - } - } - - m := &Messenger{&http.Client{Timeout: 10 * time.Second}, model, id} - - fmt.Println("Use ↑/↓ for history, 'quit' to exit\n") - - histFile := "/tmp/messenger_temp.tmp" - if id != "" { - histFile = fmt.Sprintf("/tmp/messenger_%s.tmp", id) - } - - rl, _ := readline.NewEx(&readline.Config{Prompt: blue + "You: " + reset, HistoryFile: histFile}) - defer rl.Close() - - first := id == "" - - for { - input, err := rl.Readline() - if err != nil || input == "quit" || input == "exit" { - break - } - if input = strings.TrimSpace(input); input == "" { - continue - } - - responses, err := m.Send(input) - if err != nil { - fmt.Printf("\n"+reset+"Error sending message:\n%s\n\n", err) - continue - } - - // Join all responses into a single string - response := strings.Join(responses, "\n") - - if first && m.prevID != "" { - first = false - rl.Close() - rl, _ = readline.NewEx(&readline.Config{ - Prompt: blue + "You: " + reset, - HistoryFile: fmt.Sprintf("/tmp/messenger_%s.tmp", m.prevID), - }) - writeFile(fmt.Sprintf("/tmp/messenger_%s_meta.txt", m.prevID), m.model) - fmt.Printf("(ID: %s)\n\n", m.prevID) - } - - if m.prevID != "" { - ts := time.Now().Format("2006-01-02 15:04:05") - logFile := fmt.Sprintf("/tmp/messenger_%s_log.txt", m.prevID) - appendFile(logFile, fmt.Sprintf("[%s] You: %s\n[%s] Assistant: %s\n", ts, input, ts, response)) - } - - fmt.Printf(green+"Assistant: %s"+reset+"\n\n", response) - } - - fmt.Println("Goodbye!") -}