somehow sunglocto returned

This commit is contained in:
2026-04-26 10:40:13 +01:00
parent 69994d9856
commit fc0ed5ac2c
13 changed files with 407 additions and 68 deletions
+131
View File
@@ -72,6 +72,9 @@ var disconnectBytes []byte
//go:embed assets/chart_bar.png
var barBytes []byte
//go:embed assets/chart_bar_laggy.png
var barLaggyBytes []byte
//go:embed assets/ok.png
var okBytes []byte
@@ -90,6 +93,47 @@ var informationBytes []byte
//go:embed assets/car.png
var carBytes []byte
//go:embed assets/car_high.png
var carHighBytes []byte
// muc icons
//go:embed assets/muc_open.png
var mucOpenBytes []byte
//go:embed assets/muc_membersonly.png
var mucMembersOnlyBytes []byte
//go:embed assets/muc_passwordprotected.png
var mucPasswordProtectedBytes []byte
//go:embed assets/muc_unsecured.png
var mucUnsecuredBytes []byte
//go:embed assets/muc_hidden.png
var mucHiddenBytes []byte
//go:embed assets/muc_public.png
var mucPublicBytes []byte
//go:embed assets/muc_unmoderated.png
var mucUnmoderatedBytes []byte
//go:embed assets/muc_moderated.png
var mucModeratedBytes []byte
//go:embed assets/muc_nonanonymous.png
var mucNonAnonymousBytes []byte
//go:embed assets/muc_semianonymous.png
var mucSemiAnonymousBytes []byte
//go:embed assets/muc_persistent.png
var mucPersistentBytes []byte
//go:embed assets/muc_temporary.png
var mucTemporaryBytes []byte
func init() {
loader := gdkpixbuf.NewPixbufLoader()
@@ -212,6 +256,13 @@ func init() {
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(barLaggyBytes)
loader.Close()
clientAssets["chart_bar_laggy"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(okBytes)
loader.Close()
@@ -286,4 +337,84 @@ func init() {
loader.Close()
clientAssets["car"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(carHighBytes)
loader.Close()
clientAssets["car_high"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucOpenBytes)
loader.Close()
clientAssets["muc_open"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucMembersOnlyBytes)
loader.Close()
clientAssets["muc_membersonly"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucPasswordProtectedBytes)
loader.Close()
clientAssets["muc_passwordprotected"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucUnsecuredBytes)
loader.Close()
clientAssets["muc_unsecured"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucHiddenBytes)
loader.Close()
clientAssets["muc_hidden"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucPublicBytes)
loader.Close()
clientAssets["muc_public"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucUnmoderatedBytes)
loader.Close()
clientAssets["muc_unmoderated"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucModeratedBytes)
loader.Close()
clientAssets["muc_moderated"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucNonAnonymousBytes)
loader.Close()
clientAssets["muc_nonanonymous"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucSemiAnonymousBytes)
loader.Close()
clientAssets["muc_semianonymous"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucPersistentBytes)
loader.Close()
clientAssets["muc_persistent"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
loader = gdkpixbuf.NewPixbufLoader()
loader.Write(mucTemporaryBytes)
loader.Close()
clientAssets["muc_temporary"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

+22 -3
View File
@@ -27,7 +27,7 @@ func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
})
}
func createTab(jid string, isMuc bool) bool {
func createTab(jid string, isMuc bool, name string) bool {
fmt.Println("Creating tab", jid, "isMuc:", isMuc)
_, ok := tabs.Load(jid)
_, uok := userdevices.Load(jid)
@@ -38,6 +38,7 @@ func createTab(jid string, isMuc bool) bool {
newTab.msgs = gtk.NewListBox()
newTab.msgs.SetVExpand(true)
newTab.msgs.SetShowSeparators(true)
newTab.name = name
newTab.msgs.Append(gtk.NewButtonWithLabel("Get past messages..."))
tabs.Store(jid, newTab)
@@ -58,7 +59,6 @@ func switchToTab(jid string, w *gtk.Window) {
scroller.SetChild(typed_tab.msgs)
typingStatus.SetText("")
if typed_tab.isMuc {
m, ok := mucmembers.Load(jid)
if !ok {
return
@@ -476,6 +476,9 @@ func switchToTab(jid string, w *gtk.Window) {
muci := getAvatar(jid, jid)
muci.SetPixelSize(80)
gen.Prepend(muci)
muc_name := gtk.NewLabel(typed_tab.name)
muc_name.AddCSSClass("author")
gen.Prepend(muc_name)
memberList.SetChild(gen)
} else {
memberList.SetChild(gtk.NewLabel(jid))
@@ -484,7 +487,23 @@ func switchToTab(jid string, w *gtk.Window) {
}
func showErrorDialog(err error) {
fmt.Println(err.Error())
err_win := gtk.NewWindow()
err_win.SetTitle("Error")
err_win.SetDefaultSize(400, 200)
err_win.SetResizable(false)
box := gtk.NewBox(gtk.OrientationVertical, 0)
err_label := gtk.NewLabel(err.Error())
err_label.SetSelectable(true)
box.Append(err_label)
close_btn := gtk.NewButtonWithLabel("Close")
close_btn.ConnectClicked(func() {
err_win.SetVisible(false)
})
box.Append(close_btn)
err_win.SetChild(box)
err_win.Present()
}
func createIdenticon(word string) *gtk.Image { // This function generates an identicon
+24
View File
@@ -248,6 +248,30 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
mainBox.Append(subjectlabel)
}
link_preview := LinkPreview{}
ok = m.Get(&link_preview)
if ok {
lp_box := gtk.NewBox(gtk.OrientationVertical, 10)
lp_box.AddCSSClass("link_preview")
lp_title := gtk.NewLabel(link_preview.Title)
lp_title.SetSelectable(true)
lp_title.SetWrap(true)
lp_title.SetHAlign(gtk.AlignFill)
lp_desc := gtk.NewLabel(link_preview.URL + "\n" + link_preview.Description)
lp_desc.SetSelectable(true)
lp_desc.SetWrap(true)
lp_desc.SetHAlign(gtk.AlignFill)
lp_box.Append(lp_title)
lp_box.Append(lp_desc)
warning := gtk.NewLabel("⚠️")
warning.SetTooltipText("This link preview was generated by the client sending it and may not be accurate of the actual website content")
lp_box.Append(warning)
mainBox.Append(lp_box)
}
return mainBox
}
+158 -37
View File
@@ -78,6 +78,8 @@ var pingTimes = [][]float64{}
var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
var xmlLog *os.File
func init() {
beeep.AppName = "Lambda"
@@ -94,13 +96,6 @@ func init() {
}
func main() {
// Setup log
xmlLog, err := os.CreateTemp("", "xmpp-log")
if err != nil {
panic(err)
}
defer os.Remove(xmlLog.Name())
pingTimes = append(pingTimes, []float64{})
p, err := ensureConfig()
@@ -112,7 +107,6 @@ func main() {
if err != nil {
dropToSignInPage(err)
return
// panic(err)
}
_, err = toml.Decode(string(b), &loadedConfig)
@@ -125,6 +119,17 @@ func main() {
loadedConfig.Resource = randomClientResource()
}
if !loadedConfig.Debug {
xmlLog, err = os.CreateTemp("", "xmpp-log")
if err != nil {
panic(err)
}
defer os.Remove(xmlLog.Name())
} else {
xmlLog = os.Stdout
}
config := xmpp.Config{
TransportConfiguration: xmpp.TransportConfiguration{
Address: loadedConfig.Server,
@@ -393,7 +398,7 @@ func main() {
_, ok := userdevices.Load(user)
_, mok := mucmembers.Load(user)
if !ok && !mok { // FIXME: The initial muc presence gets picked up from this check
ok := createTab(user, false)
ok := createTab(user, false, user)
if ok {
userdevices.Store(user, userUnit{})
@@ -484,10 +489,18 @@ func main() {
newsize = stat.Size()
diff := float64(newsize-oldsize) / 1000
if diff > 100 {
sIcon.SetFromPaintable(clientAssets["car_high"])
} else {
sIcon.SetFromPaintable(clientAssets["car"])
}
sStatus.SetText(fmt.Sprintf("%.2fKB/s", diff))
oldsize = stat.Size()
}
}()
connectionStatus.SetText(fmt.Sprintf("Connected as %s", JidMustParse(clientroot.Session.BindJid).Bare()))
connectionStatus.SetTooltipText(fmt.Sprintf("Binded JID: %s\nUsing TLS: %t", clientroot.Session.BindJid, clientroot.Session.TlsEnabled))
connectionIcon.SetFromPaintable(clientAssets["connect"])
@@ -514,15 +527,24 @@ func main() {
jid := item.Id
node := item.Any
autojoin := false
name := ""
for _, attr := range node.Attrs {
if attr.Name.Local == "autojoin" {
autojoin = attr.Value == "true"
break
}
}
for _, attr := range node.Attrs {
if attr.Name.Local == "name" {
name = attr.Value
break
}
}
_, ok := tabs.Load(jid)
if !ok && autojoin {
createTab(jid, true)
createTab(jid, true, name)
b := gtk.NewLabel(jid)
gesture1 := gtk.NewGestureClick()
gesture1.SetButton(1)
@@ -544,17 +566,19 @@ func main() {
for _, attr := range node.Attrs {
if attr.Name.Local == "autojoin" {
autojoin = attr.Value == "true"
break
}
}
for _, node := range node.Nodes {
if node.XMLName.Local == "nick" {
nick = node.Content
break
}
}
if autojoin {
err := joinMuc(client, clientroot.Session.BindJid, jid, nick)
err := joinMuc(client, clientroot.Session.BindJid, jid, nick, "")
if err != nil {
panic(err)
}
@@ -707,9 +731,11 @@ func activate(app *gtk.Application) {
box := gtk.NewBox(gtk.OrientationVertical, 0)
jid_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
nick_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
disco_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
jid_entry := gtk.NewEntry()
nick_entry := gtk.NewEntry()
disco_check := gtk.NewCheckButton()
jid_entry.SetHAlign(gtk.AlignEnd)
jid_entry.SetHExpand(true)
@@ -725,8 +751,14 @@ func activate(app *gtk.Application) {
nick_box.Append(gtk.NewLabel("Nick:"))
nick_box.Append(nick_entry)
disco_check.SetActive(true)
disco_box.Append(gtk.NewLabel("Check MUC features before joining"))
disco_box.Append(disco_check)
disco_box.SetTooltipText("If you are creating a MUC through this window then turn this off")
box.Append(jid_box)
box.Append(nick_box)
box.Append(disco_box)
btn := gtk.NewButtonWithLabel("Submit")
btn.SetVAlign(gtk.AlignBaseline)
@@ -742,14 +774,14 @@ func activate(app *gtk.Application) {
btn.ConnectClicked(func() {
t := jid_entry.Text()
_, ok := tabs.Load(t)
jm := func() {
err := joinMuc(client, clientroot.Session.BindJid, t, nick_entry.Text())
jm := func(n string, pw string) {
err := joinMuc(client, clientroot.Session.BindJid, t, nick_entry.Text(), pw)
if err != nil {
panic(err)
showErrorDialog(err)
return
}
createTab(t, true)
createTab(t, true, n)
b := gtk.NewLabel(t)
gesture1 := gtk.NewGestureClick()
gesture1.SetButton(1)
@@ -761,7 +793,14 @@ func activate(app *gtk.Application) {
menu.Append(b)
}
if !ok {
// First check the MUC's disco and see if it's semianon
if !disco_check.Active() {
jm(t, "")
win.SetVisible(false)
return
}
var res *stanza.DiscoInfo
allowed := true
fmt.Println("Attempting to get Disco info")
@@ -783,35 +822,44 @@ func activate(app *gtk.Application) {
mychan, err := client.SendIQ(ctx, myIQ)
if err == nil {
result := <-mychan
res, ok := result.Payload.(*stanza.DiscoInfo)
res, ok = result.Payload.(*stanza.DiscoInfo)
if ok {
semianon := false
features := res.Features
for _, feature := range features {
if feature.Var == "muc_nonanonymous" {
semianon = false
break
} else if feature.Var == "muc_semianonymous" {
semianon = true
break
}
}
if !semianon {
allowed = false
password_protected := false
password := ""
warning_win := gtk.NewWindow()
warning_win.SetTitle("Warning")
warning_win.SetTitle(fmt.Sprintf("Joining %s", res.Identity[0].Name))
warning_win.SetDefaultSize(400, 400)
warning_win.SetResizable(false)
warning_box := gtk.NewBox(gtk.OrientationVertical, 0)
warning_label := gtk.NewLabel("This muc is not semi-anonymous. Your JID will be revealed to non-moderators if you join. Continue?")
buttons := gtk.NewBox(gtk.OrientationHorizontal, 0)
join_button := gtk.NewButtonWithLabel("Join")
join_button.ConnectClicked(func() {
warning_win.SetVisible(false)
jm()
if password_protected {
allowed = false
password_win := gtk.NewWindow()
password_win.SetTitle("Password required")
password_win.SetDefaultSize(400, 1)
password_win.SetResizable(false)
box := gtk.NewBox(gtk.OrientationVertical, 0)
en := gtk.NewEntry()
en.SetPlaceholderText("Password")
submit := gtk.NewButtonWithLabel("Submit")
submit.ConnectClicked(func() {
password = en.Text()
jm(res.Identity[0].Name, password)
password_win.SetVisible(false)
})
box.Append(en)
box.Append(submit)
password_win.SetChild(box)
password_win.SetVisible(true)
}
jm(res.Identity[0].Name, password)
})
cancel_button := gtk.NewButtonWithLabel("Cancel")
@@ -821,17 +869,90 @@ func activate(app *gtk.Application) {
buttons.Append(join_button)
buttons.Append(cancel_button)
warning_box := gtk.NewBox(gtk.OrientationVertical, 0)
header := gtk.NewLabel(res.Identity[0].Name)
warning_box.Append(header)
for _, feature := range features {
switch feature.Var {
case "muc_passwordprotected":
password_protected = true
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_passwordprotected"]))
box.Append(gtk.NewLabel("This MUC is password-protected"))
warning_box.Append(box)
case "muc_unsecured":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_unsecured"]))
box.Append(gtk.NewLabel("This MUC does not require a password"))
warning_box.Append(box)
case "muc_membersonly":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_membersonly"]))
box.Append(gtk.NewLabel("Only members can join this MUC"))
warning_box.Append(box)
case "muc_open":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_open"]))
box.Append(gtk.NewLabel("Anyone can join this MUC"))
warning_box.Append(box)
case "muc_moderated":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_moderated"]))
box.Append(gtk.NewLabel("Only members can speak in this MUC"))
warning_box.Append(box)
case "muc_unmoderated":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_unmoderated"]))
box.Append(gtk.NewLabel("Anyone can speak in this MUC"))
warning_box.Append(box)
case "muc_nonanonymous":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_nonanonymous"]))
box.Append(gtk.NewLabel("This MUC is non-anonymous, your JID will be visible to other users"))
warning_box.Append(box)
case "muc_semianonymous":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_semianonymous"]))
box.Append(gtk.NewLabel("This MUC is semi-anonymous, only moderators will see your full JID"))
warning_box.Append(box)
case "muc_persistent":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_persistent"]))
box.Append(gtk.NewLabel("This MUC is persistent, it will not be deleted when the last user leaves"))
warning_box.Append(box)
case "muc_temporary":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_temporary"]))
box.Append(gtk.NewLabel("This MUC is temporary, it will be deleted when the last user leaves"))
warning_box.Append(box)
case "muc_public":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_public"]))
box.Append(gtk.NewLabel("This MUC can be found in directories and search engines"))
warning_box.Append(box)
case "muc_hidden":
box := gtk.NewBox(gtk.OrientationHorizontal, 0)
box.Append(gtk.NewImageFromPaintable(clientAssets["muc_hidden"]))
box.Append(gtk.NewLabel("This MUC is hidden and cannot be found in directories or search engines"))
warning_box.Append(box)
}
}
warning_box.Append(warning_label)
warning_box.Append(buttons)
warning_win.SetChild(warning_box)
warning_win.Present()
} else {
allowed = false
if result.Error != nil {
showErrorDialog(fmt.Errorf("Failed to get disco info: %s - %s", result.Error.Reason, result.Error.Text))
} else {
showErrorDialog(fmt.Errorf("Failed to get disco info"))
}
}
}
if allowed {
jm()
jm(res.Identity[0].Name, "")
}
}
win.SetVisible(false)
+7
View File
@@ -73,3 +73,10 @@
.None_CVD {
}
.link_preview {
color: white;
background-color: grey;
border-radius: 5px;
padding: 5px;
}
+2
View File
@@ -9,6 +9,7 @@ import (
type chatTab struct {
isMuc bool
msgs *gtk.ListBox
name string
}
type lambdaConfig struct {
@@ -21,6 +22,7 @@ type lambdaConfig struct {
JoinBookmarks bool
CVD color.CVD
Identicons bool
Debug bool
}
type mucUnit struct {
+1 -1
View File
@@ -1,3 +1,3 @@
package main
var lambda_version string = "26w15a"
var lambda_version string = "26w17a"
+17 -2
View File
@@ -28,10 +28,12 @@ func sendMessage(c xmpp.Sender, sendTo string, msgType stanza.StanzaType, body s
}
// Joins a MUC
func joinMuc(c xmpp.Sender, jid string, muc string, nick string) error {
func joinMuc(c xmpp.Sender, jid string, muc string, nick string, password string) error {
var joinPresence stanza.Presence
addr := muc + "/" + nick
fmt.Println(addr)
joinPresence := stanza.Presence{
if password == "" {
joinPresence = stanza.Presence{
Attrs: stanza.Attrs{
From: jid,
To: addr,
@@ -40,6 +42,19 @@ func joinMuc(c xmpp.Sender, jid string, muc string, nick string) error {
&stanza.MucPresence{},
},
}
} else {
joinPresence = stanza.Presence{
Attrs: stanza.Attrs{
From: jid,
To: addr,
},
Extensions: []stanza.PresExtension{
&stanza.MucPresence{
Password: password,
},
},
}
}
err := client.Send(joinPresence)
if err != nil {
+20
View File
@@ -0,0 +1,20 @@
package main
import (
"encoding/xml"
"gosrc.io/xmpp/stanza"
)
type LinkPreview struct {
stanza.MsgExtension
XMLName xml.Name `xml:"http://www.w3.org/1999/02/22-rdf-syntax-ns# Description"`
About string `xml:"https://ogp.me/ns#,attr"`
Title string `xml:"https://ogp.me/ns# title"`
Description string `xml:"https://ogp.me/ns# description"`
Image string `xml:"https://ogp.me/ns# image"`
URL string `xml:"https://ogp.me/ns# url"`
}
func init() {
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", Local: "Description"}, LinkPreview{})
}