diff --git a/README.md b/README.md index b81655b..a192f64 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,12 @@ go run cmd/main.go ``` +- Run on nix + +```shell +nix run git+https://git.foglar.tech/foglar/WhspBrd.git +``` + ## File structure - may be edited in accordance with [this project standard](https://github.com/golang-standards/project-layout) diff --git a/_examples/mpris.go b/_examples/mpris.go new file mode 100644 index 0000000..54681ca --- /dev/null +++ b/_examples/mpris.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "log" + "whspbrd/pkg/mpris" +) + +func main() { + players, err := mpris.Mpris() + if err != nil { + log.Fatalf("Error: %v", err) + } + + for _, p := range players { + fmt.Printf("Player: %s\n", p.Name) + fmt.Printf("Title : %s\n", p.Title) + if len(p.Artist) > 0 { + fmt.Printf("Artist: %s\n", p.Artist[0]) + } + fmt.Printf("Album : %s\n", p.Album) + + if p.ArtURL != "" { + if mpris.IsRemoteArt(p.ArtURL) { + fmt.Printf("Remote artwork found: %s\n", p.ArtURL) + err := mpris.DownloadArt(p.ArtURL, p.Name+"_art.jpg") + if err != nil { + fmt.Printf("Error downloading art: %v\n", err) + } else { + fmt.Printf("Artwork saved as %s_art.jpg\n", p.Name) + } + } else { + fmt.Printf("Local artwork: %s\n", p.ArtURL) + } + } else { + fmt.Println("Artwork: not available") + } + fmt.Println() + } +} diff --git a/configs/servers/default/users/bob/messages.json b/configs/servers/default/users/bob/messages.json index da818a1..cddfcb5 100644 --- a/configs/servers/default/users/bob/messages.json +++ b/configs/servers/default/users/bob/messages.json @@ -20,6 +20,13 @@ "receiver": "Filip", "content": "VG8gYnljaG9tIG1vaGxpLCB2eWhvdnVqZSB0aSB0ZW5obGUgdMO9ZGVuIHZlIHN0xZllZHU/Cg==", "timestamp": "2024-10-01T11:23:00Z" + }, + { + "id": "4", + "sender": "Bob", + "receiver": "Filip", + "content": "TmVibyB2ZSDEjXR2cnRlawo=", + "timestamp": "2024-10-01T11:24:00Z" } ] } diff --git a/docs/TODO.md b/docs/TODO.md new file mode 100644 index 0000000..c1646d6 --- /dev/null +++ b/docs/TODO.md @@ -0,0 +1,65 @@ +# Tasks to be done + +## TUI + +- [ ] Initial configuration setup +- [ ] Solve colors issue +- [ ] Complete chat loading and sending messages to contacts so it is written to the file +- [ ] Create systray +- [ ] Share what music am i listening using mpris + +### Chat + +- [ ] Add scrolling capability +- [ ] Solve multiline messages and resizing the window +- [ ] Implement calculating positions of images and profile pictures +- [ ] Add rendering image previews + - [ ] Render image in chat + - [ ] Render image in chat with scroll +- [ ] Add rendering profile pictures +- [ ] Solve too long message history problem - render only first 100 messages? +- [ ] Check chat without images, if kitty image protocol is not used +- [ ] Check chat on windows +- [ ] Create timestamps like today yesterday etc... + +### Sidebar + +- [ ] Profile pictures next to the username +- [ ] Ability to scroll through users and rerender profile images +- [ ] Colors maybe? +- [ ] Solve too long names and window width + +### Input + +- [ ] Add emoji internal selector +- [ ] Add button to add attachement +- [ ] Add correct parsing of input to correct chat + +### Profile Sidebar + +- [ ] Profile image +- [ ] Mpris integration + +## CMD + +- [ ] Create basic commands template to use chat from commandline + +## Configuration + +- [ ] Keybindings for application +- [ ] IDs instead of usernames +- [ ] Load and Write new messages to the files +- [ ] Change preloading of Contacts from list to complex structure +- [ ] Change preloading of messages from list to complex structure +- [ ] Add new contact +- [ ] Choose server to use when running TUI +- [ ] Create colorthemes +- [ ] Add reactions to messages and replies + +## Server + +## Communication + +## IDEAS + +- just set window size of the box to be even, so there should not be need in rendering half of the profile picture diff --git a/go.mod b/go.mod index 47e5ed9..2df5518 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module whspbrd go 1.24.2 require ( + github.com/Endg4meZer0/go-mpris v1.0.5 + github.com/godbus/dbus/v5 v5.1.0 github.com/jroimartin/gocui v0.5.0 golang.org/x/sys v0.30.0 ) diff --git a/go.sum b/go.sum index ea34a04..b0910c7 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,8 @@ +github.com/Endg4meZer0/go-mpris v1.0.5 h1:a3FUGD/h3tQAFlcl/YH4TVwtI/NVm4UJAGMEyaKai7I= +github.com/Endg4meZer0/go-mpris v1.0.5/go.mod h1:eh//AAqSR5XF4G2mAKe9NrSp5sH8G6f3RnYzbupi8tU= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/jroimartin/gocui v0.5.0 h1:DCZc97zY9dMnHXJSJLLmx9VqiEnAj0yh0eTNpuEtG/4= github.com/jroimartin/gocui v0.5.0/go.mod h1:l7Hz8DoYoL6NoYnlnaX6XCNR62G7J5FfSW5jEogzaxE= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= diff --git a/internal/tui/colors.go b/internal/tui/colors.go index ec2b119..db95bfe 100644 --- a/internal/tui/colors.go +++ b/internal/tui/colors.go @@ -66,8 +66,6 @@ func (p Palette) Underlined(c Color) string { return c.Underline() } // convenie func (p Palette) Text(c Color) string { return c.Text() } var Colors = Palette{ - // I kept the numeric values that you originally used in your map so behavior stays the same. - // If you'd rather map them to "standard" 30..37 and 90..97 codes, change the integers here. Base00: NewColor(31), Base01: NewColor(32), Base02: NewColor(33), diff --git a/pkg/mpris/mpris.go b/pkg/mpris/mpris.go new file mode 100644 index 0000000..8b8de66 --- /dev/null +++ b/pkg/mpris/mpris.go @@ -0,0 +1,95 @@ +package mpris + +import ( + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/Endg4meZer0/go-mpris" + "github.com/godbus/dbus/v5" +) + +type PlayerInfo struct { + Name string + Title string + Artist []string + Album string + ArtURL string +} + +func Mpris() ([]PlayerInfo, error) { + conn, err := dbus.SessionBus() + if err != nil { + return nil, err + } + + names, err := mpris.List(conn) + if err != nil { + return nil, err + } + if len(names) == 0 { + return nil, fmt.Errorf("no MPRIS players found") + } + + var players []PlayerInfo + for _, name := range names { + player := mpris.New(conn, name) + + metadata, err := player.GetMetadata() + if err != nil { + return nil, err + } + + title, _ := metadata["xesam:title"].Value().(string) + artist, _ := metadata["xesam:artist"].Value().([]string) + album, _ := metadata["xesam:album"].Value().(string) + + var artURL string + if val, ok := metadata["mpris:artUrl"]; ok { + if url, ok := val.Value().(string); ok { + artURL = url + } + } + + players = append(players, PlayerInfo{ + Name: name, + Title: title, + Artist: artist, + Album: album, + ArtURL: artURL, + }) + } + + return players, nil +} + +func IsRemoteArt(uri string) bool { + return strings.HasPrefix(uri, "http://") || strings.HasPrefix(uri, "https://") +} + +func DownloadArt(url, dest string) error { + resp, err := http.Get(url) + if err != nil { + return fmt.Errorf("failed to fetch art: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("bad status: %s", resp.Status) + } + + out, err := os.Create(dest) + if err != nil { + return fmt.Errorf("failed to create file: %w", err) + } + defer out.Close() + + _, err = io.Copy(out, resp.Body) + if err != nil { + return fmt.Errorf("failed to save art: %w", err) + } + + return nil +}