From 34b885a7701e311139a98163bad3641ee6e01fbd Mon Sep 17 00:00:00 2001 From: sunglocto Date: Sun, 11 Oct 2026 12:08:38 +0100 Subject: [PATCH] Add rendering forms to messages, add configuring mucs, add getting muc affiliations, add a bunch of other stuff idk man check the diff --- assets.go | 10 +- debug_christmas.png | Bin 0 -> 4560 bytes debug_halloween.png | Bin 0 -> 4624 bytes go-xmpp/stanza/form.go | 5 + go-xmpp/stanza/packet.go | 2 +- go.mod | 24 +++- go.sum | 92 +++++++++++++-- gtk-helpers.go | 227 +++++++++++++++++++++++++++++++++++- gtk-message.go | 97 +++++++++++----- gtk-signin.go | 2 +- gtk-update.go | 2 +- main.go | 245 ++++++++++++++++++++++++++++++++------- style.css | 5 + types.go | 9 +- version.go | 2 +- xmpp-helpers.go | 14 ++- xmpp-mucadmin.go | 35 ++++++ xmpp-mucowner.go | 27 +++++ xmpp-mucuser.go | 9 +- 19 files changed, 706 insertions(+), 101 deletions(-) create mode 100644 debug_christmas.png create mode 100644 debug_halloween.png create mode 100644 xmpp-mucadmin.go create mode 100644 xmpp-mucowner.go diff --git a/assets.go b/assets.go index 7cecf77..7557e1e 100644 --- a/assets.go +++ b/assets.go @@ -208,6 +208,9 @@ var yaximCapBytes []byte //go:embed assets/client_icons/psi.png var psiCapBytes []byte +//go:embed assets/client_icons/spark.png +var sparkCapBytes []byte + var clientAssets sync.Map func loadAsset(key string, data []byte) { @@ -296,9 +299,10 @@ func init() { "https://conversejs.org": converseJSCapBytes, "http://pidgin.im/": pidginCapBytes, "https://dev.narayana.im/narayana/Conversations-Classic": anotherImCapBytes, - "https://movim.eu/": movimCapBytes, - "https://yaxim.org/": yaximCapBytes, - "https://psi-im.org": psiCapBytes, + "https://movim.eu/": movimCapBytes, + "https://yaxim.org/": yaximCapBytes, + "https://psi-im.org": psiCapBytes, + "https://igniterealtime.org/projects/smack": sparkCapBytes, } { loadAsset(key, data) } diff --git a/debug_christmas.png b/debug_christmas.png new file mode 100644 index 0000000000000000000000000000000000000000..04ff8aa46b398c3647168260bbdf9532539abf18 GIT binary patch literal 4560 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 R85kHCJYD@<);T3K0RR*%#ozz{ literal 0 HcmV?d00001 diff --git a/debug_halloween.png b/debug_halloween.png new file mode 100644 index 0000000000000000000000000000000000000000..acc97bded7e523c255218bfc5c0b17d6196afec0 GIT binary patch literal 4624 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hEFfMe ztF&%s^@J>4rOe?Ob2U`#$Zv2{wZ7)?19)4enViE5J-$zvcFxhV?oHOOt*bpff1IiK zb~9c5c+y6%wWXN?ENRS#416?~3jL^k+bSOS;@kCGyGpYggifWiJWgZij1SQLziXXh z#2pJksU=$(e$M{Y=-|otw%Io7UaqXwjoZTiSL}HFrPWjXo65y&9eR@ky>h-gPLVrv zd79sH|HXBZKAH7DpHBCG_F&c1;I=};gwN-$wad+2`!6}Nca~g*OTSrXf9C%U)83|E zD*6~y?SFG;y!>nddpYsLi+%J@JN)DPpqJJj>vFTZ=)uzIaaJnx1)rQ{c=tzm^OgE_ zE4qEkncZ(!%6rUHHkx_uQ}g4@mlnI;KYs9cZu?f&lV_3{RxRx6?hZBas-2{?XBLO& z3#aYz4o3gVZ*R+${iNx9kLSm|+v5LgFVA^VEI3v14(}dOex0{%oNMA_IG*n3`+DSg zoPGU{OsD$eEu6w_{U4tq1l^>6a*Q?DJa{2Uf%d7SK z{;svp?pdeb)uVFHqiJfJ;L(yD7oYt#ykNfO5x2JLJRddFt_2!K8JCuL8rA+Pxq9c0 zj<2ups!f}M4hXOwYnjl&@=W{q2i4Ql^{4;+{at>dKjWvQ1J~#OKWE&n=KfE^d4s#n zyv9vDfirhi?B9In(+q*;$2V#%*Lp3^TCgxg)nSLC-0sy2xUv!w4j3%mw{KogKte~G z*9!0HdYm~&NiQ3|kdvlYke&i;XuV24vetCVk=4isL zWzXl=|Kl!dpAfDh!Ruy}b${OXClCJp4YE(|s(Gm$KXKBeOM-tr-`(7t{&em9{d%q& zl_LMYn7(A+!h>GzLPz-Le%4l&Js0-lfiY8=qm`A_l4Z->Y(JkduBxbrkhQG}aW*~C zk}0uY^6ozUa)!#42Oi)5bFS^C=)oGHZ>P)eO_R_2-58UVJvCU}|G`Z)?Xb0S7xznt z{rMfeU;VkArMCFFxs&<7y`4HW``U??M?%7ya&NeVeMKkswFOBU920Ea?e}Py|coASE3S^1azuMZ1&s^0(lUG?E^AgW9AjgYaJp{6IWKXl2uJ13hV^x?cT0FDE?gImbq-ppOf=XP2c}(i{hL6|DMiIeLpYW^Xe0=ndSbQ6)P$%G`E(1-BkEF z{=}S}pWie^mBspbcV1%tdNlDGTZL#`{xrm78AGMZhd9>>=exp3UMcuz0gf4)(v(Zhr7a%>MvKlP?> zpHaS0QAe0pO!>@vxu8<@s>8qZ!aOh;jeY*`4IBm4g%rf;BkY<^%<8^|U z<^s;cc9RO7;0v`vDb7$q&w{H z&N+9n)~os3l7_MaHZKJoEF>o!+43}?xiX01i7F%euFKC?c~9Bm)0?JbfAYu=Kdv-$ z)05`g|MdFI|H+#C(R~i@7v4wJZB91})u!BvkeDAl$H?tM$;Lw?e|m8C@yJZi z#&hPkFMV45ZAysSrH|~ZCb!R4=R3&37U{IHInqgX$Neb+|IH;G4|f@wOnIVoH({m- zL*+__%aM*Nwr%5W`M=}yn-9D9=WORFEVpS1e%M=Ob)~Js?En3vQ}*1QHmOTNG(Ot0 z;>Wy6E_WhpvcJ~8e!qv2)BNqbH3_N*B#)hW>7=RG#StSq<^GxK31?rveROe$iqF6D z6^ljF*Q+gIDJ*)tBx&-88~5vcubG5N9WzAp*iO2N87z0!sVML2-tAZB`Ff#TM`}spS7)De&O^b)1&WJwShBzH zJUw@J&!VlrjJWK5@Y}W zc--H-GI+U^isafr|F`QTYq!7ivUI8~EjVEKD7EUP*xSj+{1oq)KHebqxkK@~+vkR( zOAV}+IILK``ggreU(!d3GSvo;Qy(86KbzR}oCUwr~Ua0^4nN&=7c2Vx52k-cF^B(VdE9L+Ejq4F%f1ALrukx*| zd5lS>cVy=!74BRpHQ{&k=Coe5Lwg=Pm^e3iL-eh-bu&-yanN`k`_1Cb5xJKJadTFD zzgOL#S){40effp-1k1g9_r@li&@*`b^?F>j?!@lbZxzGDbJt|t zbeJ!)%f44(^G=6bB1(tnZds{oI<2+baKANY&>c-O!l4J7_rBnnGx zeEwGW&BrO z*x|!@FefeXwc-1uJF0Wqm+zRvA6sdd@c3A7@rDoOfpeMs$^{qRx)t^HMsh!Ahxji0 zXYX!Kw?1ZZV5g2zMi! z=}sx{?(A&KoWiok@ny2dnUp6?1`j!A6g(~4vhwzpmuF|2Pj~kD&-nGJz-5d4^ku%Y zm(8pB-K8M`cO2Jje5Nv|HgBy>G|&pN5ZJsP~mX=eSn?24) zOg7Kdu`GIWqJ8_K{1CPa^WWXt>h1hR{NeNXNzD-^o`NkmUbAwGX~abeN=6w=zm!xy zad#o(Y};zHW>w`yQv{4oN!pxAUc%TRwfJPE*Rpe9g98P1V0-x^8CoY@TD|lw?o6Sj|b28 z6gocGBKbP0E_}yrl{Rbl!t{XT@`&Kt7pGR-zLWUQq|xQ_j)xAeoR9C$Ph@z@^_=nH zt<=6h85hqBH@%z{_vozo{VTi6-<#=)1Yb|MC;$G&#^l*O?o1p7mBkx3$2)KB=yZSg zY**=PkA^)en^wJKB=PTyuRl)_mwu>)XrfKjzTK>q8S$K&? z(C24ogE#z<3cJOh?|$Utx&ZfBc_}k5hPBq658VYsGQGI>a&o`EQoQY>`PJ@Zi)$-c zpFg>5?|!3Sdz0Ig#~sRrs^^Z(HHnCFYki?BVc?`s=F2_6PAd1s7CHB`$lYwf0wr_)N>2(sQfVr|VnzC^%a?x;DBo3A?TDXt;6%`=_JkdRs`9>1v>7KizBt2U zo0w5+-aF%F?e^Dw3EGBimfI?K=LwXSZoBAv@yFSj#_H1S8~oloa&E9>{3u~@=(Y$1DcL%WuA?E5wt^K;+M@9b{2+PX39-x`Doi* z`J72^p75*J#?vj=2p3AT?@;jwJF<5-!++5lfUlh(sr&g@getIl?Wu;+5wD92r zOMWI^4!@$gOL=>-okh!yW36v58#K3kuy~_qJmcJ=|LH281(Q1n)+phaYs?%mqEp>YtR_m$Lx!onb`)9+cF72xg`{tSNVG+u| zd}p7dwBols-uS0Q2hXeg?7S9za0btXc|9B7_3-b1b>@kZ?}O!c`hJ#vD|Xp@(vxTD z0p272tbwL~AN~^QI+wcQch~{X#^~fVrEm8-ehZeKFC)p%+_7Tr9mQlP|JgrxzBNB& zt}Ap_=W)z(k#F7CFzveV>l%k2#P%=HG0oBNKbT+R}9 zloCJov$Y|Z(Y~zUh~c^KGpo3j%cCDeJbHB~hx^Z!vMUSnbwn3jl6<5YWRd^6_V+i< z=Zj=~E&cbeJ6^H;ljQ52Hw*hy=N?i1bYk)(S zr^8P_cI|5qSvKkUwyM`pmLA{vx=^zE6psUU|E#r3GOuhsHY@e=GGA%Ki(>N%3=dAI z;bYt}`A5=}>D%+~?@Qidmt4iz@z=j_!-9Ku9rJr4H2gDv)-c@ln`F&D`vaffdVAT` zk5>pi_`g`_9P8$Uy^@};FF2nRGnjFB1-~hOcjqVP{ob$V!;@DXNdJ33YQOIeQC-%^ pt@qV`9NvHKR4iY)Epr|7l!t+DgSlpvGB7YOc)I$ztaD0e0syy(@N)nF literal 0 HcmV?d00001 diff --git a/go-xmpp/stanza/form.go b/go-xmpp/stanza/form.go index f758f74..7c69218 100644 --- a/go-xmpp/stanza/form.go +++ b/go-xmpp/stanza/form.go @@ -14,6 +14,7 @@ const ( // See XEP-0004 and XEP-0068 // Pointer semantics type Form struct { + MsgExtension XMLName xml.Name `xml:"jabber:x:data x"` Instructions []string `xml:"instructions"` Title string `xml:"title,omitempty"` @@ -66,3 +67,7 @@ type Option struct { Label string `xml:"label,attr,omitempty"` ValuesList []string `xml:"value"` } + +func init() { + TypeRegistry.MapExtension(PKTMessage, xml.Name{Space: "jabber:x:data", Local: "x"}, Form{}) +} diff --git a/go-xmpp/stanza/packet.go b/go-xmpp/stanza/packet.go index 567796d..0fd1011 100644 --- a/go-xmpp/stanza/packet.go +++ b/go-xmpp/stanza/packet.go @@ -10,7 +10,7 @@ type Attrs struct { Id string `xml:"id,attr,omitempty"` From string `xml:"from,attr,omitempty"` To string `xml:"to,attr,omitempty"` - Lang string `xml:"lang,attr,omitempty"` + Lang string `xml:"xml:lang,attr,omitempty"` } type packetFormatter interface { diff --git a/go.mod b/go.mod index 4b40381..1816472 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module lambda-im -go 1.25.5 +go 1.26.0 require ( github.com/BurntSushi/toml v1.6.0 @@ -16,16 +16,28 @@ require ( github.com/rrivera/identicon v0.0.0-20240116195454-d5ba35832c0d github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef + github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c gosrc.io/xmpp v0.5.1 mellium.im/xmpp v0.23.0 ) require ( + codeberg.org/go-pdf/fpdf v0.12.0 // indirect git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect + github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 // indirect + github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 // indirect + github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect + github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 // indirect + github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f // indirect + github.com/andybalholm/brotli v1.2.2 // indirect + github.com/benoitkugler/textlayout v0.3.2 // indirect + github.com/benoitkugler/textprocessing v0.0.6 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/esiqveland/notify v0.14.0 // indirect github.com/go-analyze/bulk v0.1.5 // indirect + github.com/go-fonts/latin-modern v0.3.3 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-text/typesetting v0.3.4 // indirect github.com/godbus/dbus/v5 v5.2.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/jackmordaunt/icns/v3 v3.0.1 // indirect @@ -34,16 +46,24 @@ require ( github.com/rogpeppe/go-internal v1.16.0 // indirect github.com/sergeymakinen/go-bmp v1.0.0 // indirect github.com/sergeymakinen/go-ico v1.0.0 // indirect + github.com/srwiley/scanx v0.0.0-20190309010443-e94503791388 // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect + github.com/tdewolff/font v0.0.0-20260822205238-d0d2f004b117 // indirect + github.com/tdewolff/minify/v2 v2.24.17 // indirect + github.com/tdewolff/parse/v2 v2.8.16 // indirect + github.com/yuin/goldmark v1.8.5 // indirect golang.org/x/image v0.45.0 // indirect - golang.org/x/net v0.57.0 // indirect + golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect mellium.im/reader v0.1.0 // indirect mellium.im/xmlstream v0.15.4 // indirect + modernc.org/knuth v0.6.0 // indirect + modernc.org/token v1.1.0 // indirect nhooyr.io/websocket v1.8.17 // indirect + star-tex.org/x/tex v0.7.1 // indirect ) replace gosrc.io/xmpp => ./go-xmpp/ diff --git a/go.sum b/go.sum index bf21154..fa13b66 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,43 @@ +codeberg.org/go-fonts/liberation v0.6.0 h1:15Gh6SdwYve22CWCm9jYpVpRuaTh726av2TgHTHvAtQ= +codeberg.org/go-fonts/liberation v0.6.0/go.mod h1:J15VAa+lyxdcI/Je7lDDDl6QOhLk9feNBnnwXqEHXOk= +codeberg.org/go-latex/latex v0.3.0 h1:LKTaDHFbEC2PH1sh0sYv6PZ1pzs/g2aoeV1HItWj/bg= +codeberg.org/go-latex/latex v0.3.0/go.mod h1:8ETijTpK2bFtwRAXLXe1RZJrYxnc5pibibZfQBj+Lk4= +codeberg.org/go-pdf/fpdf v0.12.0 h1:g8E/1VqGqB2lZUUaqQrrTnA0IEJLPTTX1DZ0qS/ZmhU= +codeberg.org/go-pdf/fpdf v0.12.0/go.mod h1:WJNJ2bvCj81rZBdhOf7lKOGoSl+OKMXcIcXqDcP8r5Y= git.sr.ht/~jackmordaunt/go-toast v1.1.2 h1:/yrfI55LRt1M7H1vkaw+NaH1+L1CDxrqDltwm5euVuE= git.sr.ht/~jackmordaunt/go-toast v1.1.2/go.mod h1:jA4OqHKTQ4AFBdwrSnwnskUIIS3HYzlJSgdzCKqfavo= +git.sr.ht/~sbinet/cmpimg v0.1.0 h1:E0zPRk2muWuCqSKSVZIWsgtU9pjsw3eKHi8VmQeScxo= +git.sr.ht/~sbinet/cmpimg v0.1.0/go.mod h1:FU12psLbF4TfNXkKH2ZZQ29crIqoiqTZmeQ7dkp/pxE= +git.sr.ht/~sbinet/gg v0.8.0 h1:PjQ4AgUWRz7Dy6PVBKMLLg96eRCos1U+H6F3FJuKWHo= +git.sr.ht/~sbinet/gg v0.8.0/go.mod h1:XhlGCvSXts+BaZ1XMZLxLSFUDvwsJ/2El5F6XzPep6o= +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA= +github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 h1:lTG4HQym5oPKjL7nGs+csTgiDna685ZXjxijkne828g= +github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc h1:7D+Bh06CRPCJO3gr2F7h1sriovOZ8BMhca2Rg85c2nk= +github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046 h1:O/r2Sj+8QcMF7V5IcmiE2sMFV2q3J47BEirxbXJAdzA= +github.com/BurntSushi/xgbutil v0.0.0-20190907113008-ad855c713046/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k= +github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f h1:l7moT9o/v/9acCWA64Yz/HDLqjcRTvc0noQACi4MsJw= +github.com/ByteArena/poly2tri-go v0.0.0-20170716161910-d102ad91854f/go.mod h1:vIOkSdX3NDCPwgu8FIuTat2zDF0FPXXQ0RYFRy+oQic= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/andybalholm/brotli v1.2.2 h1:HzTuoo2ErYQqf5qvcJInB8uvqSVxRttzkFexPWtnceM= +github.com/andybalholm/brotli v1.2.2/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE= +github.com/benoitkugler/textlayout v0.3.2 h1:Y/LTkwpQ9G0Fia9yhPmZA9IR5AnE8Cq30j3C+Gx5/IE= +github.com/benoitkugler/textlayout v0.3.2/go.mod h1:o+1hFV+JSHBC9qNLIuwVoLedERU7sBPgEFcuSgfvi/w= +github.com/benoitkugler/textlayout-testdata v0.1.1/go.mod h1:i/qZl09BbUOtd7Bu/W1CAubRwTWrEXWq6JwMkw8wYxo= +github.com/benoitkugler/textprocessing v0.0.6 h1:obkMyj62GEPg3xUVYqROlCN22z1OleuZm6ULqX9Om1g= +github.com/benoitkugler/textprocessing v0.0.6/go.mod h1:Io0gN08/PXEzrSOWFa88xHx2Xv3VjvLMY7H76YoI23A= github.com/boxes-ltd/imaging v1.7.5 h1:k4kYxJEhysoGhEEN1IEeKoSbnG8/8snjj7M48Ok0fnk= github.com/boxes-ltd/imaging v1.7.5/go.mod h1:+8H+oRvis3InOFtTpcoCCB1RDXqo6p9tQBtjZfWnrC8= github.com/crazy3lf/colorconv v1.2.0 h1:UM7kSZWnwFMGiC+PpYrjxQSOd6sEyWb+dRKKTd3KslA= github.com/crazy3lf/colorconv v1.2.0/go.mod h1:2jTJ7QCWCj2sSLOhF4Gzi0J5/hoX8/VY8VzNvXAlD1I= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/diamondburned/gotk4/pkg v0.4.1 h1:w4BwVUUBkKyNLz5HKHN2Oz3kZCTLHfpfKgB/su1p6uo= github.com/diamondburned/gotk4/pkg v0.4.1/go.mod h1:BAEEDXtj5srQ1S2++5HX2K1s9c6CtuWSa60dAywrkRM= @@ -22,8 +51,14 @@ github.com/go-analyze/bulk v0.1.5 h1:Zj8w3gEOhEnp8aRZ7DHMDqaG3/Bp7CvQ7pu6Mw9YhFc github.com/go-analyze/bulk v0.1.5/go.mod h1:afon/KtFJYnekIyN20H/+XUvcLFjE8sKR1CfpqfClgM= github.com/go-analyze/charts v0.6.0 h1:aBE2cbdr58faYGwPk3RUzGQMyg5fGsSAxepwYTYmvhg= github.com/go-analyze/charts v0.6.0/go.mod h1:m8hqGNSpYYdMMc0Im/RCowY9CIA7705jiltuOVQZWuI= +github.com/go-fonts/latin-modern v0.3.3 h1:g2xNgI8yzdNzIVm+qvbMryB6yGPe0pSMss8QT3QwlJ0= +github.com/go-fonts/latin-modern v0.3.3/go.mod h1:tHaiWDGze4EPB0Go4cLT5M3QzRY3peya09Z/8KSCrpY= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-text/typesetting v0.3.4 h1:YYurUOtEb9kGSOz4uE3k4OpBGsp1dDL8+fjCeaFamAU= +github.com/go-text/typesetting v0.3.4/go.mod h1:4qZCQphq4KSgGTAeI0uMEkVbROgfah8BuyF5LRYr7XY= +github.com/go-text/typesetting-utils v0.0.0-20260223113751-2d88ac90dae3 h1:drBZzMgdYPbmyXqOto4YhhJGrFIQCX94FpR4MzTCsos= +github.com/go-text/typesetting-utils v0.0.0-20260223113751-2d88ac90dae3/go.mod h1:3/62I4La/HBRX9TcTpBj4eipLiwzf+vhI+7whTc9V7o= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= @@ -46,8 +81,9 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g= github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= @@ -61,38 +97,68 @@ github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiY github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q= github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ= github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE= +github.com/srwiley/scanFT v0.0.0-20220128184157-0d1ee492111f h1:uLR2GaV0kWYZ3Ns3l3sjtiN+mOWAQadvrL8HXcyKjl0= +github.com/srwiley/scanFT v0.0.0-20220128184157-0d1ee492111f/go.mod h1:LZwgIPG9X6nH6j5Ef+xMFspl6Hru4b5EJxzMfeqHYJY= +github.com/srwiley/scanx v0.0.0-20190309010443-e94503791388 h1:ZdkidVdpLW13BQ9a+/3uerT2ezy9J7KQWH18JCfhDmI= +github.com/srwiley/scanx v0.0.0-20190309010443-e94503791388/go.mod h1:C/WY5lmWfMtPFYYBTd3Lzdn4FTLr+RxlIeiBNye+/os= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= -golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= -golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c h1:eXJXpQAo8l0AdVnsjJctG38GlBnHnwHfhLOTLYxZYNE= +github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c/go.mod h1:K9Ux3cwFtyAFRITZ46g7v+ORXgI3ENtQa5rL9yjNz5Y= +github.com/tdewolff/font v0.0.0-20260822205238-d0d2f004b117 h1:5vSJjsbV9AH0U+Ex0uej8ROrpOBlOVBrw0mGlVef7oo= +github.com/tdewolff/font v0.0.0-20260822205238-d0d2f004b117/go.mod h1:ipESUihcEdgRBhIwTRHmBTOMamccMQrp4iCxfa1aFws= +github.com/tdewolff/minify/v2 v2.24.17 h1:6AbitfVyq0M7aW6i+XL7+49DeTQZwloOMs9O574arBg= +github.com/tdewolff/minify/v2 v2.24.17/go.mod h1:kVqn9vxXUKtlHexSNrWbYePqioOT5mc4ou/KVSMpfCM= +github.com/tdewolff/parse/v2 v2.8.16 h1:bLk5svUOQRkW/Y2SJ+DeENSIkZBcTIkq+Atyv5D8feI= +github.com/tdewolff/parse/v2 v2.8.16/go.mod h1:XdsoSFThlVIRIajAuqz1evNY7bagZS8LBOPA3aVopwQ= +github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk= +github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA= +github.com/yuin/goldmark v1.8.5/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= +golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= +golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.45.0 h1:FMb1nTbH5H9vF55SriQHgFw5GnNL9Jg6L25BwXKzhB0= golang.org/x/image v0.45.0/go.mod h1:n62x/7RqlwXDvGsSU4u6IUTUf6KghUZ9Bt7cG/T9Fx4= golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= -golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= -golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gonum.org/v1/plot v0.17.0 h1:d0DwPVBe9jnEGqQBoZGl/P2M9WciJbG2CnV59C9QBT4= +gonum.org/v1/plot v0.17.0/go.mod h1:ipt2GUN1oqzr2O7wCjLDtw1ShfIYYNBp4o0O1Ez5B3Y= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= mellium.im/reader v0.1.0 h1:UUEMev16gdvaxxZC7fC08j7IzuDKh310nB6BlwnxTww= mellium.im/reader v0.1.0/go.mod h1:F+X5HXpkIfJ9EE1zHQG9lM/hO946iYAmU7xjg5dsQHI= @@ -102,5 +168,13 @@ mellium.im/xmlstream v0.15.4 h1:gLKxcWl4rLMUpKgtzrTBvr4OexPeO/edYus+uK3F6ZI= mellium.im/xmlstream v0.15.4/go.mod h1:yXaCW2++fmVO4L9piKVkyLDqnCmictVYF7FDQW8prb4= mellium.im/xmpp v0.23.0 h1:rvKvOvMdIURCLaAWEJN8J0QpO3AJYCDKjxLsqtTPSjY= mellium.im/xmpp v0.23.0/go.mod h1:GHDKlKKQe0LNmD9YqExyxnFEEBiz84KGqnfiA2VNzb8= +modernc.org/knuth v0.6.0 h1:zLv0hzgdcpcCUB/iMwpQvn9m/iciqO/hyNXlpWymMKU= +modernc.org/knuth v0.6.0/go.mod h1:ISAZV+URZIlE8h1BwNVg1NzpwHKndV5lQ13NPPj3tds= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +star-tex.org/x/tex v0.7.1 h1:4qGAByRyY0WQsOjtcHlxz+FgrYxz8fzxIds2Gjepp5U= +star-tex.org/x/tex v0.7.1/go.mod h1:Y3y0U7sZTltTh/CDZIx0oAtMjG7eMaTuTtvDZGdyhJo= diff --git a/gtk-helpers.go b/gtk-helpers.go index cd54f93..337e811 100644 --- a/gtk-helpers.go +++ b/gtk-helpers.go @@ -7,7 +7,10 @@ import ( "image" "image/color" "image/png" + "slices" "strconv" + "strings" + _ "sync" "github.com/boxes-ltd/imaging" "github.com/crazy3lf/colorconv" @@ -77,7 +80,11 @@ func showUserWindow(u stanza.Presence, custom_nick string) { win.SetTitle(JidMustParse(u.From).Resource) nick.AddCSSClass("author") nick.SetSelectable(true) - profile_box.Append(nick) + + nbox := gtk.NewBox(gtk.OrientationHorizontal, 5) + nbox.SetHAlign(gtk.AlignCenter) + nbox.Append(nick) + profile_box.Append(nbox) if u.Status != "" { status_message := gtk.NewLabel(u.Status) status_message.AddCSSClass("status") @@ -128,6 +135,7 @@ func showUserWindow(u stanza.Presence, custom_nick string) { if ok { for _, hat := range hats.Hats { l := gtk.NewLabel(hat.Title) + l.SetSelectable(true) l.AddCSSClass("hat") profile_box.Append(l) } @@ -141,6 +149,11 @@ func showUserWindow(u stanza.Presence, custom_nick string) { ji.AddCSSClass("jid") ji.SetSelectable(true) profile_box.Append(ji) + if JidMustParse(mu.MucUserItem.JID).Bare() == JidMustParse(clientroot.Session.BindJid).Bare() { + you_label := gtk.NewLabel("(You)") + you_label.AddCSSClass("you") + nbox.Append(you_label) + } } profile_box.Append(gtk.NewLabel(loadedLocale["connectedWithRole"] + mu.MucUserItem.Role)) var affil string @@ -308,7 +321,7 @@ func createTab(jid string, isMuc bool, name string) bool { }) newTab.name = name newTab.msg_refsID = make(map[string]*abMessage) - newTab.msg_refsOSID = make(map[string]*abMessage) + //newTab.msg_refsOSID = make(map[string]*abMessage) tabs.Store(jid, newTab) return true } @@ -785,11 +798,215 @@ func showErrorDialog(err error, w *gtk.Window) { err_win.Present() } +func showDialog(s string, w *gtk.Window) { + err_win := gtk.NewWindow() + err_win.SetTitle("Information") + err_win.SetDefaultSize(400, 200) + err_win.SetResizable(false) + + box := gtk.NewBox(gtk.OrientationVertical, 0) + err_label := gtk.NewLabel(s) + err_label.SetSelectable(true) + box.Append(err_label) + + close_btn := gtk.NewButtonWithLabel(loadedLocale["close"]) + close_btn.ConnectClicked(func() { + err_win.Destroy() + }) + box.Append(close_btn) + err_win.SetChild(box) + err_win.SetTransientFor(w) + err_win.Present() +} + +func showFormDialog(f stanza.Form) (*stanza.Form, gtk.Window, string) { + form_type := "" + + var ret *stanza.Form = &f + + form_win := gtk.NewWindow() + form_win.SetTitle(f.Title) + form_win.SetDefaultSize(400, 400) + form_win.SetResizable(false) + + form_box := gtk.NewBox(gtk.OrientationVertical, 2) + + for _, instruction := range f.Instructions { + form_box.Append(gtk.NewLabel(instruction)) + } + + // for x, item := range f.Items { + for y, field := range f.Fields { + if field.Var == "FORM_TYPE" { + form_type = field.ValuesList[0] + } + field_box := gtk.NewBox(gtk.OrientationHorizontal, 2) + field_label := gtk.NewLabel(field.Label) + field_label.SetTooltipText(field.Description) + + btn_box := gtk.NewBox(gtk.OrientationVertical, 0) + btn_box.SetHAlign(gtk.AlignEnd) + btn_box.SetHExpand(true) + + switch field.Type { + case "boolean": + if len(field.ValuesList) == 0 { + field.ValuesList = append(field.ValuesList, "0") + f.Fields[y].ValuesList = append(f.Fields[y].ValuesList, "0") + } + + tick := gtk.NewCheckButton() + if field.ValuesList[0] == "1" { + tick.SetActive(true) + } else { + tick.SetActive(false) + } + + tick.ConnectToggled(func() { + str := "" + if tick.Active() { + str = "1" + } else { + str = "0" + } + f.Fields[y].ValuesList[0] = str + }) + btn_box.Append(tick) + case "fixed": + for _, v := range field.ValuesList { + label := gtk.NewLabel(v) + label.AddCSSClass("visitor") + btn_box.SetHAlign(gtk.AlignCenter) + btn_box.Append(label) + } + case "hidden": + // do nothing + case "jid-multi": + // TODO + case "jid-single": + e_box := gtk.NewBox(gtk.OrientationHorizontal, 0) + jid_entry := gtk.NewEntry() + jid_entry.SetText(field.ValuesList[0]) + buildAJIDButton := gtk.NewImageFromPaintable(clientAssetsLoad("jabber")) + buildAJIDButton.Connect("pressed", func() { + jidBuilder(jid_entry) + }) + + jid_entry.ConnectChanged(func() { + f.Fields[y].ValuesList[0] = jid_entry.Text() + }) + + e_box.Append(buildAJIDButton) + e_box.Append(jid_entry) + + btn_box.Append(e_box) + case "list-multi": + // TODO + case "list-single": + var selected uint + str := []string{} + for i, option := range field.Options { + if slices.Equal(option.ValuesList, field.ValuesList) { + selected = uint(i) + } + str = append(str, option.ValuesList[0]) + } + + dropdown := gtk.NewDropDownFromStrings(str) + dropdown.SetSelected(selected) + dropdown.ConnectActivate(func() { + f.Fields[y].ValuesList[0] = field.Options[dropdown.Selected()].ValuesList[0] + }) + + btn_box.Append(dropdown) + case "text-private": + p_entry := gtk.NewPasswordEntry() + p_entry.SetText(field.ValuesList[0]) + p_entry.ConnectChanged(func() { + f.Fields[y].ValuesList[0] = p_entry.Text() + }) + + btn_box.Append(p_entry) + case "text-multi": + textView := gtk.NewTextView() + textView.SetWrapMode(gtk.WrapWordChar) + txt := "" + + for i, line := range field.ValuesList { + txt = txt + line + if i != len(field.ValuesList)-1 { + txt = txt + "\n" + } + } + textView.Buffer().SetText(txt) + + textView.Buffer().ConnectChanged(func() { + txt := textView.Buffer().Text(textView.Buffer().StartIter(), textView.Buffer().EndIter(), false) + f.Fields[y].ValuesList = strings.Split(txt, "\n") + }) + + scroll := gtk.NewScrolledWindow() + scroll.SetChild(textView) + scroll.SetMinContentHeight(150) + + btn_box.Append(scroll) + + default: // includes text-single + en := gtk.NewEntry() + txt := "" + if len(field.ValuesList) != 0 { + txt = field.ValuesList[0] + } + en.SetText(txt) + en.ConnectChanged(func() { + if len(field.ValuesList) == 0 { + f.Fields[y].ValuesList = append(f.Fields[y].ValuesList, en.Text()) + } else { + f.Fields[y].ValuesList[0] = en.Text() + } + }) + + btn_box.Append(en) + } + + field_box.Append(field_label) + field_box.Append(btn_box) + form_box.Append(field_box) + } + + form_box.Append(gtk.NewSeparator(gtk.OrientationHorizontal)) + + btn_box := gtk.NewBox(gtk.OrientationHorizontal, 0) + btn_box.SetHAlign(gtk.AlignCenter) + + cancelbtn := gtk.NewButtonWithLabel("Cancel") + cancelbtn.ConnectClicked(func() { + form_win.Close() + }) + + okbtn := gtk.NewButtonWithLabel(loadedLocale["submit"]) + okbtn.ConnectClicked(func() { + ret = &f + form_win.Close() + }) + + btn_box.Append(okbtn) + btn_box.Append(cancelbtn) + + form_box.Append(btn_box) + + form_win.SetChild(form_box) + form_win.SetVisible(true) + + return ret, *form_win, form_type +} + func createIdenticon(word string, always_create bool, i *gtk.Image) { // This function generates an identicon if !loadedConfig.Identicons && !always_create { // return gtk.NewImageFromPaintable(clientAssetsLoad("DefaultAvatar")) - glib.IdleAdd(func() { - i.SetFromPaintable(clientAssetsLoad("DefaultAvatar")) + p := clientAssetsLoad("DefaultAvatar") + glib.IdleAddPriority(glib.PriorityLow, func() { + i.SetFromPaintable(p) }) return } @@ -814,7 +1031,7 @@ func createIdenticon(word string, always_create bool, i *gtk.Image) { // This fu p := loader.Pixbuf() gt := gdk.NewTextureForPixbuf(p) - glib.IdleAdd(func() { + glib.IdleAddPriority(glib.PriorityLow, func() { i.SetFromPaintable(gt) }) } diff --git a/gtk-message.go b/gtk-message.go index aa26fcd..8ff66e0 100644 --- a/gtk-message.go +++ b/gtk-message.go @@ -6,19 +6,21 @@ import ( "context" "encoding/base64" "fmt" - "github.com/diamondburned/gotk4/pkg/gdk/v4" - "github.com/diamondburned/gotk4/pkg/glib/v2" - "github.com/diamondburned/gotk4/pkg/gtk/v4" - "github.com/google/uuid" - "github.com/jasonlovesdoggo/gopen" - "gosrc.io/xmpp/stanza" - xmpp_color "mellium.im/xmpp/color" - "mellium.im/xmpp/jid" "os" "path/filepath" "runtime" "strings" "time" + + "github.com/diamondburned/gotk4/pkg/gdk/v4" + "github.com/diamondburned/gotk4/pkg/glib/v2" + "github.com/diamondburned/gotk4/pkg/gtk/v4" + "github.com/google/uuid" + "github.com/jasonlovesdoggo/gopen" + + "gosrc.io/xmpp/stanza" + xmpp_color "mellium.im/xmpp/color" + "mellium.im/xmpp/jid" ) func generatePresenceWidget(p stanza.Packet) gtk.Widgetter { @@ -102,7 +104,8 @@ func generateMessageWidget(p stanza.Packet, blocked bool, target string) (gtk.Wi msg.ocu_id = ocu.ID if sid.ID != "" { - typed_tab.msg_refsOSID[sid.ID] = msg + // typed_tab.msg_refsOSID[sid.ID] = msg + typed_tab.msg_refsOSID.Store(sid.ID, msg) } } @@ -241,6 +244,7 @@ func generateMessageWidget(p stanza.Packet, blocked bool, target string) (gtk.Wi live_edit := gtk.NewButtonWithLabel("Live edit") live_edit.ConnectClicked(func() { en := gtk.NewEntry() + en.SetText(m.Body) en.ConnectChanged(func() { client.SendRaw(fmt.Sprintf("%s", uuid.NewString(), @@ -277,7 +281,13 @@ func generateMessageWidget(p stanza.Packet, blocked bool, target string) (gtk.Wi 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) + + r = uint32((float32(r) / 65536) * 255) + g = uint32((float32(g) / 65536) * 255) + b = uint32((float32(b) / 65536) * 255) + + hexcolor := fmt.Sprintf("#%x%x%x", r, g, b) + fmt.Println(hexcolor) al.SetMarkup(fmt.Sprintf("%s", hexcolor, name)) al.SetSelectable(true) @@ -416,10 +426,19 @@ func generateMessageWidget(p stanza.Packet, blocked bool, target string) (gtk.Wi body = XEPToPango(body, false) mlabel := gtk.NewLabel("") - if m.Body == "" { + + form := new(stanza.Form) + contains_form := m.Get(form) + + if m.Body == "" && m.Subject == "" && !contains_form { return nil, false } + if m.Body == "" { + mlabel.SetText("No body set") + mlabel.AddCSSClass("visitor") + } + mlabel.SetMarkup(body) mlabel.SetWrap(true) mlabel.SetSelectable(true) @@ -469,27 +488,49 @@ func generateMessageWidget(p stanza.Packet, blocked bool, target string) (gtk.Wi mainBox.Append(lp_box) } + if contains_form { + btn := gtk.NewButtonWithLabel(fmt.Sprintf("Open attached form - %s", form.Title)) + btn.ConnectClicked(func() { + f, w, _ := showFormDialog(*form) + w.ConnectCloseRequest(func() (ok bool) { + go func() { + form_reply := stanza.NewMessage(stanza.Attrs{ + Type: m.Type, + Id: m.Id, + To: m.From, + }) + + form_reply.Extensions = []stanza.MsgExtension{f} + client.Send(form_reply) + }() + return false + }) + }) + mainBox.Append(btn) + } + } - if loadedConfig.BlockingMode == Highlight && blocked { - mainBox.AddCSSClass("blocked") - } + if blocked { + switch loadedConfig.BlockingMode { + case Highlight: + mainBox.AddCSSClass("blocked") + case Hide: + mainBox.SetVisible(false) + blocked_box := gtk.NewBox(gtk.OrientationVertical, 0) + blocked_part := gtk.NewBox(gtk.OrientationHorizontal, 0) + blocked_part.Append(gtk.NewLabel("This message is from a user you have blocked. ")) - if loadedConfig.BlockingMode == Hide && blocked { - mainBox.SetVisible(false) - blocked_box := gtk.NewBox(gtk.OrientationVertical, 0) - blocked_part := gtk.NewBox(gtk.OrientationHorizontal, 0) - blocked_part.Append(gtk.NewLabel("This message is from a user you have blocked. ")) + show_hide := gtk.NewButtonWithLabel("Toggle") + show_hide.ConnectClicked(func() { + mainBox.SetVisible(!mainBox.IsVisible()) + }) - show_hide := gtk.NewButtonWithLabel("Toggle") - show_hide.ConnectClicked(func() { - mainBox.SetVisible(!mainBox.IsVisible()) - }) - - blocked_part.Append(show_hide) - blocked_box.Append(blocked_part) - blocked_box.Append(mainBox) - return blocked_box, true + blocked_part.Append(show_hide) + blocked_box.Append(blocked_part) + blocked_box.Append(mainBox) + return blocked_box, true + } } return mainBox, true diff --git a/gtk-signin.go b/gtk-signin.go index 726b243..3bbc7b0 100644 --- a/gtk-signin.go +++ b/gtk-signin.go @@ -103,7 +103,7 @@ func dropToSignInPage(err error) { if err != nil { panic(err) } - os.WriteFile(filepath.Join(p, "lambda.toml"), b.Bytes(), 0644) + os.WriteFile(filepath.Join(p, config_filename), b.Bytes(), 0644) window.SetVisible(false) main() diff --git a/gtk-update.go b/gtk-update.go index c123648..8136ac9 100644 --- a/gtk-update.go +++ b/gtk-update.go @@ -121,7 +121,7 @@ func save(choice string) { if err != nil { panic(err) } - os.WriteFile(filepath.Join(p, "lambda.toml"), b.Bytes(), 0644) + os.WriteFile(filepath.Join(p, config_filename), b.Bytes(), 0644) fmt.Println("a") // Please add tenary operators to go istg this is painful diff --git a/main.go b/main.go index 704e776..847d705 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ import ( "github.com/diamondburned/gotk4/pkg/gio/v2" "github.com/diamondburned/gotk4/pkg/glib/v2" "github.com/diamondburned/gotk4/pkg/gtk/v4" + "github.com/diamondburned/gotk4/pkg/pango" "github.com/gen2brain/beeep" "github.com/go-analyze/charts" "github.com/google/uuid" @@ -83,11 +84,18 @@ var xmlLog *os.File // Basic roster var Roster = make(map[string]bool) +var config_filename = "lambda.toml" +var app_id = "net.sunglocto.lambda" + func init() { beeep.AppName = loadedLocale["appName"] } func main() { + if os.Getenv("PROFILE") != "" { + config_filename = fmt.Sprintf("lambda_%s.toml", os.Getenv("PROFILE")) + app_id = "net.sunglocto.lambda." + os.Getenv("PROFILE") + } pingTimes = append(pingTimes, []float64{}) p, err := ensureConfig() @@ -95,7 +103,7 @@ func main() { panic(err) } - b, err := os.ReadFile(filepath.Join(p, "lambda.toml")) + b, err := os.ReadFile(filepath.Join(p, config_filename)) if err != nil { dropToSignInPage(err) return @@ -169,6 +177,7 @@ func main() { iqResp.Type = "error" iqResp.From = clientroot.Session.BindJid + iqResp.Id = iq.Id stanza_err := new(stanza.Err) stanza_err.Type = stanza.ErrorTypeCancel @@ -203,6 +212,7 @@ func main() { {Var: "urn:xmpp:ping"}, {Var: "jabber:iq:oob"}, {Var: "jabber:x:oob"}, + {Var: "jabber:x:data"}, }, } iqResp.Payload = &payload @@ -230,6 +240,7 @@ func main() { iqResp.Type = "error" iqResp.From = clientroot.Session.BindJid + iqResp.Id = iq.Id stanza_err := new(stanza.Err) stanza_err.Type = stanza.ErrorTypeCancel @@ -360,8 +371,8 @@ func main() { // Handle read indicators marker := new(Marker) ok = m.Get(marker) - if ok { - _, ok := typed_tab.msg_refsOSID[marker.ID] + if ok && !loadedConfig.CompactMode { + _, ok := typed_tab.msg_refsOSID.Load(marker.ID) if ok { im := gtk.NewImage() im.SetPixelSize(20) @@ -381,10 +392,13 @@ func main() { go getAvatar(m.From, vu.Photo, im) } - ind, ok := typed_tab.msg_refsOSID[marker.ID] - if ok { - if ind.read_indicator != nil { - ind.read_indicator.Append(im) + uind, loaded := typed_tab.msg_refsOSID.Load(marker.ID) + if loaded { + ind, ok := uind.(*abMessage) + if ok { + if ind.read_indicator != nil { + ind.read_indicator.Append(im) + } } } return @@ -423,30 +437,21 @@ func main() { tab, ok := tabs.Load(originator) if ok { typed_tab := tab.(*chatTab) - /* - to_retract := "" - refs := typed_tab.msg_refsID - - // If it is a DM, then we use regular message ID. If it is a MUC, we use stanza id - to_retract = sid.ID - if typed_tab.isMuc && sid != nil { - to_retract = sid.ID - refs = typed_tab.msg_refsOSID - } - */ - - msg, ok := typed_tab.msg_refsOSID[retraction.ID] + umsg, ok := typed_tab.msg_refsOSID.Load(retraction.ID) if ok { - lab := msg.label_ref - glib.IdleAdd(func() { - GenerateRetraction(lab, *retraction) - }) - return + msg, ok := umsg.(*abMessage) + if ok { + lab := msg.label_ref + glib.IdleAdd(func() { + GenerateRetraction(lab, *retraction) + }) + return + } } } } - glib.IdleAdd(func() { + glib.IdleAddPriority(glib.PriorityLow, func() { b := gtk.NewBox(gtk.OrientationVertical, 0) tab, ok := tabs.Load(originator) @@ -472,9 +477,11 @@ func main() { if valid && converted_successfully { mStatus.SetText(originator) - typed_tab.msgs.Append(b) - b.Append(ba) - b.Append(gtk.NewSeparator(gtk.OrientationVertical)) + if typed_tab.msgs != nil { + typed_tab.msgs.Append(b) + b.Append(ba) + b.Append(gtk.NewSeparator(gtk.OrientationVertical)) + } } }) }) @@ -496,6 +503,20 @@ func main() { ok = presence.Get(&mu) if ok { // This is a presence stanza from a user in a MUC + from_self, created := false, false + for _, status := range mu.StatusCodes { + if status.Code == "110" { + from_self = true + } + + if status.Code == "201" { + created = true + } + } + + if from_self && created { + showDialog("You have created a new MUC. In order for people to be able to join this MUC, you must first configure it. You can configure this MUC by clicking on the MUC button in the top right and then clicking 'Configre MUC'.", &window.Window) + } presence.Get(&ocu) id := JidMustParse(presence.From).Resource @@ -524,7 +545,7 @@ func main() { if presence.Type != "unavailable" { _, ok := typed_unit.Members.Load(id) if !ok && loadedConfig.ShowPresenceUpdates { - glib.IdleAdd(func() { + glib.IdleAddPriority(glib.PriorityLow, func() { b := gtk.NewBox(gtk.OrientationVertical, 0) ba, ok := generatePresenceWidget(p).(*gtk.Box) if ok { @@ -548,7 +569,7 @@ func main() { } else { typed_unit.Members.Delete(id) if loadedConfig.ShowPresenceUpdates { - glib.IdleAdd(func() { + glib.IdleAddPriority(glib.PriorityLow, func() { b := gtk.NewBox(gtk.OrientationVertical, 0) ba, ok := generatePresenceWidget(p).(*gtk.Box) if ok { @@ -604,7 +625,7 @@ func main() { cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) { fmt.Println("XMPP client connected") - // client = c + client = c // clientroot = c go func() { @@ -810,7 +831,7 @@ func main() { } } - app := gtk.NewApplication("net.sunglocto.lambda", gio.ApplicationDefaultFlags) + app := gtk.NewApplication(app_id, gio.ApplicationDefaultFlags) app.ConnectActivate(func() { activate(app) go conc() @@ -834,6 +855,86 @@ func activate(app *gtk.Application) { fileMenu := gio.NewMenu() fileMenu.Append(loadedLocale["joinMUCMenu"], "app.join") fileMenu.Append(loadedLocale["destroyMUCMenu"], "app.destroymuc") + fileMenu.Append("MUC Affiliations", "app.mucaffils") + fileMenu.Append("Configure MUC", "app.configuremuc") + + viewAffilsMucAction := gio.NewSimpleAction("mucaffils", nil) + viewAffilsMucAction.ConnectActivate(func(p *glib.Variant) { + go func() { + cur, ok := tabs.Load(current) + if ok { + cur := cur.(*chatTab) + if cur.isMuc { + affils := []string{"owner", "admin", "member", "outcast"} + items := []MucAdminItem{} + + for _, affil := range affils { + iq, err := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet, To: current, Id: uuid.NewString(), From: clientroot.Session.BindJid}) + if err != nil { + return + } + + q := new(MucAdminQuery) + q.MucAdminItems = append(q.MucAdminItems, MucAdminItem{Affiliation: affil}) + iq.Payload = q + ctx := context.TODO() + mychan, err := client.SendIQ(ctx, iq) + + result := <-mychan + res, ok := result.Payload.(*MucAdminQuery) + if ok { + for _, item := range res.MucAdminItems { + items = append(items, item) + } + } + } + + glib.IdleAdd(func() { + affil_win := gtk.NewWindow() + affil_box := gtk.NewBox(gtk.OrientationVertical, 2) + affil_box.Append(gtk.NewLabel("Affiliations")) + + scroller := gtk.NewScrolledWindow() + scroller_grid := gtk.NewGrid() + scroller_grid.SetVExpand(true) + scroller.SetChild(scroller_grid) + scroller.SetVExpand(true) + + affil_box.Append(scroller) + + affil_win.SetChild(affil_box) + scroller_grid.Attach(gtk.NewLabel("Affiliation"), 0, 0, 1, 1) + scroller_grid.Attach(gtk.NewLabel("JID"), 1, 0, 1, 1) + scroller_grid.Attach(gtk.NewLabel("Nick"), 2, 0, 1, 1) + scroller_grid.Attach(gtk.NewLabel("Reason"), 3, 0, 1, 1) + + for i, v := range items { + medal_box := gtk.NewBox(gtk.OrientationHorizontal, 0) + medal_box.Append(gtk.NewImageFromPaintable(clientAssetsLoad(v.Affiliation))) + medal_box.Append(gtk.NewLabel(v.Affiliation)) + scroller_grid.Attach(medal_box, 0, i+1, 1, 1) + + jid := gtk.NewLabel(v.JID) + jid.AddCSSClass("jid") + jid.SetSelectable(true) + scroller_grid.Attach(jid, 1, i+1, 1, 1) + + nick := gtk.NewLabel(v.Nick) + nick.SetSelectable(true) + scroller_grid.Attach(nick, 2, i+1, 1, 1) + + reason := gtk.NewLabel(v.Reason) + reason.SetSelectable(true) + reason.SetEllipsize(pango.EllipsizeEnd) + scroller_grid.Attach(reason, 3, i+1, 1, 1) + } + + affil_win.SetVisible(true) + }) + } + } + }() + }) dmMenu := gio.NewMenu() dmMenu.Append(loadedLocale["startDMMenu"], "app.dm") @@ -890,12 +991,81 @@ func activate(app *gtk.Application) { a.SetAuthors([]string{"Sunglocto", "Hydrogen", "Kuyuhi"}) a.SetLicenseType(gtk.LicenseGPL30) a.SetLogo(clientAssetsLoad("logo")) - a.SetWebsite("https://forge.sunglocto.net/sunglocto/lambda") + a.SetWebsite("https://sunglocto.net/lambda") a.SetWebsiteLabel("Website") a.SetVisible(true) }) + configureMucAction := gio.NewSimpleAction("configuremuc", nil) + configureMucAction.ConnectActivate(func(p *glib.Variant) { + go func() { + cur, ok := tabs.Load(current) + if ok { + cur := cur.(*chatTab) + if cur.isMuc { + iq, err := stanza.NewIQ(stanza.Attrs{ + Type: stanza.IQTypeGet, + Id: uuid.NewString(), + To: current, + }) + + if err != nil { + glib.IdleAdd(func() { showErrorDialog(err, &window.Window) }) + return + } + + iq.Payload = &MucOwnerQuery{} + + ctx := context.TODO() + mychan, err := client.SendIQ(ctx, iq) + if err != nil { + glib.IdleAdd(func() { showErrorDialog(err, &window.Window) }) + return + } + + result := <-mychan + res, ok := result.Payload.(*MucOwnerQuery) + + if !ok { + return + } + + glib.IdleAdd(func() { + f, win, _ := showFormDialog(res.Form) + win.ConnectCloseRequest(func() (ok bool) { + go func() { + iq, err := stanza.NewIQ(stanza.Attrs{ + Id: result.Id, + To: result.From, + Type: stanza.IQTypeSet, + }) + if err != nil { + glib.IdleAdd(func() { showErrorDialog(err, &window.Window) }) + return + } + + f.Type = "submit" + mo := new(MucOwnerQuery) + mo.Form = *f + + iq.Payload = mo + + ctx := context.TODO() + _, err = client.SendIQ(ctx, iq) + if err != nil { + glib.IdleAdd(func() { showErrorDialog(err, &window.Window) }) + return + } + }() + return false + }) + }) + } + } + }() + }) + destroymucAction := gio.NewSimpleAction("destroymuc", nil) destroymucAction.ConnectActivate(func(p *glib.Variant) { cur, ok := tabs.Load(current) @@ -919,7 +1089,6 @@ func activate(app *gtk.Application) { submit := gtk.NewButtonWithLabel(loadedLocale["destroyMUCActionButton"]) submit.ConnectClicked(func() { - fmt.Println(en.Text()) if en.Text() == loadedLocale["destroyMUCPassword"] { cur, ok := tabs.Load(current) if ok { @@ -1069,7 +1238,6 @@ func activate(app *gtk.Application) { var res *stanza.DiscoInfo allowed := true - fmt.Println("Attempting to get Disco info") myIQ, err := stanza.NewIQ(stanza.Attrs{ Type: "get", @@ -1225,7 +1393,9 @@ func activate(app *gtk.Application) { app.AddAction(joinAction) app.AddAction(aboutAction) app.AddAction(destroymucAction) + app.AddAction(configureMucAction) app.AddAction(startDmAction) + app.AddAction(viewAffilsMucAction) the_menu.AppendSubmenu("MUC", fileMenu) the_menu.AppendSubmenu("DM", dmMenu) @@ -1350,16 +1520,13 @@ func activate(app *gtk.Application) { scrollerStatBar.SetChild(statBar) box.Append(scrollerStatBar) - // scroller.SetChild(empty_dialog) scroller.SetChild(empty_dialog) menu_scroll := gtk.NewScrolledWindow() menu_scroll.SetHExpand(true) menu_scroll.SetChild(menu) - // box.Append(menu_scroll) chatbox := gtk.NewBox(gtk.OrientationHorizontal, 0) - // chatbox.Append(menu_scroll) chat_pane := gtk.NewPaned(gtk.OrientationHorizontal) chat_pane.SetStartChild(scroller) chat_pane.SetEndChild(memberList) diff --git a/style.css b/style.css index fd09de1..4f5ea99 100644 --- a/style.css +++ b/style.css @@ -139,3 +139,8 @@ color: black; } + +.you { + color: red; + text-decoration: underline; +} diff --git a/types.go b/types.go index 16609a1..300ab35 100644 --- a/types.go +++ b/types.go @@ -14,10 +14,11 @@ type abMessage struct { } type chatTab struct { - isMuc bool - msgs *gtk.ListBox - msg_refsID map[string]*abMessage - msg_refsOSID map[string]*abMessage + isMuc bool + msgs *gtk.ListBox + msg_refsID map[string]*abMessage + // msg_refsOSID map[string]*abMessage + msg_refsOSID sync.Map name string current_nick string muc_presence *stanza.Presence diff --git a/version.go b/version.go index a570502..1d36c6b 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,3 @@ package main -var lambda_version string = "10" +var lambda_version string = "11" diff --git a/xmpp-helpers.go b/xmpp-helpers.go index cb3d4ac..d2aa54c 100644 --- a/xmpp-helpers.go +++ b/xmpp-helpers.go @@ -37,6 +37,7 @@ func joinMuc(c xmpp.Sender, jid string, muc string, nick string, password string Attrs: stanza.Attrs{ From: jid, To: addr, + Lang: "en", }, Extensions: []stanza.PresExtension{ &stanza.MucPresence{}, @@ -47,6 +48,7 @@ func joinMuc(c xmpp.Sender, jid string, muc string, nick string, password string Attrs: stanza.Attrs{ From: jid, To: addr, + Lang: "en", }, Extensions: []stanza.PresExtension{ &stanza.MucPresence{ @@ -162,11 +164,13 @@ func addPEPBookmark(c xmpp.Sender, jid string, name string, nick string, autojoi persist.ValuesList = []string{"true"} options_form.Fields = append(options_form.Fields, persist) - max_items := new(stanza.Field) - max_items.Type = "text-single" - max_items.Var = "pubsub#max_items" - max_items.ValuesList = []string{"max"} - options_form.Fields = append(options_form.Fields, max_items) + /* + max_items := new(stanza.Field) + max_items.Type = "text-single" + max_items.Var = "pubsub#max_items" + max_items.ValuesList = []string{"max"} + options_form.Fields = append(options_form.Fields, max_items) + */ send_last_published_item := new(stanza.Field) send_last_published_item.Type = "text-single" diff --git a/xmpp-mucadmin.go b/xmpp-mucadmin.go new file mode 100644 index 0000000..8ccb8f8 --- /dev/null +++ b/xmpp-mucadmin.go @@ -0,0 +1,35 @@ +package main + +// Implementation of XEP-0045 admin +// https://xmpp.org/extensions/xep-0045.html + +import ( + "encoding/xml" + "gosrc.io/xmpp/stanza" +) + +type MucAdminQuery struct { + XMLName xml.Name `xml:"http://jabber.org/protocol/muc#admin query"` + MucAdminItems []MucAdminItem `xml:"http://jabber.org/protocol/muc#admin item"` + ResultSet *stanza.ResultSet `xml:"set,omitempty"` +} + +type MucAdminItem struct { + Affiliation string `xml:"affiliation,attr,omitempty"` + JID string `xml:"jid,attr,omitempty"` + Nick string `xml:"nick,attr,omitempty"` + Role string `xml:"role,attr,omitempty"` + Reason string `xml:"reason,omitempty"` +} + +func (v *MucAdminQuery) Namespace() string { + return v.XMLName.Space +} + +func (v *MucAdminQuery) GetSet() *stanza.ResultSet { + return v.ResultSet +} + +func init() { + stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{Space: "http://jabber.org/protocol/muc#admin", Local: "query"}, MucAdminQuery{}) +} diff --git a/xmpp-mucowner.go b/xmpp-mucowner.go new file mode 100644 index 0000000..93a6fd7 --- /dev/null +++ b/xmpp-mucowner.go @@ -0,0 +1,27 @@ +package main + +// Implementation of XEP-0045 admin +// https://xmpp.org/extensions/xep-0045.html + +import ( + "encoding/xml" + "gosrc.io/xmpp/stanza" +) + +type MucOwnerQuery struct { + XMLName xml.Name `xml:"http://jabber.org/protocol/muc#owner query"` + ResultSet *stanza.ResultSet `xml:"set,omitempty"` + Form stanza.Form `xml:"jabber:x:data x,omitempty"` +} + +func (v *MucOwnerQuery) Namespace() string { + return v.XMLName.Space +} + +func (v *MucOwnerQuery) GetSet() *stanza.ResultSet { + return v.ResultSet +} + +func init() { + stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{Space: "http://jabber.org/protocol/muc#owner", Local: "query"}, MucOwnerQuery{}) +} diff --git a/xmpp-mucuser.go b/xmpp-mucuser.go index 5a5a704..24ec78f 100644 --- a/xmpp-mucuser.go +++ b/xmpp-mucuser.go @@ -11,8 +11,9 @@ import ( type MucUser struct { stanza.PresExtension stanza.MsgExtension - XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"` - MucUserItem MucUserItem `xml:"item,omitempty"` + XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"` + StatusCodes []MucUserStatusCode `xml:"status,omitempty"` + MucUserItem MucUserItem `xml:"item,omitempty"` } type MucUserItem struct { @@ -29,6 +30,10 @@ type Actor struct { Nick string `xml:"nick,attr"` } +type MucUserStatusCode struct { + Code string `xml:"code,attr"` +} + func init() { stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{Space: "http://jabber.org/protocol/muc#user", Local: "x"}, MucUser{}) stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "http://jabber.org/protocol/muc#user", Local: "x"}, MucUser{})