MVP 1.0: IRC-over-HTTP chat server #10

Merged
sneak merged 18 commits from feature/mvp-1.0 into main 2026-02-27 07:21:35 +01:00
3 changed files with 23 additions and 26 deletions
Showing only changes of commit d6408b2853 - Show all commits

View File

@@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/url"
"strings"
"time"
)
@@ -167,7 +168,9 @@ func (c *Client) ListChannels() ([]Channel, error) {
// GetMembers returns members of a channel.
func (c *Client) GetMembers(channel string) ([]string, error) {
data, err := c.do("GET", "/api/v1/channels/"+url.PathEscape(channel)+"/members", nil)
// Server route is /channels/{channel}/members where channel is without '#'
name := strings.TrimPrefix(channel, "#")
data, err := c.do("GET", "/api/v1/channels/"+url.PathEscape(name)+"/members", nil)
if err != nil {
return nil, err
}

View File

@@ -1,4 +1,4 @@
package chatapi
package api
import "time"
@@ -9,45 +9,40 @@ type SessionRequest struct {
// SessionResponse is the response from POST /api/v1/session.
type SessionResponse struct {
SessionID string `json:"sessionId"`
ClientID string `json:"clientId"`
Nick string `json:"nick"`
Token string `json:"token"`
ID int64 `json:"id"`
Nick string `json:"nick"`
Token string `json:"token"`
}
// StateResponse is the response from GET /api/v1/state.
type StateResponse struct {
SessionID string `json:"sessionId"`
ClientID string `json:"clientId"`
Nick string `json:"nick"`
Channels []string `json:"channels"`
ID int64 `json:"id"`
Nick string `json:"nick"`
Channels []string `json:"channels"`
}
// Message represents a chat message envelope.
type Message struct {
Command string `json:"command"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
Params []string `json:"params,omitempty"`
Body any `json:"body,omitempty"`
ID string `json:"id,omitempty"`
TS string `json:"ts,omitempty"`
Meta any `json:"meta,omitempty"`
Command string `json:"command"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
Params []string `json:"params,omitempty"`
Body interface{} `json:"body,omitempty"`
ID string `json:"id,omitempty"`
TS string `json:"ts,omitempty"`
Meta interface{} `json:"meta,omitempty"`
}
// BodyLines returns the body as a slice of strings (for text
// messages).
// BodyLines returns the body as a slice of strings (for text messages).
func (m *Message) BodyLines() []string {
switch v := m.Body.(type) {
case []any:
case []interface{}:
lines := make([]string, 0, len(v))
for _, item := range v {
if s, ok := item.(string); ok {
lines = append(lines, s)
}
}
return lines
case []string:
return v
@@ -61,7 +56,7 @@ type Channel struct {
Name string `json:"name"`
Topic string `json:"topic"`
Members int `json:"members"`
CreatedAt string `json:"createdAt"`
CreatedAt string `json:"created_at"`
}
// ServerInfo is the response from GET /api/v1/server.
@@ -89,6 +84,5 @@ func (m *Message) ParseTS() time.Time {
if err != nil {
return time.Now()
}
return t
}

View File

@@ -145,7 +145,7 @@ func (a *App) cmdConnect(serverURL string) {
a.lastQID = 0
a.mu.Unlock()
a.ui.AddStatus(fmt.Sprintf("[green]Connected! Nick: %s, Session: %s", resp.Nick, resp.SessionID))
a.ui.AddStatus(fmt.Sprintf("[green]Connected! Nick: %s, Session: %d", resp.Nick, resp.ID))
a.ui.SetStatus(resp.Nick, "", "connected")
// Start polling.