Several changes
This commit is contained in:
8
TODO.md
Normal file
8
TODO.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# TODO
|
||||||
|
- XEP-0153: vCard-Based Avatars 0%
|
||||||
|
- XEP-0393: Message Styling 0%
|
||||||
|
- XEP-0402: PEP Native Bookmarks 0%
|
||||||
|
- XEP-0066: Out of Band Data partial
|
||||||
|
- XEP-0461: Message Replies partial
|
||||||
|
- XEP-0444: Message Reactions partial
|
||||||
|
|
||||||
31
cache.go
31
cache.go
@@ -4,19 +4,29 @@ package main
|
|||||||
// It also does the same for images that need to be grabbed from HTTP.
|
// It also does the same for images that need to be grabbed from HTTP.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"github.com/kirsle/configdir"
|
||||||
)
|
)
|
||||||
|
|
||||||
// global or app-level map/cache
|
// global or app-level map/cache
|
||||||
var textureCache = make(map[string]gdk.Paintabler)
|
var textureCache = make(map[string]gdk.Paintabler)
|
||||||
|
|
||||||
|
func ensureCache() (string, error) {
|
||||||
|
cachePath := configdir.LocalCache("lambda-im")
|
||||||
|
err := configdir.MakePath(cachePath) // Ensure it exists.
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachePath, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getTexture(path string) gdk.Paintabler {
|
func getTexture(path string) gdk.Paintabler {
|
||||||
if tex, exists := textureCache[path]; exists {
|
if tex, exists := textureCache[path]; exists {
|
||||||
@@ -43,6 +53,7 @@ func newImageFromPath(path string) *gtk.Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newPictureFromWeb(url string) *gtk.Picture {
|
func newPictureFromWeb(url string) *gtk.Picture {
|
||||||
|
pa, _ := ensureCache()
|
||||||
// step 1: get a sha256 sum of the URL
|
// step 1: get a sha256 sum of the URL
|
||||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||||
|
|
||||||
@@ -62,16 +73,19 @@ func newPictureFromWeb(url string) *gtk.Picture {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fullpath := filepath.Join(pa, sum)
|
||||||
|
|
||||||
// step 3: save it
|
// step 3: save it
|
||||||
err = os.WriteFile(sum, b, 0644)
|
err = os.WriteFile(fullpath, b, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return newPictureFromPath(sum)
|
return newPictureFromPath(fullpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newImageFromWeb(url string) *gtk.Image {
|
func newImageFromWeb(url string) *gtk.Image {
|
||||||
|
pa, _ := ensureCache()
|
||||||
// step 1: get a sha256 sum of the URL
|
// step 1: get a sha256 sum of the URL
|
||||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||||
|
|
||||||
@@ -91,11 +105,14 @@ func newImageFromWeb(url string) *gtk.Image {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fullpath := filepath.Join(pa, sum)
|
||||||
|
|
||||||
// step 3: save it
|
// step 3: save it
|
||||||
err = os.WriteFile(sum, b, 0644)
|
err = os.WriteFile(fullpath, b, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return newImageFromPath(sum)
|
return newImageFromPath(fullpath)
|
||||||
}
|
}
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -7,6 +7,8 @@ require (
|
|||||||
github.com/diamondburned/gotk4/pkg v0.3.1
|
github.com/diamondburned/gotk4/pkg v0.3.1
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030
|
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030
|
||||||
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
||||||
|
github.com/kr/pretty v0.1.0
|
||||||
github.com/sqweek/dialog v0.0.0-20260123140253-64c163d53aac
|
github.com/sqweek/dialog v0.0.0-20260123140253-64c163d53aac
|
||||||
gosrc.io/xmpp v0.5.1
|
gosrc.io/xmpp v0.5.1
|
||||||
mellium.im/xmpp v0.22.0
|
mellium.im/xmpp v0.22.0
|
||||||
@@ -15,6 +17,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/KarpelesLab/weak v0.1.1 // indirect
|
github.com/KarpelesLab/weak v0.1.1 // indirect
|
||||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect
|
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect
|
||||||
|
github.com/kr/text v0.1.0 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||||
golang.org/x/net v0.29.0 // indirect
|
golang.org/x/net v0.29.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -38,11 +38,15 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
|||||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030 h1:NFCJG3BerP/5ZLXwu08x9xDs+9p7AYFMeo5IXjGANxw=
|
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030 h1:NFCJG3BerP/5ZLXwu08x9xDs+9p7AYFMeo5IXjGANxw=
|
||||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030/go.mod h1:+YdGDBjXJho3QTsEntqzdm0YaiALOsz3sL6b67QLC8M=
|
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030/go.mod h1:+YdGDBjXJho3QTsEntqzdm0YaiALOsz3sL6b67QLC8M=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f h1:dKccXx7xA56UNqOcFIbuqFjAWPVtP688j5QMgmo6OHU=
|
||||||
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0=
|
||||||
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
|
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
"github.com/sqweek/dialog"
|
"github.com/sqweek/dialog"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
Jid "mellium.im/xmpp/jid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
||||||
@@ -31,6 +33,19 @@ func createTab(jid string, isMuc bool) {
|
|||||||
func switchToTab(jid string) {
|
func switchToTab(jid string) {
|
||||||
current = jid
|
current = jid
|
||||||
scroller.SetChild(tabs[current].msgs)
|
scroller.SetChild(tabs[current].msgs)
|
||||||
|
m, _ := mucmembers.Load(jid)
|
||||||
|
ma := m.(mucUnit)
|
||||||
|
mm := ma.Members
|
||||||
|
gen := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
|
||||||
|
mm.Range(func(k, v any) bool {
|
||||||
|
u := v.(stanza.Presence)
|
||||||
|
gen.Append(gtk.NewLabel(Jid.MustParse(u.From).Resourcepart()))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
memberList.SetChild(gen)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func showErrorDialog(err error) {
|
func showErrorDialog(err error) {
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
// mainBox.Append(media)
|
// mainBox.Append(media)
|
||||||
// media.AddCSSClass("chat_image")
|
// media.AddCSSClass("chat_image")
|
||||||
mbtn := gtk.NewButtonWithLabel("🖼️")
|
mbtn := gtk.NewButtonWithLabel("🖼️")
|
||||||
|
// mbtn.SetChild(newImageFromWeb(oob.URL))
|
||||||
mbtn.ConnectClicked(func(){
|
mbtn.ConnectClicked(func(){
|
||||||
gopen.Open(oob.URL)
|
gopen.Open(oob.URL)
|
||||||
})
|
})
|
||||||
|
|||||||
66
main.go
66
main.go
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -18,7 +19,7 @@ import (
|
|||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"github.com/kr/pretty"
|
||||||
)
|
)
|
||||||
|
|
||||||
var loadedConfig lambdaConfig
|
var loadedConfig lambdaConfig
|
||||||
@@ -32,6 +33,7 @@ var tabs map[string]*chatTab = make(map[string]*chatTab)
|
|||||||
var current string
|
var current string
|
||||||
|
|
||||||
var scroller *gtk.ScrolledWindow
|
var scroller *gtk.ScrolledWindow
|
||||||
|
var memberList *gtk.ScrolledWindow
|
||||||
|
|
||||||
//go:embed style.css
|
//go:embed style.css
|
||||||
var styleCSS string
|
var styleCSS string
|
||||||
@@ -40,6 +42,9 @@ var clientroot *xmpp.Client
|
|||||||
|
|
||||||
var uiQueue = make(chan func(), 100)
|
var uiQueue = make(chan func(), 100)
|
||||||
|
|
||||||
|
// stores members of mucs
|
||||||
|
var mucmembers sync.Map
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
go func() {
|
go func() {
|
||||||
for fn := range uiQueue {
|
for fn := range uiQueue {
|
||||||
@@ -69,7 +74,7 @@ func main() {
|
|||||||
Jid: loadedConfig.Username,
|
Jid: loadedConfig.Username,
|
||||||
Credential: xmpp.Password(loadedConfig.Password),
|
Credential: xmpp.Password(loadedConfig.Password),
|
||||||
Insecure: loadedConfig.Insecure,
|
Insecure: loadedConfig.Insecure,
|
||||||
StreamLogger: os.Stdout,
|
// StreamLogger: os.Stdout,
|
||||||
}
|
}
|
||||||
router := xmpp.NewRouter()
|
router := xmpp.NewRouter()
|
||||||
|
|
||||||
@@ -159,6 +164,52 @@ func main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.HandleFunc("presence", func(s xmpp.Sender, p stanza.Packet) {
|
||||||
|
presence, ok := p.(stanza.Presence)
|
||||||
|
pretty.Println(presence)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var mu MucUser
|
||||||
|
var ocu OccupantID
|
||||||
|
|
||||||
|
ok = presence.Get(&mu)
|
||||||
|
|
||||||
|
if ok { // This is a presence stanza from a user in a MUC
|
||||||
|
presence.Get(&ocu)
|
||||||
|
muc := jid.MustParse(presence.From).Bare().String()
|
||||||
|
_, ok = mucmembers.Load(muc)
|
||||||
|
if !ok {
|
||||||
|
mucmembers.Store(muc, mucUnit{})
|
||||||
|
}
|
||||||
|
|
||||||
|
unit, ok := mucmembers.Load(muc)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
typed_unit := unit.(mucUnit)
|
||||||
|
/*
|
||||||
|
if typed_unit.Members == nil {
|
||||||
|
typed_unit.Members = make(map[string]stanza.Presence)
|
||||||
|
mucmembers.Store(muc, typed_unit)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if presence.Type != "unavailable" {
|
||||||
|
typed_unit.Members.Store(ocu.ID, presence)
|
||||||
|
} else {
|
||||||
|
typed_unit.Members.Delete(ocu.ID)
|
||||||
|
// delete(typed_unit.Members, ocu.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
mucmembers.Store(muc, typed_unit)
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
c, err := xmpp.NewClient(&config, router, func(err error) {
|
c, err := xmpp.NewClient(&config, router, func(err error) {
|
||||||
showErrorDialog(err)
|
showErrorDialog(err)
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -236,6 +287,10 @@ func activate(app *gtk.Application) {
|
|||||||
empty_dialog.SetVExpand(true)
|
empty_dialog.SetVExpand(true)
|
||||||
|
|
||||||
scroller = gtk.NewScrolledWindow()
|
scroller = gtk.NewScrolledWindow()
|
||||||
|
memberList = gtk.NewScrolledWindow()
|
||||||
|
|
||||||
|
scroller.SetHExpand(true)
|
||||||
|
memberList.SetHExpand(true)
|
||||||
|
|
||||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
// scroller.SetChild(empty_dialog)
|
// scroller.SetChild(empty_dialog)
|
||||||
@@ -243,7 +298,11 @@ func activate(app *gtk.Application) {
|
|||||||
menu_scroll := gtk.NewScrolledWindow()
|
menu_scroll := gtk.NewScrolledWindow()
|
||||||
menu_scroll.SetChild(menu)
|
menu_scroll.SetChild(menu)
|
||||||
box.Append(menu_scroll)
|
box.Append(menu_scroll)
|
||||||
box.Append(scroller)
|
|
||||||
|
chatbox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
chatbox.Append(scroller)
|
||||||
|
chatbox.Append(memberList)
|
||||||
|
box.Append(chatbox)
|
||||||
|
|
||||||
entry_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
entry_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
@@ -281,7 +340,6 @@ func activate(app *gtk.Application) {
|
|||||||
debug_btn := gtk.NewButtonWithLabel("Join muc")
|
debug_btn := gtk.NewButtonWithLabel("Join muc")
|
||||||
|
|
||||||
debug_btn.ConnectClicked(func() {
|
debug_btn.ConnectClicked(func() {
|
||||||
showErrorDialog(errors.New("test error"))
|
|
||||||
t := en.Text()
|
t := en.Text()
|
||||||
_, ok := tabs[t]
|
_, ok := tabs[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
9
types.go
9
types.go
@@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,3 +17,10 @@ type lambdaConfig struct {
|
|||||||
Insecure bool
|
Insecure bool
|
||||||
Nick string
|
Nick string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type mucUnit struct {
|
||||||
|
// key: OccupantID
|
||||||
|
// value: last user presence
|
||||||
|
Members sync.Map
|
||||||
|
}
|
||||||
|
|||||||
27
xmpp-vcard.go
Normal file
27
xmpp-vcard.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Implementation of XEP-0054
|
||||||
|
// https://xmpp.org/extensions/xep-0054.html
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VCard struct {
|
||||||
|
XMLName xml.Name `xml:"vcard-temp vCard"`
|
||||||
|
Photo Photo `xml:"PHOTO"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *VCard) Namespace() string {
|
||||||
|
return v.XMLName.Space
|
||||||
|
}
|
||||||
|
|
||||||
|
type Photo struct {
|
||||||
|
Type string `xml:"TYPE"`
|
||||||
|
Binval string `xml:"BINVAL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{Space: "vcard-temp", Local: "vCard"}, VCard{})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user