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.
|
||||
|
||||
import (
|
||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"github.com/kirsle/configdir"
|
||||
)
|
||||
|
||||
// global or app-level map/cache
|
||||
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 {
|
||||
if tex, exists := textureCache[path]; exists {
|
||||
@@ -43,6 +53,7 @@ func newImageFromPath(path string) *gtk.Image {
|
||||
}
|
||||
|
||||
func newPictureFromWeb(url string) *gtk.Picture {
|
||||
pa, _ := ensureCache()
|
||||
// step 1: get a sha256 sum of the URL
|
||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||
|
||||
@@ -62,16 +73,19 @@ func newPictureFromWeb(url string) *gtk.Picture {
|
||||
return nil
|
||||
}
|
||||
|
||||
fullpath := filepath.Join(pa, sum)
|
||||
|
||||
// step 3: save it
|
||||
err = os.WriteFile(sum, b, 0644)
|
||||
err = os.WriteFile(fullpath, b, 0644)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return newPictureFromPath(sum)
|
||||
return newPictureFromPath(fullpath)
|
||||
}
|
||||
|
||||
func newImageFromWeb(url string) *gtk.Image {
|
||||
pa, _ := ensureCache()
|
||||
// step 1: get a sha256 sum of the URL
|
||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||
|
||||
@@ -91,11 +105,14 @@ func newImageFromWeb(url string) *gtk.Image {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
fullpath := filepath.Join(pa, sum)
|
||||
|
||||
// step 3: save it
|
||||
err = os.WriteFile(sum, b, 0644)
|
||||
err = os.WriteFile(fullpath, b, 0644)
|
||||
if err != 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/google/uuid v1.1.1
|
||||
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
|
||||
gosrc.io/xmpp v0.5.1
|
||||
mellium.im/xmpp v0.22.0
|
||||
@@ -15,6 +17,7 @@ require (
|
||||
require (
|
||||
github.com/KarpelesLab/weak v0.1.1 // 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
|
||||
golang.org/x/net v0.29.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/go.mod h1:+YdGDBjXJho3QTsEntqzdm0YaiALOsz3sL6b67QLC8M=
|
||||
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/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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
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-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
"github.com/sqweek/dialog"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
Jid "mellium.im/xmpp/jid"
|
||||
)
|
||||
|
||||
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
||||
@@ -31,6 +33,19 @@ func createTab(jid string, isMuc bool) {
|
||||
func switchToTab(jid string) {
|
||||
current = jid
|
||||
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) {
|
||||
|
||||
@@ -97,6 +97,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
// mainBox.Append(media)
|
||||
// media.AddCSSClass("chat_image")
|
||||
mbtn := gtk.NewButtonWithLabel("🖼️")
|
||||
// mbtn.SetChild(newImageFromWeb(oob.URL))
|
||||
mbtn.ConnectClicked(func(){
|
||||
gopen.Open(oob.URL)
|
||||
})
|
||||
|
||||
66
main.go
66
main.go
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
@@ -18,7 +19,7 @@ import (
|
||||
|
||||
_ "embed"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"github.com/kr/pretty"
|
||||
)
|
||||
|
||||
var loadedConfig lambdaConfig
|
||||
@@ -32,6 +33,7 @@ var tabs map[string]*chatTab = make(map[string]*chatTab)
|
||||
var current string
|
||||
|
||||
var scroller *gtk.ScrolledWindow
|
||||
var memberList *gtk.ScrolledWindow
|
||||
|
||||
//go:embed style.css
|
||||
var styleCSS string
|
||||
@@ -40,6 +42,9 @@ var clientroot *xmpp.Client
|
||||
|
||||
var uiQueue = make(chan func(), 100)
|
||||
|
||||
// stores members of mucs
|
||||
var mucmembers sync.Map
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
for fn := range uiQueue {
|
||||
@@ -69,7 +74,7 @@ func main() {
|
||||
Jid: loadedConfig.Username,
|
||||
Credential: xmpp.Password(loadedConfig.Password),
|
||||
Insecure: loadedConfig.Insecure,
|
||||
StreamLogger: os.Stdout,
|
||||
// StreamLogger: os.Stdout,
|
||||
}
|
||||
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) {
|
||||
showErrorDialog(err)
|
||||
panic(err)
|
||||
@@ -236,6 +287,10 @@ func activate(app *gtk.Application) {
|
||||
empty_dialog.SetVExpand(true)
|
||||
|
||||
scroller = gtk.NewScrolledWindow()
|
||||
memberList = gtk.NewScrolledWindow()
|
||||
|
||||
scroller.SetHExpand(true)
|
||||
memberList.SetHExpand(true)
|
||||
|
||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
// scroller.SetChild(empty_dialog)
|
||||
@@ -243,7 +298,11 @@ func activate(app *gtk.Application) {
|
||||
menu_scroll := gtk.NewScrolledWindow()
|
||||
menu_scroll.SetChild(menu)
|
||||
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)
|
||||
|
||||
@@ -281,7 +340,6 @@ func activate(app *gtk.Application) {
|
||||
debug_btn := gtk.NewButtonWithLabel("Join muc")
|
||||
|
||||
debug_btn.ConnectClicked(func() {
|
||||
showErrorDialog(errors.New("test error"))
|
||||
t := en.Text()
|
||||
_, ok := tabs[t]
|
||||
if !ok {
|
||||
|
||||
9
types.go
9
types.go
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
"sync"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
)
|
||||
|
||||
@@ -17,3 +17,10 @@ type lambdaConfig struct {
|
||||
Insecure bool
|
||||
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