diff --git a/assets.go b/assets.go index bcb8214..918bee5 100644 --- a/assets.go +++ b/assets.go @@ -226,7 +226,7 @@ func init() { "fail": failBytes, "please_wait": pleaseWaitBytes, "update": updateBytes, - "solar": solarBytes, + "solar": solarBytes, } { loadAsset(key, data) } diff --git a/go-xmpp b/go-xmpp new file mode 160000 index 0000000..bc2b7ce --- /dev/null +++ b/go-xmpp @@ -0,0 +1 @@ +Subproject commit bc2b7ceee4b4d4c9ba5ec2451afdd43462f0b188 diff --git a/go.mod b/go.mod index 822a4a0..150f5fb 100644 --- a/go.mod +++ b/go.mod @@ -49,3 +49,5 @@ require ( mellium.im/xmlstream v0.15.4 // indirect nhooyr.io/websocket v1.8.17 // indirect ) + +replace gosrc.io/xmpp => ./go-xmpp/ diff --git a/gtk-helpers.go b/gtk-helpers.go index 3518b96..f663fe2 100644 --- a/gtk-helpers.go +++ b/gtk-helpers.go @@ -23,6 +23,201 @@ import ( func init() { } +func showUserWindow(u stanza.Presence, custom_nick string) { + var ocu OccupantID + u.Get(&ocu) + + var id string + id = JidMustParse(u.From).Resource + + win := gtk.NewWindow() + win.SetDefaultSize(400, 400) + win.SetResizable(false) + profile_box := gtk.NewBox(gtk.OrientationVertical, 0) + nick := gtk.NewLabel(JidMustParse(u.From).Resource) + + if custom_nick != "" { + nick.SetText(custom_nick) + } + + ver_text := gtk.NewLabel(loadedLocale["gettingVersion"]) + ver_text.AddCSSClass("visitor") + + win.SetTitle(JidMustParse(u.From).Resource) + nick.AddCSSClass("author") + nick.SetSelectable(true) + profile_box.Append(nick) + if u.Status != "" { + status_message := gtk.NewLabel(u.Status) + status_message.AddCSSClass("status") + profile_box.Append(status_message) + } + profile_box.Append(ver_text) + fr := gtk.NewLabel(u.From) + fr.AddCSSClass("jid") + fr.SetSelectable(true) + profile_box.Append(fr) + profile_box.Append(ver_text) + + iqResp, err := stanza.NewIQ(stanza.Attrs{ + Type: "get", + From: clientroot.Session.BindJid, + To: u.From, + Id: "vc2", + Lang: "en", + }) + + if err != nil { + panic(err) + } + + iqResp.Payload = &stanza.Version{} + loading_version_text := gtk.NewLabel("...") + + var hats Hats + ok := u.Get(&hats) + if ok { + for _, hat := range hats.Hats { + l := gtk.NewLabel(hat.Title) + l.AddCSSClass("hat") + profile_box.Append(l) + } + } + + var mu MucUser + ok = u.Get(&mu) + if ok { + if mu.MucUserItem.JID != "" { + ji := (gtk.NewLabel(mu.MucUserItem.JID)) + ji.AddCSSClass("jid") + ji.SetSelectable(true) + profile_box.Append(ji) + } + profile_box.Append(gtk.NewLabel(loadedLocale["connectedWithRole"] + mu.MucUserItem.Role)) + var affil string + if mu.MucUserItem.Affiliation == "none" { + affil = loadedLocale["unaffiliated"] + } else { + affil = loadedLocale["affiliatedAs"] + mu.MucUserItem.Affiliation + } + profile_box.Append(gtk.NewLabel(affil)) + } + + if ocu.ID != "" { + ocu_label := gtk.NewLabel(ocu.ID) + ocu_label.AddCSSClass("jid") + ocu_label.SetSelectable(true) + profile_box.Append(ocu_label) + } + + go func() { + + myIQ, err := stanza.NewIQ(stanza.Attrs{ + Type: "get", + From: clientroot.Session.BindJid, + To: u.From, + Id: "dicks", + Lang: "en", + }) + + if err != nil { + panic(err) + } + + myIQ.Payload = &stanza.DiscoInfo{} + + ctx := context.TODO() + mychan, err := client.SendIQ(ctx, myIQ) + if err == nil { + result := <-mychan + res, ok := result.Payload.(*stanza.DiscoInfo) + if ok { + idents := res.Identity + for i, ident := range idents { + profile_box.Append(gtk.NewLabel(fmt.Sprintf("%d: Name: %s, Category: %s, Type: %s", i+1, ident.Name, ident.Category, ident.Type))) + } + + /* + s := fmt.Sprintf("%v", res.Features) + h := sha1.New() + h.Write([]byte(s)) + sha1_hash := hex.EncodeToString(h.Sum(nil)) + */ + + sw := gtk.NewScrolledWindow() + s := "" + for _, feature := range res.Features { + s = s + feature.Var + "\n" + } + sw.SetChild(gtk.NewLabel(s)) + + profile_box.Append(sw) + } + } + }() + + go func() { + ctx := context.TODO() + mychan, err := client.SendIQ(ctx, iqResp) + if err == nil { + result := <-mychan + ver, ok := result.Payload.(*stanza.Version) + if ok { + loading_version_text.SetVisible(false) + name := ver.Name + version := ver.Version + os := ver.OS + + vr := fmt.Sprintf("%s %s %s", name, version, os) + if name == "" && version == "" && os == "" { + ver_text.SetText(loadedLocale["versionQueryEmpty"]) + } else { + ver_text.SetText(vr) + ver_text.RemoveCSSClass("visitor") + } + } else if result.Error != nil && result.Error.Type != "" { + ver_text.SetText(loadedLocale["versionQueryError"]) + ver_text.SetTooltipText(result.Error.Reason + ": " + result.Error.Text) + ver_text.RemoveCSSClass("visitor") + ver_text.AddCSSClass("error") + } + } + }() + + go func() { + mo, _ := mucmembers.Load(JidMustParse(u.From).Bare()) + mm := mo.(mucUnit) + mmm := mm.Members + mmmm, ok := mmm.Load(id) + if ok { + pres := mmmm.(stanza.Presence) + + var vu VCardUpdate + pres.Get(&vu) + if vu.Photo != "" { + im := getAvatar(u.From, vu.Photo) + im.SetPixelSize(80) + im.AddCSSClass("author_img") + profile_box.Prepend(im) + } else { + im := createIdenticon(u.From, false) + im.SetPixelSize(80) + im.AddCSSClass("author_img") + profile_box.Prepend(im) + } + } else { + im := createIdenticon(u.From, false) + im.SetPixelSize(80) + im.AddCSSClass("author_img") + profile_box.Prepend(im) + } + }() + + win.SetChild(profile_box) + win.SetTransientFor(win) + win.Present() +} + func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) { glib.IdleAdd(func() bool { vAdj := scrolledWindow.VAdjustment() @@ -147,10 +342,9 @@ func switchToTab(jid string, w *gtk.Window) { //id := ocu.ID //if id == "" { - id := JidMustParse(u.From).Resource + // id := JidMustParse(u.From).Resource //} - nick_label := gtk.NewLabel(JidMustParse(u.From).Resource) var custom_nick string @@ -166,15 +360,13 @@ func switchToTab(jid string, w *gtk.Window) { ocu_nick, ok := loadedConfig.CustomNicks[ocu.ID] if ok { - custom_nick = ocu_nick + custom_nick = ocu_nick } if custom_nick != "" { nick_label.SetText(custom_nick) } - - nick_label.SetEllipsize(pango.EllipsizeEnd) nick_label.AddCSSClass(mu.MucUserItem.Role) if mu.MucUserItem.Role == "visitor" { @@ -332,7 +524,7 @@ func switchToTab(jid string, w *gtk.Window) { `, clientroot.Session.BindJid, jid, the_entry.Text(), JidMustParse(mu.MucUserItem.JID).Bare())) - win.SetVisible(false) + win.Destroy() }) box.Append(the_entry) @@ -399,191 +591,7 @@ func switchToTab(jid string, w *gtk.Window) { }) gesture.Connect("pressed", func(n_press, x, y int) { - win := gtk.NewWindow() - win.SetDefaultSize(400, 400) - win.SetResizable(false) - profile_box := gtk.NewBox(gtk.OrientationVertical, 0) - nick := gtk.NewLabel(JidMustParse(u.From).Resource) - if custom_nick != "" { - nick.SetText(custom_nick) - } - - ver_text := gtk.NewLabel(loadedLocale["gettingVersion"]) - ver_text.AddCSSClass("visitor") - - win.SetTitle(JidMustParse(u.From).Resource) - nick.AddCSSClass("author") - nick.SetSelectable(true) - profile_box.Append(nick) - if u.Status != "" { - status_message := gtk.NewLabel(u.Status) - status_message.AddCSSClass("status") - profile_box.Append(status_message) - } - profile_box.Append(ver_text) - fr := gtk.NewLabel(u.From) - fr.AddCSSClass("jid") - fr.SetSelectable(true) - profile_box.Append(fr) - profile_box.Append(ver_text) - - iqResp, err := stanza.NewIQ(stanza.Attrs{ - Type: "get", - From: clientroot.Session.BindJid, - To: u.From, - Id: "vc2", - Lang: "en", - }) - - if err != nil { - panic(err) - } - - iqResp.Payload = &stanza.Version{} - loading_version_text := gtk.NewLabel("...") - - var hats Hats - ok := u.Get(&hats) - if ok { - for _, hat := range hats.Hats { - l := gtk.NewLabel(hat.Title) - l.AddCSSClass("hat") - profile_box.Append(l) - } - } - - var mu MucUser - ok = u.Get(&mu) - if ok { - if mu.MucUserItem.JID != "" { - ji := (gtk.NewLabel(mu.MucUserItem.JID)) - ji.AddCSSClass("jid") - ji.SetSelectable(true) - profile_box.Append(ji) - } - profile_box.Append(gtk.NewLabel(loadedLocale["connectedWithRole"] + mu.MucUserItem.Role)) - var affil string - if mu.MucUserItem.Affiliation == "none" { - affil = loadedLocale["unaffiliated"] - } else { - affil = loadedLocale["affiliatedAs"] + mu.MucUserItem.Affiliation - } - profile_box.Append(gtk.NewLabel(affil)) - } - - if ocu.ID != "" { - ocu_label := gtk.NewLabel(ocu.ID) - ocu_label.AddCSSClass("jid") - ocu_label.SetSelectable(true) - profile_box.Append(ocu_label) - } - - go func() { - - myIQ, err := stanza.NewIQ(stanza.Attrs{ - Type: "get", - From: clientroot.Session.BindJid, - To: u.From, - Id: "dicks", - Lang: "en", - }) - - if err != nil { - panic(err) - } - - myIQ.Payload = &stanza.DiscoInfo{} - - ctx := context.TODO() - mychan, err := client.SendIQ(ctx, myIQ) - if err == nil { - result := <-mychan - res, ok := result.Payload.(*stanza.DiscoInfo) - if ok { - idents := res.Identity - for i, ident := range idents { - profile_box.Append(gtk.NewLabel(fmt.Sprintf("%d: Name: %s, Category: %s, Type: %s", i+1, ident.Name, ident.Category, ident.Type))) - } - - /* - s := fmt.Sprintf("%v", res.Features) - h := sha1.New() - h.Write([]byte(s)) - sha1_hash := hex.EncodeToString(h.Sum(nil)) - */ - - sw := gtk.NewScrolledWindow() - s := "" - for _, feature := range res.Features { - s = s + feature.Var + "\n" - } - sw.SetChild(gtk.NewLabel(s)) - - profile_box.Append(sw) - } - } - }() - - go func() { - ctx := context.TODO() - mychan, err := client.SendIQ(ctx, iqResp) - if err == nil { - result := <-mychan - ver, ok := result.Payload.(*stanza.Version) - if ok { - loading_version_text.SetVisible(false) - name := ver.Name - version := ver.Version - os := ver.OS - - vr := fmt.Sprintf("%s %s %s", name, version, os) - if name == "" && version == "" && os == "" { - ver_text.SetText(loadedLocale["versionQueryEmpty"]) - } else { - ver_text.SetText(vr) - ver_text.RemoveCSSClass("visitor") - } - } else if result.Error != nil && result.Error.Type != "" { - ver_text.SetText(loadedLocale["versionQueryError"]) - ver_text.SetTooltipText(result.Error.Reason + ": " + result.Error.Text) - ver_text.RemoveCSSClass("visitor") - ver_text.AddCSSClass("error") - } - } - }() - - go func() { - mo, _ := mucmembers.Load(JidMustParse(u.From).Bare()) - mm := mo.(mucUnit) - mmm := mm.Members - mmmm, ok := mmm.Load(id) - if ok { - pres := mmmm.(stanza.Presence) - - var vu VCardUpdate - pres.Get(&vu) - if vu.Photo != "" { - im := getAvatar(u.From, vu.Photo) - im.SetPixelSize(80) - im.AddCSSClass("author_img") - profile_box.Prepend(im) - } else { - im := createIdenticon(u.From, false) - im.SetPixelSize(80) - im.AddCSSClass("author_img") - profile_box.Prepend(im) - } - } else { - im := createIdenticon(u.From, false) - im.SetPixelSize(80) - im.AddCSSClass("author_img") - profile_box.Prepend(im) - } - }() - - win.SetChild(profile_box) - win.SetTransientFor(win) - win.Present() + showUserWindow(u, custom_nick) }) userbox.AddController(gesture) @@ -625,7 +633,6 @@ func switchToTab(jid string, w *gtk.Window) { } else { memberList.SetChild(gtk.NewLabel(jid)) } - } func showErrorDialog(err error, w *gtk.Window) { @@ -641,7 +648,7 @@ func showErrorDialog(err error, w *gtk.Window) { close_btn := gtk.NewButtonWithLabel(loadedLocale["close"]) close_btn.ConnectClicked(func() { - err_win.SetVisible(false) + err_win.Destroy() }) box.Append(close_btn) err_win.SetChild(box) @@ -737,7 +744,7 @@ func jidBuilder(en *gtk.Entry) { // This function spawns a window that allows th jid := localPart + at + domain + slash + resource en.SetText(jid) - win.SetVisible(false) + win.Destroy() }) box.Append(submit) diff --git a/gtk-message.go b/gtk-message.go index 9e82b85..ca51807 100644 --- a/gtk-message.go +++ b/gtk-message.go @@ -110,6 +110,37 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { sid := StanzaID{} m.Get(&sid) + ocu := OccupantID{} + + m.Get(&ocu) + id := JidMustParse(m.From).Resource + + name := id + custom_nick, ok := loadedConfig.CustomNicks[ocu.ID] + if ok { + name = custom_nick + } + + // Avatar hash custom nicks. This code needs to be optimised. + mo, ok := mucmembers.Load(JidMustParse(m.From).Bare()) + if ok { + mm, ok := mo.(mucUnit) + if ok { + mmm, ok := mm.Members.Load(id) + if ok { + u := mmm.(stanza.Presence) + var vcu VCardUpdate + ok = u.Get(&vcu) + if ok { + av_nick, ok := loadedConfig.CustomNicksAV[vcu.Photo] + if ok { + name = av_nick + } + } + } + } + } + padding := 10 orientation := gtk.OrientationVertical if loadedConfig.CompactMode { @@ -177,6 +208,23 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { }) rc_box.Append(quote) + vp := gtk.NewButtonWithLabel("View profile") + vp.ConnectClicked(func() { + mo, ok := mucmembers.Load(JidMustParse(m.From).Bare()) + if ok { + mm, ok := mo.(mucUnit) + if ok { + mmm, ok := mm.Members.Load(id) + if ok { + u := mmm.(stanza.Presence) + showUserWindow(u, name) + } + } + } + }) + + rc_box.Append(vp) + popover.SetChild(rc_box) rect := gdk.NewRectangle(x, y, 1, 1) popover.SetPointingTo(&rect) @@ -185,50 +233,18 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter { mainBox.AddController(gesture) - ocu := OccupantID{} - m.Get(&ocu) - id := JidMustParse(m.From).Resource - name := id - - - // Avatar hash custom nicks. This code needs to be optimised. - mo, ok := mucmembers.Load(JidMustParse(m.From).Bare()) - if ok { - mm, ok := mo.(mucUnit) - if ok { - mmm, ok := mm.Members.Load(id) - if ok { - u := mmm.(stanza.Presence) - var vcu VCardUpdate - ok = u.Get(&vcu) - if ok { - av_nick, ok := loadedConfig.CustomNicksAV[vcu.Photo] - if ok { - name = av_nick - } - } - } - } - } - - - custom_nick, ok := loadedConfig.CustomNicks[ocu.ID] - if ok { - name = custom_nick - } - if name == "" { name = JidMustParse(m.From).Bare() } if loadedConfig.CompactMode { - al := gtk.NewLabel(id) + al := gtk.NewLabel(name) ycbcr := xmpp_color.String(id, 255, loadedConfig.CVD) r, g, b, _ := ycbcr.RGBA() hexcolor := fmt.Sprintf("#%02x%02x%02x", r, g, b) - al.SetMarkup(fmt.Sprintf("%s", hexcolor, id)) + al.SetMarkup(fmt.Sprintf("%s", hexcolor, name)) al.SetSelectable(true) al.SetVAlign(gtk.AlignStart) mainBox.Append(al) diff --git a/main.go b/main.go index 49d1f6e..5a4a530 100644 --- a/main.go +++ b/main.go @@ -6,9 +6,9 @@ import ( "sync" "context" + "crypto/tls" "fmt" "path/filepath" - "crypto/tls" "github.com/diamondburned/gotk4/pkg/gdk/v4" "github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2" @@ -139,7 +139,7 @@ func main() { return charset.NewReaderLabel(c, input) }, ConnectTimeout: 300, - TLSConfig: &tls.Config{InsecureSkipVerify: loadedConfig.Insecure}, + TLSConfig: &tls.Config{InsecureSkipVerify: loadedConfig.Insecure}, }, Jid: loadedConfig.Username + "/" + loadedConfig.Resource, Credential: xmpp.Password(loadedConfig.Password), @@ -588,6 +588,7 @@ func main() { if err == nil { res, ok := result.Payload.(*stanza.PubSubGeneric) if ok { + fmt.Printf("%d rooms in bookmarks\n", len(res.Items.List)) for _, item := range res.Items.List { jid := item.Id node := item.Any @@ -1263,11 +1264,11 @@ func activate(app *gtk.Application) { if JidMustParse(mui.MucUserItem.JID).Bare() == JidMustParse(clientroot.Session.BindJid).Bare() { if mui.MucUserItem.Role == "visitor" { win := gtk.NewWindow() - win.SetDefaultSize(1,1) + win.SetDefaultSize(1, 1) box := gtk.NewBox(gtk.OrientationVertical, 5) txt := gtk.NewLabel("You are a visitor and do not have voice.\nWould you like to request voice from the moderators of this MUC?") btn := gtk.NewButtonWithLabel("Request voice") - btn.ConnectClicked(func (){ + btn.ConnectClicked(func() { sendVoiceRequest(client, current) win.SetVisible(false) }) @@ -1276,7 +1277,6 @@ func activate(app *gtk.Application) { win.SetVisible(false) }) - box.Append(txt) box.Append(btn) box.Append(dismiss) @@ -1319,8 +1319,6 @@ func activate(app *gtk.Application) { exts = append(exts, new_attention) } - - err := sendMessage(client, current, message_type, t, "", "", exts) if err != nil { showErrorDialog(err, &window.Window) diff --git a/types.go b/types.go index 22049de..135f18d 100644 --- a/types.go +++ b/types.go @@ -30,7 +30,7 @@ type lambdaConfig struct { CompactMode bool ShowAvatarsInMemberList bool CustomNicks map[string]string // Anyone with a specific Occupant ID will have this nick - CustomNicksAV map[string]string // Anyone with a specific avatar hash will have this nick + CustomNicksAV map[string]string // Anyone with a specific avatar hash will have this nick CheckUpdate string } diff --git a/version.go b/version.go index c2226d9..ffb439b 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package main -var lambda_version string = "3" +var lambda_version string = "4" diff --git a/xmpp-helpers.go b/xmpp-helpers.go index a6db8c6..375a267 100644 --- a/xmpp-helpers.go +++ b/xmpp-helpers.go @@ -1,9 +1,9 @@ package main import ( + "fmt" "gosrc.io/xmpp" "gosrc.io/xmpp/stanza" - "fmt" ) // This file has small functions that can be used to do XMPP stuff without writing tons of boilerplate @@ -140,4 +140,3 @@ func sendVoiceRequest(c xmpp.Sender, sendTo string) error { } return nil } -