Compare commits
31 Commits
777df725b6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 359e8ed63e | |||
| 3b9df94cf6 | |||
| 634f451595 | |||
| 00d7945f70 | |||
| 60d6a287e5 | |||
| d5981b7475 | |||
| 523722bae6 | |||
| 71e6a58fbd | |||
| a7e90e4ae5 | |||
| 654ab8b618 | |||
| 7a201808e3 | |||
| 77e4e444d4 | |||
| bf1685a382 | |||
| 0ac43946b1 | |||
| 15ff7a20db | |||
| 1dd3f09fed | |||
| 87bdbc440a | |||
| 62b5a9db72 | |||
| 39156af48a | |||
| 3f40d3da29 | |||
| a7a49f7441 | |||
| e026e777f6 | |||
| 09a809c102 | |||
| 29ef37e237 | |||
| 713cb24508 | |||
| 589101c292 | |||
| f807565cb2 | |||
| 6626d35920 | |||
| 5c76729a6b | |||
| c260b8b231 | |||
| 971147dcb8 |
15
CONTRIBUTING.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
As you might have gathered, this Gitea instance does not allow registrations. If you want to have an account on this instance in order to contribute to this project, you must contact me for an account. I am available on:
|
||||||
|
|
||||||
|
- XMPP: `x@sunglocto.net`
|
||||||
|
- Matrix: `@x:sunglocto.net`
|
||||||
|
- E-Mail: sunglocto <img width=16px height=16px src="https://sunglocto.net/icons/email.gif"/> protonmail.com
|
||||||
|
|
||||||
|
For actually submitting your patches, you can just fork this repo and submit a pull request. I currently have no contribution policy that you have to follow however if you just use common sense you should be good.
|
||||||
|
|
||||||
|
## AI contributions
|
||||||
|
|
||||||
|
Obviously vibe-coded or fully AI-generated contributions will be immediately rejected. This is in order to ensure the stability of the project and decrease the chance of significant bugs or vulnerabilities.
|
||||||
|
|
||||||
|
AI contributions that you have reviewed, edited and **ensured that they compile, run and work successfully** are acceptable. AI is a tool, and like any other tools, can be used for good if used in moderation.
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
# lambda
|
# lambda
|
||||||
|
|
||||||
an XMPP client
|
an XMPP client
|
||||||
|
|
||||||
|
icons are from Psi+ ([https://github.com/psi-im](https://github.com/psi-im))
|
||||||
|
|
||||||
|
additional icons are by Mark James's Silk Icon Set [https://github.com/markjames/famfamfam-silk-icons](https://github.com/markjames/famfamfam-silk-icons)
|
||||||
|
|||||||
5
TODO.md
@@ -1,7 +1,6 @@
|
|||||||
# TODO
|
# TODO
|
||||||
- XEP-0393: Message Styling 0%
|
- XEP-0393: Message Styling 0%
|
||||||
- XEP-0402: PEP Native Bookmarks 0%
|
- XEP-0402: PEP Native Bookmarks 50% (often lags out client if enabled)
|
||||||
- XEP-0066: Out of Band Data partial
|
- XEP-0461: Message Replies 0%
|
||||||
- XEP-0461: Message Replies partial
|
|
||||||
- XEP-0444: Message Reactions partial
|
- XEP-0444: Message Reactions partial
|
||||||
|
|
||||||
|
|||||||
334
assets.go
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"encoding/base64"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed debug.png
|
||||||
|
var defaultAvatarBytes []byte
|
||||||
|
var defaultAvatarB64 string = base64.StdEncoding.EncodeToString(defaultAvatarBytes)
|
||||||
|
|
||||||
|
//go:embed failed_load.png
|
||||||
|
var failedBytes []byte
|
||||||
|
var failedB64 string = base64.StdEncoding.EncodeToString(failedBytes)
|
||||||
|
|
||||||
|
//go:embed assets/owner.png
|
||||||
|
var ownerMedalBytes []byte
|
||||||
|
var ownerMedalB64 string = base64.StdEncoding.EncodeToString(ownerMedalBytes)
|
||||||
|
|
||||||
|
//go:embed assets/admin.png
|
||||||
|
var adminMedalBytes []byte
|
||||||
|
var adminMedalB64 string = base64.StdEncoding.EncodeToString(adminMedalBytes)
|
||||||
|
|
||||||
|
//go:embed assets/member.png
|
||||||
|
var memberMedalBytes []byte
|
||||||
|
var memberMedalB64 string = base64.StdEncoding.EncodeToString(memberMedalBytes)
|
||||||
|
|
||||||
|
//go:embed assets/noaffiliation.png
|
||||||
|
var noneMedalBytes []byte
|
||||||
|
var noneMedalB64 string = base64.StdEncoding.EncodeToString(noneMedalBytes)
|
||||||
|
|
||||||
|
//go:embed assets/outcast.png
|
||||||
|
var outcastMedalBytes []byte
|
||||||
|
var outcastMedalB64 string = base64.StdEncoding.EncodeToString(outcastMedalBytes)
|
||||||
|
|
||||||
|
//go:embed assets/cancel.png
|
||||||
|
var cancelBytes []byte
|
||||||
|
var cancelB64 string = base64.StdEncoding.EncodeToString(cancelBytes)
|
||||||
|
|
||||||
|
//go:embed assets/status_away.png
|
||||||
|
var sABytes []byte
|
||||||
|
var sAB64 string = base64.StdEncoding.EncodeToString(sABytes)
|
||||||
|
|
||||||
|
//go:embed assets/status_busy.png
|
||||||
|
var sBBytes []byte
|
||||||
|
var sBB64 string = base64.StdEncoding.EncodeToString(sBBytes)
|
||||||
|
|
||||||
|
//go:embed assets/status_chatty.png
|
||||||
|
var sCBytes []byte
|
||||||
|
var sCB64 string = base64.StdEncoding.EncodeToString(sCBytes)
|
||||||
|
|
||||||
|
//go:embed assets/status_online.png
|
||||||
|
var sOBytes []byte
|
||||||
|
var sOB64 string = base64.StdEncoding.EncodeToString(sOBytes)
|
||||||
|
|
||||||
|
//go:embed assets/status_xa.png
|
||||||
|
var xaBytes []byte
|
||||||
|
var xaB64 string = base64.StdEncoding.EncodeToString(xaBytes)
|
||||||
|
|
||||||
|
//go:embed assets/tag.png
|
||||||
|
var tagBytes []byte
|
||||||
|
var tagB64 string = base64.StdEncoding.EncodeToString(tagBytes)
|
||||||
|
|
||||||
|
//go:embed assets/lambda-disabled.png
|
||||||
|
var logoDisabledBytes []byte
|
||||||
|
var logoDisabledB64 string = base64.StdEncoding.EncodeToString(logoDisabledBytes)
|
||||||
|
|
||||||
|
//go:embed assets/group.png
|
||||||
|
var groupBytes []byte
|
||||||
|
var groupB64 string = base64.StdEncoding.EncodeToString(groupBytes)
|
||||||
|
|
||||||
|
//go:embed assets/door_in.png
|
||||||
|
var doorInBytes []byte
|
||||||
|
var doorInB64 string = base64.StdEncoding.EncodeToString(doorInBytes)
|
||||||
|
|
||||||
|
//go:embed assets/door_out.png
|
||||||
|
var doorOutBytes []byte
|
||||||
|
var doorOutB64 string = base64.StdEncoding.EncodeToString(doorOutBytes)
|
||||||
|
|
||||||
|
//go:embed assets/large_group.png
|
||||||
|
var largeGroupBytes []byte
|
||||||
|
var largeGroupB64 string = base64.StdEncoding.EncodeToString(largeGroupBytes)
|
||||||
|
|
||||||
|
//go:embed assets/world.png
|
||||||
|
var worldBytes []byte
|
||||||
|
var worldB64 string = base64.StdEncoding.EncodeToString(worldBytes)
|
||||||
|
|
||||||
|
//go:embed assets/disconnect.png
|
||||||
|
var disconnectBytes []byte
|
||||||
|
var disconnectB64 string = base64.StdEncoding.EncodeToString(disconnectBytes)
|
||||||
|
|
||||||
|
//go:embed assets/chart_bar.png
|
||||||
|
var barBytes []byte
|
||||||
|
var barB64 string = base64.StdEncoding.EncodeToString(barBytes)
|
||||||
|
|
||||||
|
//go:embed assets/ok.png
|
||||||
|
var okBytes []byte
|
||||||
|
var okB64 string = base64.StdEncoding.EncodeToString(okBytes)
|
||||||
|
|
||||||
|
//go:embed assets/hourglass.png
|
||||||
|
var hourglassBytes []byte
|
||||||
|
var hourglassB64 string = base64.StdEncoding.EncodeToString(hourglassBytes)
|
||||||
|
|
||||||
|
//go:embed assets/connect_tls.png
|
||||||
|
var connectBytes []byte
|
||||||
|
var connectB64 string = base64.StdEncoding.EncodeToString(connectBytes)
|
||||||
|
|
||||||
|
//go:embed assets/comment.png
|
||||||
|
var commentBytes []byte
|
||||||
|
var commentB64 string = base64.StdEncoding.EncodeToString(commentBytes)
|
||||||
|
|
||||||
|
//go:embed assets/information.png
|
||||||
|
var informationBytes []byte
|
||||||
|
var informationB64 string = base64.StdEncoding.EncodeToString(informationBytes)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
loader := gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
defaultAvatarData, _ := base64.StdEncoding.DecodeString(defaultAvatarB64)
|
||||||
|
loader.Write(defaultAvatarData)
|
||||||
|
loader.Close()
|
||||||
|
clientAssets["DefaultAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
failedData, _ := base64.StdEncoding.DecodeString(failedB64)
|
||||||
|
loader.Write(failedData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["FailedAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
ownerMedalData, _ := base64.StdEncoding.DecodeString(ownerMedalB64)
|
||||||
|
loader.Write(ownerMedalData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["owner"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
cancelData, _ := base64.StdEncoding.DecodeString(cancelB64)
|
||||||
|
loader.Write(cancelData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["cancel"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
tagData, _ := base64.StdEncoding.DecodeString(tagB64)
|
||||||
|
loader.Write(tagData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["tag"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
adminMedalData, _ := base64.StdEncoding.DecodeString(adminMedalB64)
|
||||||
|
loader.Write(adminMedalData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["admin"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
memberMedalData, _ := base64.StdEncoding.DecodeString(memberMedalB64)
|
||||||
|
loader.Write(memberMedalData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["member"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
noneMedalData, _ := base64.StdEncoding.DecodeString(noneMedalB64)
|
||||||
|
loader.Write(noneMedalData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["none"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
outcastMedalData, _ := base64.StdEncoding.DecodeString(outcastMedalB64)
|
||||||
|
loader.Write(outcastMedalData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["outcast"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
disabledLogoData, _ := base64.StdEncoding.DecodeString(logoDisabledB64)
|
||||||
|
loader.Write(disabledLogoData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["disabled_logo"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
groupData, _ := base64.StdEncoding.DecodeString(groupB64)
|
||||||
|
loader.Write(groupData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["group"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
doorInData, _ := base64.StdEncoding.DecodeString(doorInB64)
|
||||||
|
loader.Write(doorInData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["door_in"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
doorOutData, _ := base64.StdEncoding.DecodeString(doorOutB64)
|
||||||
|
loader.Write(doorOutData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["door_out"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
largeGroupData, _ := base64.StdEncoding.DecodeString(largeGroupB64)
|
||||||
|
loader.Write(largeGroupData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["large_group"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
worldData, _ := base64.StdEncoding.DecodeString(worldB64)
|
||||||
|
loader.Write(worldData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["world"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
disconnectData, _ := base64.StdEncoding.DecodeString(disconnectB64)
|
||||||
|
loader.Write(disconnectData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["disconnect"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
barData, _ := base64.StdEncoding.DecodeString(barB64)
|
||||||
|
loader.Write(barData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["chart_bar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
okData, _ := base64.StdEncoding.DecodeString(okB64)
|
||||||
|
loader.Write(okData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["ok"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
hourglassData, _ := base64.StdEncoding.DecodeString(hourglassB64)
|
||||||
|
loader.Write(hourglassData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["hourglass"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
connectData, _ := base64.StdEncoding.DecodeString(connectB64)
|
||||||
|
loader.Write(connectData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["connect"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
commentData, _ := base64.StdEncoding.DecodeString(commentB64)
|
||||||
|
loader.Write(commentData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["comment"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
informationData, _ := base64.StdEncoding.DecodeString(informationB64)
|
||||||
|
loader.Write(informationData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["information"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
sAData, _ := base64.StdEncoding.DecodeString(sAB64)
|
||||||
|
loader.Write(sAData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["status_away"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
sBData, _ := base64.StdEncoding.DecodeString(sBB64)
|
||||||
|
loader.Write(sBData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["status_dnd"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
sCData, _ := base64.StdEncoding.DecodeString(sCB64)
|
||||||
|
loader.Write(sCData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["status_chat"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
xaData, _ := base64.StdEncoding.DecodeString(xaB64)
|
||||||
|
loader.Write(xaData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["status_xa"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
|
||||||
|
loader = gdkpixbuf.NewPixbufLoader()
|
||||||
|
|
||||||
|
sOData, _ := base64.StdEncoding.DecodeString(sOB64)
|
||||||
|
loader.Write(sOData)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
clientAssets["status_"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
||||||
|
}
|
||||||
BIN
assets/ban.png
Normal file
|
After Width: | Height: | Size: 762 B |
BIN
assets/cancel.png
Normal file
|
After Width: | Height: | Size: 607 B |
BIN
assets/chart_bar.png
Normal file
|
After Width: | Height: | Size: 541 B |
1
assets/client_icons/README
Normal file
@@ -0,0 +1 @@
|
|||||||
|
All client assets are owned by their respective owners
|
||||||
BIN
assets/comment.png
Normal file
|
After Width: | Height: | Size: 413 B |
BIN
assets/connect_tls.png
Normal file
|
After Width: | Height: | Size: 721 B |
BIN
assets/disconnect.png
Normal file
|
After Width: | Height: | Size: 796 B |
BIN
assets/door_in.png
Normal file
|
After Width: | Height: | Size: 693 B |
BIN
assets/door_out.png
Normal file
|
After Width: | Height: | Size: 643 B |
BIN
assets/group.png
Normal file
|
After Width: | Height: | Size: 753 B |
BIN
assets/hourglass.png
Normal file
|
After Width: | Height: | Size: 744 B |
BIN
assets/icon.ico
Normal file
|
After Width: | Height: | Size: 264 KiB |
BIN
assets/icon.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/information.png
Normal file
|
After Width: | Height: | Size: 778 B |
BIN
assets/jabber.png
Normal file
|
After Width: | Height: | Size: 730 B |
BIN
assets/kick.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/lambda-disabled.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/large_group.png
Normal file
|
After Width: | Height: | Size: 747 B |
BIN
assets/ok.png
Normal file
|
After Width: | Height: | Size: 460 B |
BIN
assets/status_away.png
Normal file
|
After Width: | Height: | Size: 794 B |
BIN
assets/status_busy.png
Normal file
|
After Width: | Height: | Size: 751 B |
BIN
assets/status_chatty.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/status_online.png
Normal file
|
After Width: | Height: | Size: 722 B |
BIN
assets/status_xa.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/tag.png
Normal file
|
After Width: | Height: | Size: 666 B |
BIN
assets/vcard.png
Normal file
|
After Width: | Height: | Size: 362 B |
BIN
assets/world.png
Normal file
|
After Width: | Height: | Size: 923 B |
3
cache.go
@@ -18,6 +18,9 @@ import (
|
|||||||
// global or app-level map/cache
|
// global or app-level map/cache
|
||||||
var textureCache = make(map[string]gdk.Paintabler)
|
var textureCache = make(map[string]gdk.Paintabler)
|
||||||
|
|
||||||
|
// Invalid images, if an image/avatar cannot be loaded on the system (e.g: incompatible format) it's put here
|
||||||
|
var invalidImages = make(map[string]bool)
|
||||||
|
|
||||||
func ensureCache() (string, error) {
|
func ensureCache() (string, error) {
|
||||||
cachePath := configdir.LocalCache("lambda-im")
|
cachePath := configdir.LocalCache("lambda-im")
|
||||||
err := configdir.MakePath(cachePath) // Ensure it exists.
|
err := configdir.MakePath(cachePath) // Ensure it exists.
|
||||||
|
|||||||
BIN
failed_load.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
24
go.mod
@@ -5,22 +5,38 @@ go 1.25.5
|
|||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.6.0
|
github.com/BurntSushi/toml v1.6.0
|
||||||
github.com/diamondburned/gotk4/pkg v0.3.1
|
github.com/diamondburned/gotk4/pkg v0.3.1
|
||||||
|
github.com/gen2brain/beeep v0.11.2
|
||||||
|
github.com/go-analyze/charts v0.5.24
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/jacoblockett/sanitizefilename v1.0.1
|
github.com/jacoblockett/sanitizefilename v1.0.1
|
||||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030
|
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030
|
||||||
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
||||||
github.com/kr/pretty v0.1.0
|
github.com/kr/pretty v0.2.0
|
||||||
|
golang.org/x/net v0.29.0
|
||||||
gosrc.io/xmpp v0.5.1
|
gosrc.io/xmpp v0.5.1
|
||||||
mellium.im/xmpp v0.22.0
|
mellium.im/xmpp v0.22.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
|
||||||
github.com/KarpelesLab/weak v0.1.1 // indirect
|
github.com/KarpelesLab/weak v0.1.1 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/esiqveland/notify v0.13.3 // indirect
|
||||||
|
github.com/go-analyze/bulk v0.1.3 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
|
github.com/jackmordaunt/icns/v3 v3.0.1 // indirect
|
||||||
github.com/kr/text v0.1.0 // indirect
|
github.com/kr/text v0.1.0 // indirect
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
|
github.com/sergeymakinen/go-bmp v1.0.0 // indirect
|
||||||
|
github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect
|
||||||
|
github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||||
golang.org/x/net v0.29.0 // indirect
|
golang.org/x/image v0.24.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/text v0.18.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
|
golang.org/x/text v0.22.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 // indirect
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 // indirect
|
||||||
mellium.im/reader v0.1.0 // indirect
|
mellium.im/reader v0.1.0 // indirect
|
||||||
mellium.im/xmlstream v0.15.4 // indirect
|
mellium.im/xmlstream v0.15.4 // indirect
|
||||||
|
|||||||
56
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
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=
|
||||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
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/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/KarpelesLab/weak v0.1.1 h1:fNnlPo3aypS9tBzoEQluY13XyUfd/eWaSE/vMvo9s4g=
|
github.com/KarpelesLab/weak v0.1.1 h1:fNnlPo3aypS9tBzoEQluY13XyUfd/eWaSE/vMvo9s4g=
|
||||||
@@ -10,18 +12,35 @@ github.com/chromedp/cdproto v0.0.0-20190926234355-1b4886c6fad6/go.mod h1:0YChpVz
|
|||||||
github.com/chromedp/chromedp v0.3.1-0.20190619195644-fd957a4d2901/go.mod h1:mJdvfrVn594N9tfiPecUidF6W5jPRKHymqHfzbobPsM=
|
github.com/chromedp/chromedp v0.3.1-0.20190619195644-fd957a4d2901/go.mod h1:mJdvfrVn594N9tfiPecUidF6W5jPRKHymqHfzbobPsM=
|
||||||
github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4=
|
github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/diamondburned/gotk4/pkg v0.3.1 h1:uhkXSUPUsCyz3yujdvl7DSN8jiLS2BgNTQE95hk6ygg=
|
github.com/diamondburned/gotk4/pkg v0.3.1 h1:uhkXSUPUsCyz3yujdvl7DSN8jiLS2BgNTQE95hk6ygg=
|
||||||
github.com/diamondburned/gotk4/pkg v0.3.1/go.mod h1:DqeOW+MxSZFg9OO+esk4JgQk0TiUJJUBfMltKhG+ub4=
|
github.com/diamondburned/gotk4/pkg v0.3.1/go.mod h1:DqeOW+MxSZFg9OO+esk4JgQk0TiUJJUBfMltKhG+ub4=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
|
github.com/esiqveland/notify v0.13.3 h1:QCMw6o1n+6rl+oLUfg8P1IIDSFsDEb2WlXvVvIJbI/o=
|
||||||
|
github.com/esiqveland/notify v0.13.3/go.mod h1:hesw/IRYTO0x99u1JPweAl4+5mwXJibQVUcP0Iu5ORE=
|
||||||
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/gen2brain/beeep v0.11.2 h1:+KfiKQBbQCuhfJFPANZuJ+oxsSKAYNe88hIpJuyKWDA=
|
||||||
|
github.com/gen2brain/beeep v0.11.2/go.mod h1:jQVvuwnLuwOcdctHn/uyh8horSBNJ8uGb9Cn2W4tvoc=
|
||||||
|
github.com/go-analyze/bulk v0.1.3 h1:pzRdBqzHDAT9PyROt0SlWE0YqPtdmTcEpIJY0C3vF0c=
|
||||||
|
github.com/go-analyze/bulk v0.1.3/go.mod h1:afon/KtFJYnekIyN20H/+XUvcLFjE8sKR1CfpqfClgM=
|
||||||
|
github.com/go-analyze/charts v0.5.24 h1:HamvYWEgbANbLb/P6uijygtG9qeSYev34ki1RelA1Lw=
|
||||||
|
github.com/go-analyze/charts v0.5.24/go.mod h1:s1YvQhjiSwtLx1f2dOKfiV9x2TT49nVSL6v2rlRpTbY=
|
||||||
github.com/go-interpreter/wagon v0.5.1-0.20190713202023-55a163980b6c/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
|
github.com/go-interpreter/wagon v0.5.1-0.20190713202023-55a163980b6c/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
|
||||||
github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
|
github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
|
||||||
|
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/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@@ -33,6 +52,8 @@ github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OI
|
|||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/jackmordaunt/icns/v3 v3.0.1 h1:xxot6aNuGrU+lNgxz5I5H0qSeCjNKp8uTXB1j8D4S3o=
|
||||||
|
github.com/jackmordaunt/icns/v3 v3.0.1/go.mod h1:5sHL59nqTd2ynTnowxB/MDQFhKNqkK8X687uKNygaSQ=
|
||||||
github.com/jacoblockett/sanitizefilename v1.0.1 h1:HhPMoPp05U5aKjhht+d7lsqhyF4trKSBme0FE6S/1C4=
|
github.com/jacoblockett/sanitizefilename v1.0.1 h1:HhPMoPp05U5aKjhht+d7lsqhyF4trKSBme0FE6S/1C4=
|
||||||
github.com/jacoblockett/sanitizefilename v1.0.1/go.mod h1:/cQHSz2fHeR1iDKpHTSW/MIyONa+Uqj0eszbvPuIxNs=
|
github.com/jacoblockett/sanitizefilename v1.0.1/go.mod h1:/cQHSz2fHeR1iDKpHTSW/MIyONa+Uqj0eszbvPuIxNs=
|
||||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030 h1:NFCJG3BerP/5ZLXwu08x9xDs+9p7AYFMeo5IXjGANxw=
|
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030 h1:NFCJG3BerP/5ZLXwu08x9xDs+9p7AYFMeo5IXjGANxw=
|
||||||
@@ -43,8 +64,9 @@ github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDS
|
|||||||
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
|
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
@@ -58,21 +80,37 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
|||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
|
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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
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.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M=
|
||||||
|
github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY=
|
||||||
|
github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3K6MFJw4GfZvQ=
|
||||||
|
github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk=
|
||||||
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
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/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=
|
||||||
github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A=
|
github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A=
|
||||||
go.coder.com/go-tools v0.0.0-20190317003359-0c6a35b74a16/go.mod h1:iKV5yK9t+J5nG9O3uF6KYdPEz3dyfMyB15MN1rbQ8Qw=
|
go.coder.com/go-tools v0.0.0-20190317003359-0c6a35b74a16/go.mod h1:iKV5yK9t+J5nG9O3uF6KYdPEz3dyfMyB15MN1rbQ8Qw=
|
||||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||||
@@ -85,6 +123,8 @@ golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
|
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||||
|
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
@@ -97,8 +137,8 @@ golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
|||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -108,9 +148,12 @@ golang.org/x/sys v0.0.0-20190618155005-516e3c20635f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
@@ -127,6 +170,9 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW
|
|||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
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=
|
||||||
gosrc.io/xmpp v0.5.1 h1:Rgrm5s2rt+npGggJH3HakQxQXR8ZZz3+QRzakRQqaq4=
|
gosrc.io/xmpp v0.5.1 h1:Rgrm5s2rt+npGggJH3HakQxQXR8ZZz3+QRzakRQqaq4=
|
||||||
gosrc.io/xmpp v0.5.1/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
|
gosrc.io/xmpp v0.5.1/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
|
||||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
|
|||||||
323
gtk-helpers.go
@@ -3,8 +3,10 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/pango"
|
||||||
"gosrc.io/xmpp/stanza"
|
"gosrc.io/xmpp/stanza"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +21,9 @@ func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
|||||||
func createTab(jid string, isMuc bool) bool {
|
func createTab(jid string, isMuc bool) bool {
|
||||||
fmt.Println("Creating tab", jid, "isMuc:", isMuc)
|
fmt.Println("Creating tab", jid, "isMuc:", isMuc)
|
||||||
_, ok := tabs.Load(jid)
|
_, ok := tabs.Load(jid)
|
||||||
if !ok {
|
_, uok := userdevices.Load(jid)
|
||||||
|
_, mok := mucmembers.Load(jid)
|
||||||
|
if !ok && !uok && !mok {
|
||||||
newTab := new(chatTab)
|
newTab := new(chatTab)
|
||||||
newTab.isMuc = isMuc
|
newTab.isMuc = isMuc
|
||||||
newTab.msgs = gtk.NewListBox()
|
newTab.msgs = gtk.NewListBox()
|
||||||
@@ -45,12 +49,21 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
|
|
||||||
scroller.SetChild(typed_tab.msgs)
|
scroller.SetChild(typed_tab.msgs)
|
||||||
if typed_tab.isMuc {
|
if typed_tab.isMuc {
|
||||||
m, _ := mucmembers.Load(jid)
|
|
||||||
ma := m.(mucUnit)
|
m, ok := mucmembers.Load(jid)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ma, ok := m.(mucUnit)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
mm := ma.Members
|
mm := ma.Members
|
||||||
gen := gtk.NewBox(gtk.OrientationVertical, 0)
|
gen := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
|
||||||
|
i := 0
|
||||||
mm.Range(func(k, v any) bool {
|
mm.Range(func(k, v any) bool {
|
||||||
|
i++
|
||||||
userbox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
userbox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
u := v.(stanza.Presence)
|
u := v.(stanza.Presence)
|
||||||
@@ -58,57 +71,203 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
var ocu OccupantID
|
var ocu OccupantID
|
||||||
u.Get(&mu)
|
u.Get(&mu)
|
||||||
u.Get(&ocu)
|
u.Get(&ocu)
|
||||||
|
//id := ocu.ID
|
||||||
|
//if id == "" {
|
||||||
|
id := JidMustParse(u.From).Resource
|
||||||
|
//}
|
||||||
|
|
||||||
nick_label := gtk.NewLabel(JidMustParse(u.From).Resource)
|
nick_label := gtk.NewLabel(JidMustParse(u.From).Resource)
|
||||||
/*
|
nick_label.SetEllipsize(pango.EllipsizeEnd)
|
||||||
affil_label := gtk.NewLabel("")
|
|
||||||
switch mu.MucUserItem.Affiliation {
|
|
||||||
case "owner":
|
|
||||||
affil_label.SetText("O")
|
|
||||||
case "admin":
|
|
||||||
affil_label.SetText("A")
|
|
||||||
case "member":
|
|
||||||
affil_label.SetText("M")
|
|
||||||
case "none":
|
|
||||||
affil_label.SetText("-")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
nick_label.AddCSSClass(mu.MucUserItem.Role)
|
nick_label.AddCSSClass(mu.MucUserItem.Role)
|
||||||
if mu.MucUserItem.Role == "visitor" {
|
if mu.MucUserItem.Role == "visitor" {
|
||||||
nick_label.SetOpacity(0.5)
|
nick_label.SetOpacity(0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\nClick for more information", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation))
|
||||||
affil_label.SetHAlign(gtk.AlignEnd)
|
|
||||||
affil_label.SetHExpand(true)
|
|
||||||
|
|
||||||
|
|
||||||
affil_label.AddCSSClass(mu.MucUserItem.Affiliation)
|
|
||||||
*/
|
|
||||||
|
|
||||||
userbox.SetTooltipText(fmt.Sprintf("%s\n%s\n%s\nRight-click for more information", u.From, mu.MucUserItem.Role, mu.MucUserItem.Affiliation))
|
|
||||||
userbox.Append(nick_label)
|
userbox.Append(nick_label)
|
||||||
// userbox.Append(affil_label)
|
|
||||||
|
var hats Hats
|
||||||
|
ok := u.Get(&hats)
|
||||||
|
if ok {
|
||||||
|
for _, hat := range hats.Hats {
|
||||||
|
tag := gtk.NewImageFromPaintable(clientAssets["tag"])
|
||||||
|
tag.SetTooltipText(hat.Title)
|
||||||
|
userbox.Prepend(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status := gtk.NewImageFromPaintable(clientAssets["status_"+string(u.Show)])
|
||||||
|
status.SetTooltipText(string(u.Show))
|
||||||
|
|
||||||
|
status.SetHAlign(gtk.AlignEnd)
|
||||||
|
// medal.SetHExpand(true)
|
||||||
|
userbox.Prepend(status)
|
||||||
|
|
||||||
medal := gtk.NewImageFromPaintable(clientAssets[mu.MucUserItem.Affiliation])
|
medal := gtk.NewImageFromPaintable(clientAssets[mu.MucUserItem.Affiliation])
|
||||||
|
medal.SetTooltipText(mu.MucUserItem.Affiliation)
|
||||||
|
|
||||||
medal.SetHAlign(gtk.AlignEnd)
|
medal.SetHAlign(gtk.AlignEnd)
|
||||||
medal.SetHExpand(true)
|
medal.SetHExpand(true)
|
||||||
userbox.Append(medal)
|
userbox.Append(medal)
|
||||||
|
|
||||||
gesture := gtk.NewGestureClick()
|
gesture := gtk.NewGestureClick()
|
||||||
gesture.SetButton(3) // Right click
|
gesture.SetButton(1)
|
||||||
|
|
||||||
|
mod_gesture := gtk.NewGestureClick()
|
||||||
|
mod_gesture.SetButton(3)
|
||||||
|
|
||||||
|
popover := gtk.NewPopover()
|
||||||
|
popover.SetHasArrow(false)
|
||||||
|
popover.SetParent(userbox)
|
||||||
|
|
||||||
|
rc_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
bb := gtk.NewButtonWithLabel("Ban")
|
||||||
|
kb := gtk.NewButtonWithLabel("Kick")
|
||||||
|
ab := gtk.NewButtonWithLabel("Set affil")
|
||||||
|
rb := gtk.NewButtonWithLabel("Set role")
|
||||||
|
|
||||||
|
kb.ConnectClicked(func() {
|
||||||
|
client.SendRaw(fmt.Sprintf(`
|
||||||
|
<iq from='%s'
|
||||||
|
id='kick1'
|
||||||
|
to='%s'
|
||||||
|
type='set'>
|
||||||
|
<query xmlns='http://jabber.org/protocol/muc#admin'>
|
||||||
|
<item nick='%s' role='none'>
|
||||||
|
</item>
|
||||||
|
</query>
|
||||||
|
</iq>
|
||||||
|
`, clientroot.Session.BindJid, jid, JidMustParse(u.From).Resource))
|
||||||
|
})
|
||||||
|
|
||||||
|
bb.ConnectClicked(func() {
|
||||||
|
var mu MucUser
|
||||||
|
ok = u.Get(&mu)
|
||||||
|
if ok {
|
||||||
|
if mu.MucUserItem.JID != "" {
|
||||||
|
client.SendRaw(fmt.Sprintf(`
|
||||||
|
<iq from='%s'
|
||||||
|
id='ban1'
|
||||||
|
to='%s'
|
||||||
|
type='set'>
|
||||||
|
<query xmlns='http://jabber.org/protocol/muc#admin'>
|
||||||
|
<item affiliation='outcast' jid='%s'/>
|
||||||
|
</query>
|
||||||
|
</iq>
|
||||||
|
`, clientroot.Session.BindJid, jid, JidMustParse(mu.MucUserItem.JID).Bare()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ab.ConnectClicked(func() {
|
||||||
|
var mu MucUser
|
||||||
|
ok = u.Get(&mu)
|
||||||
|
if ok {
|
||||||
|
if mu.MucUserItem.JID != "" {
|
||||||
|
win := gtk.NewWindow()
|
||||||
|
win.SetDefaultSize(400, 1)
|
||||||
|
win.SetResizable(false)
|
||||||
|
|
||||||
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
box.Append(gtk.NewLabel("Set " + JidMustParse(u.From).Resource + "'s affiliation"))
|
||||||
|
|
||||||
|
the_entry := gtk.NewEntry()
|
||||||
|
the_entry.SetText(mu.MucUserItem.Affiliation)
|
||||||
|
|
||||||
|
submit := gtk.NewButtonWithLabel("Submit")
|
||||||
|
submit.ConnectClicked(func() {
|
||||||
|
client.SendRaw(fmt.Sprintf(`
|
||||||
|
<iq from='%s'
|
||||||
|
id='ba1'
|
||||||
|
to='%s'
|
||||||
|
type='set'>
|
||||||
|
<query xmlns='http://jabber.org/protocol/muc#admin'>
|
||||||
|
<item affiliation='%s' jid='%s'/>
|
||||||
|
</query>
|
||||||
|
</iq>
|
||||||
|
`, clientroot.Session.BindJid, jid, the_entry.Text(), JidMustParse(mu.MucUserItem.JID).Bare()))
|
||||||
|
win.SetVisible(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
box.Append(the_entry)
|
||||||
|
box.Append(submit)
|
||||||
|
|
||||||
|
win.SetChild(box)
|
||||||
|
win.SetVisible(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
rb.ConnectClicked(func() {
|
||||||
|
var mu MucUser
|
||||||
|
ok = u.Get(&mu)
|
||||||
|
if ok {
|
||||||
|
if mu.MucUserItem.JID != "" {
|
||||||
|
win := gtk.NewWindow()
|
||||||
|
win.SetDefaultSize(400, 1)
|
||||||
|
win.SetResizable(false)
|
||||||
|
|
||||||
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
box.Append(gtk.NewLabel("Set " + JidMustParse(u.From).Resource + "'s role"))
|
||||||
|
box.Append(gtk.NewLabel("Important: if you want this to be permanent, set their affiliation instead"))
|
||||||
|
|
||||||
|
the_entry := gtk.NewEntry()
|
||||||
|
the_entry.SetText(mu.MucUserItem.Role)
|
||||||
|
|
||||||
|
submit := gtk.NewButtonWithLabel("Submit")
|
||||||
|
submit.ConnectClicked(func() {
|
||||||
|
|
||||||
|
client.SendRaw(fmt.Sprintf(`
|
||||||
|
<iq from='%s'
|
||||||
|
id='kick1'
|
||||||
|
to='%s'
|
||||||
|
type='set'>
|
||||||
|
<query xmlns='http://jabber.org/protocol/muc#admin'>
|
||||||
|
<item nick='%s' role='%s'>
|
||||||
|
</item>
|
||||||
|
</query>
|
||||||
|
</iq>
|
||||||
|
`, clientroot.Session.BindJid, jid, JidMustParse(u.From).Resource, the_entry.Text()))
|
||||||
|
})
|
||||||
|
|
||||||
|
box.Append(the_entry)
|
||||||
|
box.Append(submit)
|
||||||
|
|
||||||
|
win.SetChild(box)
|
||||||
|
win.SetVisible(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
rc_box.Append(bb)
|
||||||
|
rc_box.Append(kb)
|
||||||
|
rc_box.Append(ab)
|
||||||
|
rc_box.Append(rb)
|
||||||
|
|
||||||
|
popover.SetChild(rc_box)
|
||||||
|
|
||||||
|
mod_gesture.Connect("pressed", func(n_press, x, y int) {
|
||||||
|
rect := gdk.NewRectangle(x, y, 1, 1)
|
||||||
|
popover.SetPointingTo(&rect)
|
||||||
|
popover.Popup()
|
||||||
|
})
|
||||||
|
|
||||||
gesture.Connect("pressed", func(n_press, x, y int) {
|
gesture.Connect("pressed", func(n_press, x, y int) {
|
||||||
win := gtk.NewWindow()
|
win := gtk.NewWindow()
|
||||||
win.SetDefaultSize(400, 400)
|
win.SetDefaultSize(400, 400)
|
||||||
profile_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
profile_box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
nick := gtk.NewLabel(JidMustParse(u.From).Resource)
|
nick := gtk.NewLabel(JidMustParse(u.From).Resource)
|
||||||
|
ver_text := gtk.NewLabel("Getting version...")
|
||||||
|
ver_text.AddCSSClass("visitor")
|
||||||
|
|
||||||
win.SetTitle(JidMustParse(u.From).Resource)
|
win.SetTitle(JidMustParse(u.From).Resource)
|
||||||
nick.AddCSSClass("author")
|
nick.AddCSSClass("author")
|
||||||
profile_box.Append(nick)
|
profile_box.Append(nick)
|
||||||
profile_box.Append(gtk.NewLabel(u.From))
|
profile_box.Append(ver_text)
|
||||||
|
fr := gtk.NewLabel(u.From)
|
||||||
|
fr.AddCSSClass("jid")
|
||||||
|
profile_box.Append(fr)
|
||||||
|
profile_box.Append(ver_text)
|
||||||
|
|
||||||
iqResp, err := stanza.NewIQ(stanza.Attrs{
|
iqResp, err := stanza.NewIQ(stanza.Attrs{
|
||||||
Type: "get",
|
Type: "get",
|
||||||
@@ -129,7 +288,9 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
ok := u.Get(&hats)
|
ok := u.Get(&hats)
|
||||||
if ok {
|
if ok {
|
||||||
for _, hat := range hats.Hats {
|
for _, hat := range hats.Hats {
|
||||||
profile_box.Append(gtk.NewLabel(hat.Title))
|
l := gtk.NewLabel(hat.Title)
|
||||||
|
l.AddCSSClass("hat")
|
||||||
|
profile_box.Append(l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,12 +298,61 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
ok = u.Get(&mu)
|
ok = u.Get(&mu)
|
||||||
if ok {
|
if ok {
|
||||||
if mu.MucUserItem.JID != "" {
|
if mu.MucUserItem.JID != "" {
|
||||||
profile_box.Append(gtk.NewLabel(mu.MucUserItem.JID))
|
ji := (gtk.NewLabel(mu.MucUserItem.JID))
|
||||||
|
ji.AddCSSClass("jid")
|
||||||
|
profile_box.Append(ji)
|
||||||
}
|
}
|
||||||
profile_box.Append(gtk.NewLabel("Connected with role " + mu.MucUserItem.Role))
|
profile_box.Append(gtk.NewLabel("Connected with role " + mu.MucUserItem.Role))
|
||||||
profile_box.Append(gtk.NewLabel("Affiliated as " + mu.MucUserItem.Affiliation))
|
profile_box.Append(gtk.NewLabel("Affiliated as " + mu.MucUserItem.Affiliation))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
fmt.Println("Attempting to get Disco info")
|
||||||
|
|
||||||
|
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() {
|
go func() {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
mychan, err := client.SendIQ(ctx, iqResp)
|
mychan, err := client.SendIQ(ctx, iqResp)
|
||||||
@@ -155,9 +365,18 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
version := ver.Version
|
version := ver.Version
|
||||||
os := ver.OS
|
os := ver.OS
|
||||||
|
|
||||||
profile_box.Append(gtk.NewLabel(name))
|
vr := fmt.Sprintf("%s %s %s", name, version, os)
|
||||||
profile_box.Append(gtk.NewLabel(version))
|
if name == "" && version == "" && os == "" {
|
||||||
profile_box.Append(gtk.NewLabel(os))
|
ver_text.SetText("Client responded with empty version")
|
||||||
|
} else {
|
||||||
|
ver_text.SetText(vr)
|
||||||
|
ver_text.RemoveCSSClass("visitor")
|
||||||
|
}
|
||||||
|
} else if result.Error != nil && result.Error.Type != "" {
|
||||||
|
ver_text.SetText("Got error trying to get version")
|
||||||
|
ver_text.SetTooltipText(result.Error.Reason + ": " + result.Error.Text)
|
||||||
|
ver_text.RemoveCSSClass("visitor")
|
||||||
|
ver_text.AddCSSClass("error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -166,7 +385,7 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
mo, _ := mucmembers.Load(JidMustParse(u.From).Bare())
|
mo, _ := mucmembers.Load(JidMustParse(u.From).Bare())
|
||||||
mm := mo.(mucUnit)
|
mm := mo.(mucUnit)
|
||||||
mmm := mm.Members
|
mmm := mm.Members
|
||||||
mmmm, ok := mmm.Load(ocu.ID)
|
mmmm, ok := mmm.Load(id)
|
||||||
if ok {
|
if ok {
|
||||||
pres := mmmm.(stanza.Presence)
|
pres := mmmm.(stanza.Presence)
|
||||||
|
|
||||||
@@ -176,18 +395,18 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
im := getAvatar(u.From, vu.Photo)
|
im := getAvatar(u.From, vu.Photo)
|
||||||
im.SetPixelSize(80)
|
im.SetPixelSize(80)
|
||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
profile_box.Append(im)
|
profile_box.Prepend(im)
|
||||||
} else {
|
} else {
|
||||||
im := newImageFromPath("debug.png")
|
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
im.SetPixelSize(80)
|
im.SetPixelSize(80)
|
||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
profile_box.Append(im)
|
profile_box.Prepend(im)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
im := newImageFromPath("debug.png")
|
im := gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
im.SetPixelSize(80)
|
im.SetPixelSize(80)
|
||||||
im.AddCSSClass("author_img")
|
im.AddCSSClass("author_img")
|
||||||
profile_box.Append(im)
|
profile_box.Prepend(im)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -195,12 +414,32 @@ func switchToTab(jid string, w *gtk.Window) {
|
|||||||
win.SetTransientFor(win)
|
win.SetTransientFor(win)
|
||||||
win.Present()
|
win.Present()
|
||||||
})
|
})
|
||||||
userbox.AddController(gesture)
|
|
||||||
|
|
||||||
gen.Append(userbox)
|
userbox.AddController(gesture)
|
||||||
|
userbox.AddController(mod_gesture)
|
||||||
|
|
||||||
|
if mu.MucUserItem.Role == "moderator" {
|
||||||
|
gen.Prepend(userbox)
|
||||||
|
} else {
|
||||||
|
gen.Append(userbox)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
headerBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
if i >= 500 {
|
||||||
|
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["world"]))
|
||||||
|
} else if i >= 50 {
|
||||||
|
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["large_group"]))
|
||||||
|
} else {
|
||||||
|
headerBox.Append(gtk.NewImageFromPaintable(clientAssets["group"]))
|
||||||
|
}
|
||||||
|
headerBox.Append(gtk.NewLabel(fmt.Sprintf("%d participant(s)", i)))
|
||||||
|
gen.Prepend(headerBox)
|
||||||
|
|
||||||
|
muci := getAvatar(jid, jid)
|
||||||
|
muci.SetPixelSize(80)
|
||||||
|
gen.Prepend(muci)
|
||||||
memberList.SetChild(gen)
|
memberList.SetChild(gen)
|
||||||
} else {
|
} else {
|
||||||
memberList.SetChild(gtk.NewLabel(jid))
|
memberList.SetChild(gtk.NewLabel(jid))
|
||||||
|
|||||||
123
gtk-message.go
@@ -6,8 +6,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
|
||||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jacoblockett/sanitizefilename"
|
"github.com/jacoblockett/sanitizefilename"
|
||||||
"github.com/jasonlovesdoggo/gopen"
|
"github.com/jasonlovesdoggo/gopen"
|
||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
||||||
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
presence, ok := p.(stanza.Presence)
|
presence, ok := p.(stanza.Presence)
|
||||||
if !ok {
|
if !ok {
|
||||||
return gtk.NewLabel("Unsupported message.")
|
return gtk.NewLabel("Unsupported message.")
|
||||||
@@ -29,14 +30,21 @@ func generatePresenceWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
ok := presence.Get(&mu)
|
ok := presence.Get(&mu)
|
||||||
if ok {
|
if ok {
|
||||||
if mu.MucUserItem.Affiliation == "outcast" {
|
if mu.MucUserItem.Affiliation == "outcast" {
|
||||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " has been banned!")
|
b.Append(gtk.NewImageFromPaintable(clientAssets["outcast"]))
|
||||||
|
b.Append(gtk.NewLabel(JidMustParse(presence.From).Resource + " has been banned by " + mu.MucUserItem.Actor.Nick + "!"))
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " left the MUC")
|
b.Append(gtk.NewImageFromPaintable(clientAssets["door_out"]))
|
||||||
|
b.Append(gtk.NewLabel(JidMustParse(presence.From).Resource))
|
||||||
} else {
|
} else {
|
||||||
return gtk.NewLabel(jid.MustParse(presence.From).Resourcepart() + " joined the MUC")
|
b.Append(gtk.NewImageFromPaintable(clientAssets["door_in"]))
|
||||||
|
b.Append(gtk.NewLabel(JidMustParse(presence.From).Resource))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.SetTooltipText(presence.Status)
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||||
@@ -49,9 +57,29 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
readmarker := Marker{}
|
readmarker := Marker{}
|
||||||
ok = m.Get(&readmarker)
|
ok = m.Get(&readmarker)
|
||||||
if ok {
|
if ok {
|
||||||
return gtk.NewLabel(fmt.Sprintf("%s has read to this point", JidMustParse(m.From).Resource))
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
b.Append(gtk.NewLabel(fmt.Sprintf("%s has read to this point", JidMustParse(m.From).Resource)))
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composing := stanza.StateComposing{}
|
||||||
|
ok = m.Get(&composing)
|
||||||
|
if ok {
|
||||||
|
b := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
b.Append(gtk.NewLabel(fmt.Sprintf("%s is typing...", JidMustParse(m.From).Resource)))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Error.Type != "" {
|
||||||
|
error_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
cancel_img := gtk.NewImageFromPaintable(clientAssets["cancel"])
|
||||||
|
error_label := gtk.NewLabel(m.Error.Text + ": ")
|
||||||
|
|
||||||
|
error_box.Append(cancel_img)
|
||||||
|
error_box.Append(error_label)
|
||||||
|
error_box.Append(gtk.NewLabel(m.Error.Reason))
|
||||||
|
return error_box
|
||||||
|
}
|
||||||
|
|
||||||
sid := StanzaID{}
|
sid := StanzaID{}
|
||||||
m.Get(&sid)
|
m.Get(&sid)
|
||||||
@@ -60,7 +88,6 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
gesture := gtk.NewGestureClick()
|
gesture := gtk.NewGestureClick()
|
||||||
gesture.SetButton(3) // Right click
|
gesture.SetButton(3) // Right click
|
||||||
|
|
||||||
|
|
||||||
popover := gtk.NewPopover()
|
popover := gtk.NewPopover()
|
||||||
popover.SetParent(mainBox)
|
popover.SetParent(mainBox)
|
||||||
popover.SetHasArrow(false)
|
popover.SetHasArrow(false)
|
||||||
@@ -74,7 +101,7 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
like.SetLabel(v)
|
like.SetLabel(v)
|
||||||
like.SetHExpand(true)
|
like.SetHExpand(true)
|
||||||
like.ConnectClicked(func() {
|
like.ConnectClicked(func() {
|
||||||
fmt.Println("licked") // TODO: Implement proper support for reactions via extension
|
fmt.Println("licked") // TODO: Implement proper support for reactions via extension
|
||||||
client.SendRaw(fmt.Sprintf(`
|
client.SendRaw(fmt.Sprintf(`
|
||||||
<message from='%s' to='%s' id='%s' type='%s'>
|
<message from='%s' to='%s' id='%s' type='%s'>
|
||||||
<reactions id='%s' xmlns='urn:xmpp:reactions:0'>
|
<reactions id='%s' xmlns='urn:xmpp:reactions:0'>
|
||||||
@@ -88,21 +115,11 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
|
|
||||||
rc_box.Append(reactions)
|
rc_box.Append(reactions)
|
||||||
|
|
||||||
if m.Type == stanza.MessageTypeGroupchat {
|
quote := gtk.NewButtonWithLabel("Quote")
|
||||||
moderate := gtk.NewButtonWithLabel("Moderate") // TODO: Implement proper support for moderations via extension
|
quote.ConnectClicked(func() {
|
||||||
moderate.ConnectClicked(func() {
|
message_en.SetText("> " + m.Body + "\n")
|
||||||
client.SendRaw(fmt.Sprintf(`
|
|
||||||
<iq type='set' to='%s' id='%s'>
|
|
||||||
<moderate id='%s' xmlns='urn:xmpp:message-moderate:1'>
|
|
||||||
<retract xmlns='urn:xmpp-message-retract:1'/>
|
|
||||||
<reason>Retracted</reason>
|
|
||||||
</moderate>
|
|
||||||
</iq>
|
|
||||||
`, jid.MustParse(m.From).Bare().String(), uuid.New().String(), sid.ID))
|
|
||||||
})
|
})
|
||||||
rc_box.Append(moderate)
|
rc_box.Append(quote)
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
popover.SetChild(rc_box)
|
popover.SetChild(rc_box)
|
||||||
|
|
||||||
@@ -114,32 +131,34 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
|
|
||||||
mainBox.AddController(gesture)
|
mainBox.AddController(gesture)
|
||||||
|
|
||||||
reply := Reply{}
|
|
||||||
ok = m.Get(&reply)
|
|
||||||
if ok {
|
|
||||||
replyBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
|
||||||
replyBox.Append(gtk.NewLabel("↱ " + jid.MustParse(reply.To).Resourcepart()))
|
|
||||||
mainBox.Append(replyBox)
|
|
||||||
}
|
|
||||||
|
|
||||||
ocu := OccupantID{}
|
ocu := OccupantID{}
|
||||||
|
|
||||||
m.Get(&ocu)
|
m.Get(&ocu)
|
||||||
|
//id := ocu.ID
|
||||||
|
// if id == "" {
|
||||||
|
id := JidMustParse(m.From).Resource
|
||||||
|
// }
|
||||||
|
|
||||||
authorBox := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
authorBox := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||||
|
|
||||||
contentBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
contentBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
// im := newImageFromPath("debug.png")
|
// im := newImageFromPath("debug.png")
|
||||||
|
|
||||||
// authorBox.Append(im)
|
// authorBox.Append(im)
|
||||||
|
|
||||||
al := gtk.NewLabel(jid.MustParse(m.From).Resourcepart())
|
n := jid.MustParse(m.From).Resourcepart()
|
||||||
|
if n == "" {
|
||||||
|
n = jid.MustParse(m.From).String()
|
||||||
|
}
|
||||||
|
al := gtk.NewLabel(n)
|
||||||
al.AddCSSClass("author")
|
al.AddCSSClass("author")
|
||||||
|
al.SetSelectable(true)
|
||||||
|
|
||||||
if m.Type == stanza.MessageTypeGroupchat {
|
if m.Type == stanza.MessageTypeGroupchat {
|
||||||
mo, _ := mucmembers.Load(jid.MustParse(m.From).Bare().String())
|
mo, _ := mucmembers.Load(jid.MustParse(m.From).Bare().String())
|
||||||
mm := mo.(mucUnit)
|
mm := mo.(mucUnit)
|
||||||
mmm := mm.Members
|
mmm := mm.Members
|
||||||
mmmm, ok := mmm.Load(ocu.ID)
|
mmmm, ok := mmm.Load(id)
|
||||||
if ok {
|
if ok {
|
||||||
pres := mmmm.(stanza.Presence)
|
pres := mmmm.(stanza.Presence)
|
||||||
|
|
||||||
@@ -166,13 +185,29 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
al.SetText(al.Text() + " whispers")
|
al.SetText(al.Text() + " whispers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
authorBox.Append(al)
|
authorBox.Append(al)
|
||||||
|
|
||||||
|
wxdc := XDCEl{}
|
||||||
|
ok = m.Get(&wxdc)
|
||||||
|
if ok {
|
||||||
|
authorBox.Append(gtk.NewLabel("🎮"))
|
||||||
|
}
|
||||||
|
|
||||||
mlabel := gtk.NewLabel(m.Body)
|
mlabel := gtk.NewLabel(m.Body)
|
||||||
|
if m.Body == "" {
|
||||||
|
mlabel.SetText("No body set")
|
||||||
|
mlabel.AddCSSClass("visitor")
|
||||||
|
}
|
||||||
mlabel.SetWrap(true)
|
mlabel.SetWrap(true)
|
||||||
mlabel.SetSelectable(true)
|
mlabel.SetSelectable(true)
|
||||||
mlabel.SetHAlign(gtk.AlignFill)
|
mlabel.SetHAlign(gtk.AlignFill)
|
||||||
|
|
||||||
|
mum := MucUser{}
|
||||||
|
ok = m.Get(&mum)
|
||||||
|
if ok {
|
||||||
|
mlabel.SetText(fmt.Sprintf("%s's affiliation has been changed to %s", mum.MucUserItem.JID, mum.MucUserItem.Affiliation))
|
||||||
|
}
|
||||||
|
|
||||||
contentBox.Append(mlabel)
|
contentBox.Append(mlabel)
|
||||||
|
|
||||||
mainBox.Append(authorBox)
|
mainBox.Append(authorBox)
|
||||||
@@ -195,6 +230,15 @@ func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
|||||||
authorBox.Append(mbtn)
|
authorBox.Append(mbtn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.Subject != "" {
|
||||||
|
subjectlabel := gtk.NewLabel(m.Subject)
|
||||||
|
subjectlabel.SetWrap(true)
|
||||||
|
subjectlabel.SetSelectable(true)
|
||||||
|
subjectlabel.SetHAlign(gtk.AlignFill)
|
||||||
|
subjectlabel.AddCSSClass("subject")
|
||||||
|
mainBox.Append(subjectlabel)
|
||||||
|
}
|
||||||
|
|
||||||
return mainBox
|
return mainBox
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,9 +248,10 @@ func getVAdjustment(scrolledWindow *gtk.ScrolledWindow) *gtk.Adjustment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shouldn't be here, and should probably be in xmpp-helpers or somewhere similar.
|
func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shouldn't be here, and should probably be in xmpp-helpers or somewhere similar.
|
||||||
|
oghash := hash
|
||||||
p, err := ensureCache()
|
p, err := ensureCache()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
return gtk.NewImageFromPaintable(clientAssets["FailedAvatar"])
|
||||||
}
|
}
|
||||||
|
|
||||||
if hash == "" {
|
if hash == "" {
|
||||||
@@ -214,6 +259,12 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou
|
|||||||
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, ok := invalidImages[hash]
|
||||||
|
if ok {
|
||||||
|
fmt.Println("Image is invalid")
|
||||||
|
return gtk.NewImageFromPaintable(clientAssets["FailedAvatar"])
|
||||||
|
}
|
||||||
|
|
||||||
hash = filepath.Join(p, sanitizefilename.Sanitize(hash))
|
hash = filepath.Join(p, sanitizefilename.Sanitize(hash))
|
||||||
|
|
||||||
_, err = os.ReadFile(hash)
|
_, err = os.ReadFile(hash)
|
||||||
@@ -247,8 +298,10 @@ func getAvatar(j, hash string) *gtk.Image { // TODO: This function probably shou
|
|||||||
}
|
}
|
||||||
|
|
||||||
base64_data := card.Photo.Binval
|
base64_data := card.Photo.Binval
|
||||||
if card.Photo.Binval == "" || (card.Photo.Type == "image/svg+xml" && runtime.GOOS == "windows") {
|
if card.Photo.Binval == "" || ((card.Photo.Type == "image/svg+xml" || card.Photo.Type == "image/webp") && (runtime.GOOS == "windows" || runtime.GOOS == "netbsd")) {
|
||||||
return gtk.NewImageFromPaintable(clientAssets["DefaultAvatar"])
|
fmt.Println("Blocking image")
|
||||||
|
invalidImages[oghash] = true
|
||||||
|
return gtk.NewImageFromPaintable(clientAssets["FailedAvatar"])
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := base64.StdEncoding.DecodeString(base64_data)
|
data, err := base64.StdEncoding.DecodeString(base64_data)
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
||||||
"os"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/diamondburned/gotk4/pkg/gio/v2"
|
"github.com/diamondburned/gotk4/pkg/gio/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
|
"math/rand/v2"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func randomClientResource() string {
|
||||||
|
chars := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZλ"
|
||||||
|
str := ""
|
||||||
|
for range 4 {
|
||||||
|
str = str + string(chars[rand.IntN(len(chars))])
|
||||||
|
}
|
||||||
|
|
||||||
|
return "lambda." + str
|
||||||
|
}
|
||||||
|
|
||||||
func dropToSignInPage(err error) {
|
func dropToSignInPage(err error) {
|
||||||
app := gtk.NewApplication("net.sunglocto.lambda.login", gio.ApplicationFlagsNone)
|
app := gtk.NewApplication("net.sunglocto.lambda.login", gio.ApplicationFlagsNone)
|
||||||
app.ConnectActivate(func() {
|
app.ConnectActivate(func() {
|
||||||
@@ -52,7 +62,6 @@ func dropToSignInPage(err error) {
|
|||||||
insecure_check.SetHAlign(gtk.AlignEnd)
|
insecure_check.SetHAlign(gtk.AlignEnd)
|
||||||
insecure_check.SetHExpand(true)
|
insecure_check.SetHExpand(true)
|
||||||
|
|
||||||
|
|
||||||
server_box.Append(server_label)
|
server_box.Append(server_label)
|
||||||
server_box.Append(server_entry)
|
server_box.Append(server_entry)
|
||||||
|
|
||||||
@@ -82,6 +91,8 @@ func dropToSignInPage(err error) {
|
|||||||
conf.Password = password_entry.Text()
|
conf.Password = password_entry.Text()
|
||||||
conf.Nick = nickname_entry.Text()
|
conf.Nick = nickname_entry.Text()
|
||||||
conf.Insecure = insecure_check.Active()
|
conf.Insecure = insecure_check.Active()
|
||||||
|
conf.JoinBookmarks = true
|
||||||
|
conf.Resource = randomClientResource()
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
e := toml.NewEncoder(&b)
|
e := toml.NewEncoder(&b)
|
||||||
@@ -93,7 +104,6 @@ func dropToSignInPage(err error) {
|
|||||||
}
|
}
|
||||||
os.WriteFile(filepath.Join(p, "lambda.toml"), b.Bytes(), 0644)
|
os.WriteFile(filepath.Join(p, "lambda.toml"), b.Bytes(), 0644)
|
||||||
|
|
||||||
|
|
||||||
window.SetVisible(false)
|
window.SetVisible(false)
|
||||||
main()
|
main()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|||||||
594
main.go
@@ -2,16 +2,19 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/gio/v2"
|
"github.com/diamondburned/gotk4/pkg/gio/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
"github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
"github.com/gen2brain/beeep"
|
||||||
_ "github.com/kr/pretty"
|
"github.com/go-analyze/charts"
|
||||||
|
"golang.org/x/net/html/charset"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
@@ -22,14 +25,22 @@ import (
|
|||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"math/rand/v2"
|
"github.com/kr/pretty"
|
||||||
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
"encoding/base64"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var loadedConfig lambdaConfig
|
var loadedConfig lambdaConfig
|
||||||
|
|
||||||
var empty_dialog *gtk.Label
|
var empty_dialog *gtk.Image
|
||||||
|
|
||||||
|
var connectionStatus *gtk.Label
|
||||||
|
var connectionIcon *gtk.Image
|
||||||
|
|
||||||
|
var mStatus *gtk.Label
|
||||||
|
var mIcon *gtk.Image
|
||||||
|
|
||||||
|
var pingStatus *gtk.Label
|
||||||
|
|
||||||
// var msgs *gtk.ListBox
|
// var msgs *gtk.ListBox
|
||||||
var content *gtk.Widgetter
|
var content *gtk.Widgetter
|
||||||
@@ -41,6 +52,7 @@ var current string
|
|||||||
var scroller *gtk.ScrolledWindow
|
var scroller *gtk.ScrolledWindow
|
||||||
var memberList *gtk.ScrolledWindow
|
var memberList *gtk.ScrolledWindow
|
||||||
var menu *gtk.Box
|
var menu *gtk.Box
|
||||||
|
var message_en *gtk.Entry
|
||||||
|
|
||||||
//go:embed style.css
|
//go:embed style.css
|
||||||
var styleCSS string
|
var styleCSS string
|
||||||
@@ -57,34 +69,13 @@ var mucmembers sync.Map
|
|||||||
// stores devices of users
|
// stores devices of users
|
||||||
var userdevices sync.Map
|
var userdevices sync.Map
|
||||||
|
|
||||||
//go:embed debug.png
|
var pingTimes = [][]float64{}
|
||||||
var defaultAvatarBytes []byte
|
|
||||||
var defaultAvatarB64 string = base64.StdEncoding.EncodeToString(defaultAvatarBytes)
|
|
||||||
|
|
||||||
//go:embed assets/owner.png
|
|
||||||
var ownerMedalBytes []byte
|
|
||||||
var ownerMedalB64 string = base64.StdEncoding.EncodeToString(ownerMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/admin.png
|
|
||||||
var adminMedalBytes []byte
|
|
||||||
var adminMedalB64 string = base64.StdEncoding.EncodeToString(adminMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/member.png
|
|
||||||
var memberMedalBytes []byte
|
|
||||||
var memberMedalB64 string = base64.StdEncoding.EncodeToString(memberMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/noaffiliation.png
|
|
||||||
var noneMedalBytes []byte
|
|
||||||
var noneMedalB64 string = base64.StdEncoding.EncodeToString(noneMedalBytes)
|
|
||||||
|
|
||||||
//go:embed assets/outcast.png
|
|
||||||
var outcastMedalBytes []byte
|
|
||||||
var outcastMedalB64 string = base64.StdEncoding.EncodeToString(outcastMedalBytes)
|
|
||||||
|
|
||||||
|
|
||||||
var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
|
var clientAssets map[string]gdk.Paintabler = make(map[string]gdk.Paintabler)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
beeep.AppName = "Lambda"
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for fn := range uiQueue {
|
for fn := range uiQueue {
|
||||||
glib.IdleAdd(func() bool {
|
glib.IdleAdd(func() bool {
|
||||||
@@ -95,56 +86,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
loader := gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
defaultAvatarData, _ := base64.StdEncoding.DecodeString(defaultAvatarB64)
|
|
||||||
loader.Write(defaultAvatarData)
|
|
||||||
loader.Close()
|
|
||||||
clientAssets["DefaultAvatar"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
ownerMedalData, _ := base64.StdEncoding.DecodeString(ownerMedalB64)
|
|
||||||
loader.Write(ownerMedalData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["owner"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
adminMedalData, _ := base64.StdEncoding.DecodeString(adminMedalB64)
|
|
||||||
loader.Write(adminMedalData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["admin"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
memberMedalData, _ := base64.StdEncoding.DecodeString(memberMedalB64)
|
|
||||||
loader.Write(memberMedalData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["member"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
noneMedalData, _ := base64.StdEncoding.DecodeString(noneMedalB64)
|
|
||||||
loader.Write(noneMedalData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["none"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
|
|
||||||
loader = gdkpixbuf.NewPixbufLoader()
|
|
||||||
|
|
||||||
outcastMedalData, _ := base64.StdEncoding.DecodeString(outcastMedalB64)
|
|
||||||
loader.Write(outcastMedalData)
|
|
||||||
loader.Close()
|
|
||||||
|
|
||||||
clientAssets["outcast"] = gdk.NewTextureForPixbuf(loader.Pixbuf())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
pingTimes = append(pingTimes, []float64{})
|
||||||
p, err := ensureConfig()
|
p, err := ensureConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -162,21 +107,23 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put 4 random characters at the end
|
if loadedConfig.Resource == "" {
|
||||||
chars := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZλ"
|
fmt.Println("Config resource is empty! Generating a random one")
|
||||||
str := ""
|
loadedConfig.Resource = randomClientResource()
|
||||||
for range 4 {
|
|
||||||
str = str + string(chars[rand.IntN(len(chars))])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config := xmpp.Config{
|
config := xmpp.Config{
|
||||||
TransportConfiguration: xmpp.TransportConfiguration{
|
TransportConfiguration: xmpp.TransportConfiguration{
|
||||||
Address: loadedConfig.Server,
|
Address: loadedConfig.Server,
|
||||||
|
CharsetReader: func(c string, input io.Reader) (io.Reader, error) {
|
||||||
|
return charset.NewReaderLabel(c, input)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Jid: loadedConfig.Username + "/lambda." + str,
|
Jid: loadedConfig.Username + "/" + loadedConfig.Resource,
|
||||||
Credential: xmpp.Password(loadedConfig.Password),
|
Credential: xmpp.Password(loadedConfig.Password),
|
||||||
Insecure: loadedConfig.Insecure,
|
Insecure: loadedConfig.Insecure,
|
||||||
StreamLogger: os.Stdout,
|
// StreamLogger: os.Stdout,
|
||||||
|
StreamManagementEnable: true,
|
||||||
}
|
}
|
||||||
router := xmpp.NewRouter()
|
router := xmpp.NewRouter()
|
||||||
|
|
||||||
@@ -208,8 +155,8 @@ func main() {
|
|||||||
{Var: "jabber:iq:version"},
|
{Var: "jabber:iq:version"},
|
||||||
{Var: "urn:xmpp:delegation:1"},
|
{Var: "urn:xmpp:delegation:1"},
|
||||||
{Var: "http://jabber.org/protocol/muc"},
|
{Var: "http://jabber.org/protocol/muc"},
|
||||||
{Var: "urn:xmpp:reply:0"},
|
|
||||||
{Var: "λ"},
|
{Var: "λ"},
|
||||||
|
{Var: "urn:xmpp:attention:0"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
iqResp.Payload = &payload
|
iqResp.Payload = &payload
|
||||||
@@ -241,23 +188,73 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e := stanza.PubSubEvent{}
|
||||||
|
ok = m.Get(&e)
|
||||||
|
if ok {
|
||||||
|
fmt.Println(e)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if m.Body == "" {
|
if m.Body == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
originator := jid.MustParse(m.From).Bare().String()
|
originator := JidMustParse(m.From).Bare()
|
||||||
|
mStatus.SetText(originator)
|
||||||
|
|
||||||
|
at := new(Attention)
|
||||||
|
ok = m.Get(at)
|
||||||
|
if ok {
|
||||||
|
beeep.Notify("Attention", fmt.Sprintf("%s: %s", JidMustParse(m.From).Resource, m.Body), commentBytes) // TODO: Use localpart if DM
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle mentions
|
||||||
|
for _, ext := range m.Extensions {
|
||||||
|
mention, ok := ext.(*Mention)
|
||||||
|
if ok {
|
||||||
|
pretty.Println(mention)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sc := new(SentCarbon)
|
||||||
|
ok = m.Get(sc)
|
||||||
|
if ok {
|
||||||
|
fm, ok := sc.Forwarded.Stanza.(stanza.Message)
|
||||||
|
if ok {
|
||||||
|
if JidMustParse(fm.From).Bare() == JidMustParse(m.From).Bare() {
|
||||||
|
p = sc.Forwarded.Stanza
|
||||||
|
m = sc.Forwarded.Stanza.(stanza.Message)
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintln("Impersonation: ", fm.From, m.From))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc := new(SentCarbon)
|
||||||
|
ok = m.Get(rc)
|
||||||
|
if ok {
|
||||||
|
fm, ok := rc.Forwarded.Stanza.(stanza.Message)
|
||||||
|
if ok {
|
||||||
|
if JidMustParse(fm.From).Bare() == JidMustParse(m.From).Bare() {
|
||||||
|
p = rc.Forwarded.Stanza
|
||||||
|
m = rc.Forwarded.Stanza.(stanza.Message)
|
||||||
|
} else {
|
||||||
|
panic(fmt.Sprintln("Impersonation: ", fm.From, m.From))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
glib.IdleAdd(func() {
|
glib.IdleAdd(func() {
|
||||||
//uiQueue <- func() {
|
//uiQueue <- func() {
|
||||||
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
ba, ok := generateMessageWidget(p).(*gtk.Box)
|
|
||||||
if ok {
|
|
||||||
b = ba
|
|
||||||
}
|
|
||||||
|
|
||||||
tab, ok := tabs.Load(originator)
|
tab, ok := tabs.Load(originator)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
typed_tab := tab.(*chatTab)
|
typed_tab := tab.(*chatTab)
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
@@ -266,6 +263,11 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println("Got message when the tab does not exist!")
|
fmt.Println("Got message when the tab does not exist!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ba, ok := generateMessageWidget(p).(*gtk.Box)
|
||||||
|
if ok {
|
||||||
|
b.Append(ba)
|
||||||
|
}
|
||||||
//}
|
//}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -276,7 +278,8 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if presence.Error != *new(stanza.Err) {
|
if presence.Error.Reason != "" {
|
||||||
|
beeep.Notify(fmt.Sprintf("%s : %s", presence.From, presence.Error.Reason), presence.Error.Text, cancelBytes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +290,10 @@ func main() {
|
|||||||
|
|
||||||
if ok { // This is a presence stanza from a user in a MUC
|
if ok { // This is a presence stanza from a user in a MUC
|
||||||
presence.Get(&ocu)
|
presence.Get(&ocu)
|
||||||
|
// id := ocu.ID
|
||||||
|
// if id == "" {
|
||||||
|
id := JidMustParse(presence.From).Resource
|
||||||
|
// }
|
||||||
from, _ := stanza.NewJid(presence.From)
|
from, _ := stanza.NewJid(presence.From)
|
||||||
muc := from.Bare()
|
muc := from.Bare()
|
||||||
_, ok = mucmembers.Load(muc)
|
_, ok = mucmembers.Load(muc)
|
||||||
@@ -302,13 +309,32 @@ func main() {
|
|||||||
typed_unit := unit.(mucUnit)
|
typed_unit := unit.(mucUnit)
|
||||||
|
|
||||||
if presence.Type != "unavailable" {
|
if presence.Type != "unavailable" {
|
||||||
typed_unit.Members.Store(ocu.ID, presence)
|
_, ok := typed_unit.Members.Load(id)
|
||||||
|
if !ok {
|
||||||
|
glib.IdleAdd(func() {
|
||||||
|
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
ba, ok := generatePresenceWidget(p).(*gtk.Box)
|
||||||
|
if ok {
|
||||||
|
b = ba
|
||||||
|
}
|
||||||
|
|
||||||
|
tab, ok := tabs.Load(muc)
|
||||||
|
typed_tab := tab.(*chatTab)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
typed_tab.msgs.Append(b)
|
||||||
|
scrollToBottomAfterUpdate(scroller)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Got message when the tab does not exist!")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
typed_unit.Members.Store(id, presence)
|
||||||
} else {
|
} else {
|
||||||
typed_unit.Members.Delete(ocu.ID)
|
typed_unit.Members.Delete(id)
|
||||||
glib.IdleAdd(func() {
|
glib.IdleAdd(func() {
|
||||||
//uiQueue <- func() {
|
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
b := gtk.NewLabel("")
|
ba, ok := generatePresenceWidget(p).(*gtk.Box)
|
||||||
ba, ok := generatePresenceWidget(p).(*gtk.Label)
|
|
||||||
if ok {
|
if ok {
|
||||||
b = ba
|
b = ba
|
||||||
}
|
}
|
||||||
@@ -322,7 +348,6 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println("Got message when the tab does not exist!")
|
fmt.Println("Got message when the tab does not exist!")
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,11 +363,14 @@ func main() {
|
|||||||
if ok {
|
if ok {
|
||||||
userdevices.Store(user, userUnit{})
|
userdevices.Store(user, userUnit{})
|
||||||
|
|
||||||
b := gtk.NewButtonWithLabel(user)
|
b := gtk.NewLabel(user)
|
||||||
b.ConnectClicked(func() {
|
gesture1 := gtk.NewGestureClick()
|
||||||
b.AddCSSClass("accent")
|
gesture1.SetButton(1)
|
||||||
|
gesture1.Connect("pressed", func() {
|
||||||
switchToTab(user, &window.Window)
|
switchToTab(user, &window.Window)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
b.AddController(gesture1)
|
||||||
menu.Append(b)
|
menu.Append(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,12 +392,14 @@ func main() {
|
|||||||
|
|
||||||
userdevices.Store(user, typed_unit)
|
userdevices.Store(user, typed_unit)
|
||||||
}
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
})
|
})
|
||||||
|
|
||||||
c, err := xmpp.NewClient(&config, router, func(err error) {
|
c, err := xmpp.NewClient(&config, router, func(err error) {
|
||||||
showErrorDialog(err)
|
connectionStatus.SetText(fmt.Sprintf("Disconnected: %s", err.Error()))
|
||||||
panic(err)
|
connectionIcon.SetFromPaintable(clientAssets["disconnect"])
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
showErrorDialog(err)
|
showErrorDialog(err)
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -379,21 +409,114 @@ func main() {
|
|||||||
|
|
||||||
cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) {
|
cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) {
|
||||||
fmt.Println("XMPP client connected")
|
fmt.Println("XMPP client connected")
|
||||||
/*
|
go func() {
|
||||||
*/
|
for {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
pingStatus.AddCSSClass("pending")
|
||||||
|
before := time.Now()
|
||||||
|
iq := new(stanza.IQ)
|
||||||
|
iq.From = clientroot.Session.BindJid
|
||||||
|
iq.To = iq.From
|
||||||
|
iq.Type = "get"
|
||||||
|
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
mychan, err := client.SendIQ(ctx, iq)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_ = <-mychan
|
||||||
|
|
||||||
|
pingStatus.RemoveCSSClass("pending")
|
||||||
|
delay := time.Since(before) / time.Millisecond
|
||||||
|
pingStatus.SetText(fmt.Sprintf("%d ms", delay))
|
||||||
|
pingTimes[0] = append(pingTimes[0], float64(delay))
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
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"])
|
||||||
|
// Enable carbons
|
||||||
|
client.SendRaw(fmt.Sprintf(
|
||||||
|
`<iq xmlns='jabber:client'
|
||||||
|
from='%s'
|
||||||
|
id='enable1'
|
||||||
|
type='set'>
|
||||||
|
<enable xmlns='urn:xmpp:carbons:2'/>
|
||||||
|
</iq>
|
||||||
|
`, clientroot.Session.BindJid))
|
||||||
|
|
||||||
|
// Join rooms in bookmarks
|
||||||
|
if loadedConfig.JoinBookmarks {
|
||||||
|
books, err := stanza.NewItemsRequest("", "urn:xmpp:bookmarks:1", 0)
|
||||||
|
if err == nil {
|
||||||
|
mychan, err := c.SendIQ(context.TODO(), books)
|
||||||
|
result := <-mychan
|
||||||
|
if err == nil {
|
||||||
|
res, ok := result.Payload.(*stanza.PubSubGeneric)
|
||||||
|
if ok {
|
||||||
|
for _, item := range res.Items.List {
|
||||||
|
go func() {
|
||||||
|
jid := item.Id
|
||||||
|
node := item.Any
|
||||||
|
autojoin := false
|
||||||
|
nick := loadedConfig.Nick
|
||||||
|
for _, attr := range node.Attrs {
|
||||||
|
if attr.Name.Local == "autojoin" {
|
||||||
|
autojoin = attr.Value == "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range node.Nodes {
|
||||||
|
if node.XMLName.Local == "nick" {
|
||||||
|
nick = node.Content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := tabs.Load(jid)
|
||||||
|
if !ok && autojoin {
|
||||||
|
err := joinMuc(client, clientroot.Session.BindJid, jid, nick)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
createTab(jid, true)
|
||||||
|
b := gtk.NewLabel(jid)
|
||||||
|
gesture1 := gtk.NewGestureClick()
|
||||||
|
gesture1.SetButton(1)
|
||||||
|
gesture1.Connect("pressed", func() {
|
||||||
|
switchToTab(jid, &window.Window)
|
||||||
|
})
|
||||||
|
|
||||||
|
b.AddController(gesture1)
|
||||||
|
menu.Append(b)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
go func() {
|
conc := func() {
|
||||||
time.Sleep(3 * time.Second)
|
time.Sleep(3 * time.Second)
|
||||||
|
connectionStatus.SetText("Connecting...")
|
||||||
|
connectionIcon.SetFromPaintable(clientAssets["hourglass"])
|
||||||
|
|
||||||
err = cm.Run()
|
err = cm.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
showErrorDialog(err)
|
fmt.Println(err.Error())
|
||||||
panic(err)
|
connectionStatus.SetText(fmt.Sprintf("Disconnected: %s", err.Error()))
|
||||||
|
connectionIcon.SetFromPaintable(clientAssets["disconnect"])
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
|
|
||||||
app := gtk.NewApplication("net.sunglocto.lambda", gio.ApplicationFlagsNone)
|
app := gtk.NewApplication("net.sunglocto.lambda", gio.ApplicationFlagsNone)
|
||||||
app.ConnectActivate(func() { activate(app) })
|
app.ConnectActivate(func() {
|
||||||
|
go conc()
|
||||||
|
activate(app)
|
||||||
|
})
|
||||||
|
|
||||||
if code := app.Run(os.Args); code > 0 {
|
if code := app.Run(os.Args); code > 0 {
|
||||||
os.Exit(code)
|
os.Exit(code)
|
||||||
@@ -413,6 +536,103 @@ func activate(app *gtk.Application) {
|
|||||||
fileMenu := gio.NewMenu()
|
fileMenu := gio.NewMenu()
|
||||||
fileMenu.Append("Join MUC", "app.join")
|
fileMenu.Append("Join MUC", "app.join")
|
||||||
fileMenu.Append("Start DM", "app.dm")
|
fileMenu.Append("Start DM", "app.dm")
|
||||||
|
fileMenu.Append("Destroy MUC", "app.destroymuc")
|
||||||
|
|
||||||
|
helpMenu := gio.NewMenu()
|
||||||
|
helpMenu.Append("About", "app.about")
|
||||||
|
|
||||||
|
aboutAction := gio.NewSimpleAction("about", nil)
|
||||||
|
aboutAction.ConnectActivate(func(p *glib.Variant) {
|
||||||
|
a := gtk.AboutDialog{}
|
||||||
|
a.SetVisible(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
destroymucAction := gio.NewSimpleAction("destroymuc", nil)
|
||||||
|
destroymucAction.ConnectActivate(func(p *glib.Variant) {
|
||||||
|
cur, ok := tabs.Load(current)
|
||||||
|
if ok {
|
||||||
|
cur := cur.(*chatTab)
|
||||||
|
if cur.isMuc {
|
||||||
|
win := gtk.NewWindow()
|
||||||
|
win.SetTitle("Destroy MUC")
|
||||||
|
win.SetDefaultSize(400, 1)
|
||||||
|
win.SetResizable(false)
|
||||||
|
|
||||||
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
box.Append(gtk.NewLabel("Are you sure? This MUC will be gone forever! (a very long time)"))
|
||||||
|
box.Append(gtk.NewLabel("If you wish to continue, type 'I understand'"))
|
||||||
|
cancel := gtk.NewButtonWithLabel("Cancel")
|
||||||
|
cancel.ConnectClicked(func() {
|
||||||
|
win.SetVisible(false)
|
||||||
|
})
|
||||||
|
en := gtk.NewEntry()
|
||||||
|
en.SetPlaceholderText("...")
|
||||||
|
|
||||||
|
submit := gtk.NewButtonWithLabel("Destroy")
|
||||||
|
submit.ConnectClicked(func() {
|
||||||
|
fmt.Println(en.Text())
|
||||||
|
if en.Text() == "I understand" {
|
||||||
|
cur, ok := tabs.Load(current)
|
||||||
|
if ok {
|
||||||
|
cur := cur.(*chatTab)
|
||||||
|
if cur.isMuc {
|
||||||
|
client.SendRaw(fmt.Sprintf(`
|
||||||
|
<iq from='%s'
|
||||||
|
id='begone'
|
||||||
|
to='%s'
|
||||||
|
type='set'>
|
||||||
|
<query xmlns='http://jabber.org/protocol/muc#owner'>
|
||||||
|
<destroy jid='%s'>
|
||||||
|
<reason>User requested</reason>
|
||||||
|
</destroy>
|
||||||
|
</query>
|
||||||
|
</iq>
|
||||||
|
`, clientroot.Session.BindJid, current, JidMustParse(clientroot.Session.BindJid).Bare()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
win.SetVisible(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
box.Append(en)
|
||||||
|
box.Append(submit)
|
||||||
|
box.Append(cancel)
|
||||||
|
|
||||||
|
mu, ok := mucmembers.Load(current)
|
||||||
|
if ok {
|
||||||
|
typed_mu := mu.(mucUnit)
|
||||||
|
typed_mu.Members.Range(func(k, v any) bool {
|
||||||
|
user, ok := v.(stanza.Presence)
|
||||||
|
if ok {
|
||||||
|
mu := MucUser{}
|
||||||
|
ok := user.Get(&mu)
|
||||||
|
if ok {
|
||||||
|
if mu.MucUserItem.JID != "" {
|
||||||
|
if JidMustParse(mu.MucUserItem.JID).Bare() == JidMustParse(clientroot.Session.BindJid).Bare() {
|
||||||
|
if mu.MucUserItem.Affiliation != "owner" {
|
||||||
|
box.Append(gtk.NewLabel("You are not an owner of this MUC and thus will most likely not be able to delete it"))
|
||||||
|
}
|
||||||
|
// return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic("not ok")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic("not ok")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
panic("not ok")
|
||||||
|
}
|
||||||
|
|
||||||
|
win.SetChild(box)
|
||||||
|
win.SetVisible(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
joinAction := gio.NewSimpleAction("join", nil)
|
joinAction := gio.NewSimpleAction("join", nil)
|
||||||
joinAction.ConnectActivate(func(p *glib.Variant) {
|
joinAction.ConnectActivate(func(p *glib.Variant) {
|
||||||
@@ -447,7 +667,8 @@ func activate(app *gtk.Application) {
|
|||||||
|
|
||||||
win := gtk.NewWindow()
|
win := gtk.NewWindow()
|
||||||
win.SetTitle("Join MUC")
|
win.SetTitle("Join MUC")
|
||||||
win.SetDefaultSize(200, 200)
|
win.SetDefaultSize(400, 1)
|
||||||
|
win.SetResizable(false)
|
||||||
win.SetChild(box)
|
win.SetChild(box)
|
||||||
|
|
||||||
btn.ConnectClicked(func() {
|
btn.ConnectClicked(func() {
|
||||||
@@ -460,11 +681,14 @@ func activate(app *gtk.Application) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createTab(t, true)
|
createTab(t, true)
|
||||||
b := gtk.NewButtonWithLabel(t)
|
b := gtk.NewLabel(t)
|
||||||
b.ConnectClicked(func() {
|
gesture1 := gtk.NewGestureClick()
|
||||||
b.AddCSSClass("accent")
|
gesture1.SetButton(1)
|
||||||
|
gesture1.Connect("pressed", func() {
|
||||||
switchToTab(t, &window.Window)
|
switchToTab(t, &window.Window)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
b.AddController(gesture1)
|
||||||
menu.Append(b)
|
menu.Append(b)
|
||||||
}
|
}
|
||||||
win.SetVisible(false)
|
win.SetVisible(false)
|
||||||
@@ -475,8 +699,11 @@ func activate(app *gtk.Application) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
app.AddAction(joinAction)
|
app.AddAction(joinAction)
|
||||||
|
app.AddAction(aboutAction)
|
||||||
|
app.AddAction(destroymucAction)
|
||||||
|
|
||||||
the_menu.AppendSubmenu("File", fileMenu)
|
the_menu.AppendSubmenu("File", fileMenu)
|
||||||
|
the_menu.AppendSubmenu("Help", helpMenu)
|
||||||
|
|
||||||
the_menuBar := gtk.NewPopoverMenuBarFromModel(the_menu)
|
the_menuBar := gtk.NewPopoverMenuBarFromModel(the_menu)
|
||||||
app.SetMenubar(gio.NewMenu())
|
app.SetMenubar(gio.NewMenu())
|
||||||
@@ -486,7 +713,8 @@ func activate(app *gtk.Application) {
|
|||||||
window.Window.SetDefaultSize(500, 500)
|
window.Window.SetDefaultSize(500, 500)
|
||||||
menu = gtk.NewBox(gtk.OrientationVertical, 0)
|
menu = gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
|
||||||
empty_dialog = gtk.NewLabel("You are not focused on any chats.")
|
empty_dialog = gtk.NewImageFromPaintable(clientAssets["disabled_logo"])
|
||||||
|
empty_dialog.SetPixelSize(100)
|
||||||
empty_dialog.SetVExpand(true)
|
empty_dialog.SetVExpand(true)
|
||||||
|
|
||||||
scroller = gtk.NewScrolledWindow()
|
scroller = gtk.NewScrolledWindow()
|
||||||
@@ -498,6 +726,99 @@ func activate(app *gtk.Application) {
|
|||||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
box.Append(the_menuBar)
|
box.Append(the_menuBar)
|
||||||
|
|
||||||
|
statBar := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
|
cBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
connectionIcon = gtk.NewImageFromPaintable((clientAssets["disconnect"]))
|
||||||
|
connectionIcon.AddCSSClass("icon")
|
||||||
|
connectionStatus = gtk.NewLabel("Disconnected")
|
||||||
|
|
||||||
|
cBox.Append(connectionIcon)
|
||||||
|
cBox.Append(connectionStatus)
|
||||||
|
|
||||||
|
statBar.Append(cBox)
|
||||||
|
|
||||||
|
mBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
|
gesture1 := gtk.NewGestureClick()
|
||||||
|
gesture1.SetButton(1)
|
||||||
|
gesture1.Connect("pressed", func() {
|
||||||
|
current = mStatus.Text()
|
||||||
|
switchToTab(current, &window.Window)
|
||||||
|
})
|
||||||
|
|
||||||
|
mIcon = gtk.NewImageFromPaintable((clientAssets["comment"]))
|
||||||
|
mIcon.AddCSSClass("icon")
|
||||||
|
mStatus = gtk.NewLabel("-")
|
||||||
|
mStatus.AddController(gesture1)
|
||||||
|
|
||||||
|
cBox.Append(mIcon)
|
||||||
|
cBox.Append(mStatus)
|
||||||
|
|
||||||
|
statBar.Append(mBox)
|
||||||
|
|
||||||
|
pBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
pBox.SetTooltipText("Ping between you and your XMPP server\nRight-click to see graph")
|
||||||
|
gesture := gtk.NewGestureClick()
|
||||||
|
gesture.SetButton(3)
|
||||||
|
gesture.Connect("pressed", func() {
|
||||||
|
opt := charts.NewLineChartOptionWithData(pingTimes)
|
||||||
|
opt.Title = charts.TitleOption{
|
||||||
|
Text: "Server latency",
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
opt.XAxis.Labels = []string{
|
||||||
|
// The 7 labels here match to the 7 values above
|
||||||
|
"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
|
||||||
|
}*/
|
||||||
|
opt.Legend = charts.LegendOption{
|
||||||
|
SeriesNames: []string{
|
||||||
|
"Ping (ms)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
opt.StrokeSmoothingTension = 0.9
|
||||||
|
|
||||||
|
p := charts.NewPainter(charts.PainterOptions{
|
||||||
|
Width: 600,
|
||||||
|
Height: 400,
|
||||||
|
})
|
||||||
|
err := p.LineChart(opt)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := p.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
loader := gdkpixbuf.NewPixbufLoader()
|
||||||
|
loader.Write(buf)
|
||||||
|
loader.Close()
|
||||||
|
|
||||||
|
i := gtk.NewPictureForPaintable(gdk.NewTextureForPixbuf(loader.Pixbuf()))
|
||||||
|
win := gtk.NewWindow()
|
||||||
|
win.SetDefaultSize(600, 400)
|
||||||
|
win.SetTitle("Server latency")
|
||||||
|
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||||
|
box.Append(i)
|
||||||
|
win.SetChild(box)
|
||||||
|
win.SetVisible(true)
|
||||||
|
})
|
||||||
|
pBox.AddController(gesture)
|
||||||
|
|
||||||
|
i := (gtk.NewImageFromPaintable(clientAssets["chart_bar"]))
|
||||||
|
i.AddCSSClass("icon")
|
||||||
|
pBox.Append(i)
|
||||||
|
pingStatus = gtk.NewLabel("...")
|
||||||
|
pBox.Append(pingStatus)
|
||||||
|
statBar.Append(pBox)
|
||||||
|
|
||||||
|
scrollerStatBar := gtk.NewScrolledWindow()
|
||||||
|
scrollerStatBar.SetChild(statBar)
|
||||||
|
box.Append(scrollerStatBar)
|
||||||
|
|
||||||
// scroller.SetChild(empty_dialog)
|
// scroller.SetChild(empty_dialog)
|
||||||
scroller.SetChild(empty_dialog)
|
scroller.SetChild(empty_dialog)
|
||||||
menu_scroll := gtk.NewScrolledWindow()
|
menu_scroll := gtk.NewScrolledWindow()
|
||||||
@@ -523,12 +844,15 @@ func activate(app *gtk.Application) {
|
|||||||
|
|
||||||
entry_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
entry_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||||
|
|
||||||
en := gtk.NewEntry()
|
oob_en := gtk.NewEntry()
|
||||||
en.SetPlaceholderText("Say something, what else are you gonna do here?")
|
oob_en.SetPlaceholderText("Embed URL")
|
||||||
|
|
||||||
|
message_en = gtk.NewEntry()
|
||||||
|
message_en.SetPlaceholderText("Say something, what else are you gonna do here?")
|
||||||
b := gtk.NewButtonWithLabel("Send")
|
b := gtk.NewButtonWithLabel("Send")
|
||||||
|
|
||||||
sendtxt := func() {
|
sendtxt := func() {
|
||||||
t := en.Text()
|
t := message_en.Text()
|
||||||
if t == "" {
|
if t == "" {
|
||||||
dialog := >k.AlertDialog{}
|
dialog := >k.AlertDialog{}
|
||||||
dialog.SetDetail("detail")
|
dialog.SetDetail("detail")
|
||||||
@@ -548,21 +872,45 @@ func activate(app *gtk.Application) {
|
|||||||
message_type = stanza.MessageTypeGroupchat
|
message_type = stanza.MessageTypeGroupchat
|
||||||
}
|
}
|
||||||
|
|
||||||
err := sendMessage(client, current, message_type, t, "", "")
|
exts := []stanza.MsgExtension{}
|
||||||
|
if oob_en.Text() != "" {
|
||||||
|
new_oob := new(stanza.OOB)
|
||||||
|
new_oob.URL = oob_en.Text()
|
||||||
|
exts = append(exts, new_oob)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(t, "@everyone") {
|
||||||
|
start := strings.Index(t, "@everyone")
|
||||||
|
end := start + len("@everyone")
|
||||||
|
|
||||||
|
new_mention := new(Mention)
|
||||||
|
new_mention.Type = "urn:xmpp:mentions:0#channel"
|
||||||
|
|
||||||
|
new_mention.Begin = start
|
||||||
|
new_mention.End = end
|
||||||
|
|
||||||
|
exts = append(exts, new_mention)
|
||||||
|
} else if strings.Contains(t, "@here") {
|
||||||
|
new_attention := new(Attention)
|
||||||
|
exts = append(exts, new_attention)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := sendMessage(client, current, message_type, t, "", "", exts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO: Show error message via GTK
|
panic(err) // TODO: Show error message via GTK
|
||||||
}
|
}
|
||||||
en.SetText("")
|
message_en.SetText("")
|
||||||
scrollToBottomAfterUpdate(scroller)
|
scrollToBottomAfterUpdate(scroller)
|
||||||
}
|
}
|
||||||
|
|
||||||
en.Connect("activate", sendtxt)
|
message_en.Connect("activate", sendtxt)
|
||||||
|
|
||||||
b.ConnectClicked(sendtxt)
|
b.ConnectClicked(sendtxt)
|
||||||
|
|
||||||
en.SetHExpand(true)
|
message_en.SetHExpand(true)
|
||||||
|
|
||||||
entry_box.Append(en)
|
entry_box.Append(oob_en)
|
||||||
|
entry_box.Append(message_en)
|
||||||
entry_box.Append(b)
|
entry_box.Append(b)
|
||||||
|
|
||||||
box.Append(entry_box)
|
box.Append(entry_box)
|
||||||
|
|||||||
BIN
please_wait.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
rsrc_windows_amd64.syso
Normal file
28
style.css
@@ -33,3 +33,31 @@
|
|||||||
.visitor {
|
.visitor {
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pending {
|
||||||
|
color: grey;
|
||||||
|
transition-duration: 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hat {
|
||||||
|
background-color: orange;
|
||||||
|
color: black;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subject {
|
||||||
|
color: lime;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jid {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: white;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|||||||
16
types.go
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
"mellium.im/xmpp/color"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,15 +12,18 @@ type chatTab struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type lambdaConfig struct {
|
type lambdaConfig struct {
|
||||||
Server string
|
Server string
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Resource string
|
||||||
Insecure bool
|
Password string
|
||||||
Nick string
|
Insecure bool
|
||||||
|
Nick string
|
||||||
|
JoinBookmarks bool
|
||||||
|
CVD color.CVD
|
||||||
}
|
}
|
||||||
|
|
||||||
type mucUnit struct {
|
type mucUnit struct {
|
||||||
// key: OccupantID
|
// key: Resource
|
||||||
// value: last user presence
|
// value: last user presence
|
||||||
Members sync.Map
|
Members sync.Map
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var lambda_version string = "0.1.0"
|
var lambda_version string = "26w11a"
|
||||||
|
|||||||
17
xmpp-attention.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implementation of XEP-0224: Attention
|
||||||
|
|
||||||
|
type Attention struct {
|
||||||
|
stanza.MsgExtension
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:attention:0 attention"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:attention:0", Local: "attention"}, Attention{})
|
||||||
|
}
|
||||||
22
xmpp-bookmarks.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implementation of XEP-0402: PEP Native Bookmarks
|
||||||
|
// https://xmpp.org/extensions/xep-0402.html
|
||||||
|
|
||||||
|
type Bookmark struct {
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:bookmarks:1 conference"`
|
||||||
|
Name string `xml:"name,attr,omitempty"`
|
||||||
|
Autojoin bool `xml:"autojoin,attr,omitempty"`
|
||||||
|
Nick string `xml:"nick,omitempty"`
|
||||||
|
Password string `xml:"password,omitempty"`
|
||||||
|
Extensions []any `xml:"extensions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{Space: "urn:xmpp:bookmarks:1", Local: "conference"}, Bookmark{})
|
||||||
|
}
|
||||||
26
xmpp-carbons.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Implementation of XEP-0280: Message Carbons
|
||||||
|
// https://xmpp.org/extensions/xep-0280.html
|
||||||
|
|
||||||
|
type ReceivedCarbon struct {
|
||||||
|
stanza.MsgExtension
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:carbons:2 received"`
|
||||||
|
Forwarded stanza.Forwarded
|
||||||
|
}
|
||||||
|
|
||||||
|
type SentCarbon struct {
|
||||||
|
stanza.MsgExtension
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:carbons:2 sent"`
|
||||||
|
Forwarded stanza.Forwarded
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:carbons:2", Local: "received"}, ReceivedCarbon{})
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:carbons:2", Local: "sent"}, SentCarbon{})
|
||||||
|
}
|
||||||
18
xmpp-hats.go
@@ -9,21 +9,21 @@ import (
|
|||||||
// https://xmpp.org/extensions/xep-0317.html
|
// https://xmpp.org/extensions/xep-0317.html
|
||||||
|
|
||||||
type Hats struct {
|
type Hats struct {
|
||||||
XMLName xml.Name `xml:"urn:xmpp:hats:0 hats"`
|
XMLName xml.Name `xml:"urn:xmpp:hats:0 hats"`
|
||||||
Hats []Hat `xml:"hat"`
|
Hats []Hat `xml:"hat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hat struct {
|
type Hat struct {
|
||||||
Title string `xml:"title,attr"`
|
Title string `xml:"title,attr"`
|
||||||
URI string `xml:"uri,attr"`
|
URI string `xml:"uri,attr"`
|
||||||
Hue string `xml:"hue,attr"`
|
Hue string `xml:"hue,attr"`
|
||||||
Lang string `xml:"xml:lang,attr"`
|
Lang string `xml:"xml:lang,attr"`
|
||||||
Badge Badge `xml:"badge"`
|
Badge Badge `xml:"badge"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Badge struct {
|
type Badge struct {
|
||||||
XMLName xml.Name `xml:"urn:example:badges badge"`
|
XMLName xml.Name `xml:"urn:example:badges badge"`
|
||||||
Level int `xml:"level,attr"`
|
Level int `xml:"level,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -9,15 +9,16 @@ import (
|
|||||||
// This file has small functions that can be used to do XMPP stuff without writing tons of boilerplate
|
// This file has small functions that can be used to do XMPP stuff without writing tons of boilerplate
|
||||||
|
|
||||||
// Basic message sender. Anything more complex should be written by hand
|
// Basic message sender. Anything more complex should be written by hand
|
||||||
func sendMessage(c xmpp.Sender, sendTo string, msgType stanza.StanzaType, body string, subject string, thread string) error {
|
func sendMessage(c xmpp.Sender, sendTo string, msgType stanza.StanzaType, body string, subject string, thread string, exts []stanza.MsgExtension) error {
|
||||||
m := stanza.Message{
|
m := stanza.Message{
|
||||||
Attrs: stanza.Attrs{
|
Attrs: stanza.Attrs{
|
||||||
To: sendTo,
|
To: sendTo,
|
||||||
Type: msgType,
|
Type: msgType,
|
||||||
},
|
},
|
||||||
Body: body,
|
Body: body,
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
Thread: thread,
|
Thread: thread,
|
||||||
|
Extensions: exts,
|
||||||
}
|
}
|
||||||
err := c.Send(m)
|
err := c.Send(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -50,7 +51,7 @@ func joinMuc(c xmpp.Sender, jid string, muc string, nick string) error {
|
|||||||
// jid MustParse but using gosrc's instead of mellium
|
// jid MustParse but using gosrc's instead of mellium
|
||||||
// This function will panic if its an invalid JID
|
// This function will panic if its an invalid JID
|
||||||
|
|
||||||
func JidMustParse(s string) (*stanza.Jid) {
|
func JidMustParse(s string) *stanza.Jid {
|
||||||
j, err := stanza.NewJid(s)
|
j, err := stanza.NewJid(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
23
xmpp-mentions.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Experimental implementation of XEP-XXXX: Explicit Mentions
|
||||||
|
// https://git.isekai.rocks/snit/protoxeps/tree/explicit-mentions.xml
|
||||||
|
|
||||||
|
type Mention struct {
|
||||||
|
stanza.MsgExtension
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:mentions:0 mention"`
|
||||||
|
URI string `xml:"uri,attr,omitempty"`
|
||||||
|
Begin int `xml:"begin,attr,omitempty"`
|
||||||
|
End int `xml:"end,attr,omitempty"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Target string `xml:"target,attr,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:mentions:0", Local: "mention"}, Mention{})
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
type MucUser struct {
|
type MucUser struct {
|
||||||
stanza.PresExtension
|
stanza.PresExtension
|
||||||
|
stanza.MsgExtension
|
||||||
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
||||||
MucUserItem MucUserItem `xml:"item,omitempty"`
|
MucUserItem MucUserItem `xml:"item,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -19,9 +20,16 @@ type MucUserItem struct {
|
|||||||
Affiliation string `xml:"affiliation,attr,omitempty"` // TODO: Use enum
|
Affiliation string `xml:"affiliation,attr,omitempty"` // TODO: Use enum
|
||||||
Role string `xml:"role,attr,omitempty"` // TODO: Use enum
|
Role string `xml:"role,attr,omitempty"` // TODO: Use enum
|
||||||
JID string `xml:"jid,attr,omitempty"`
|
JID string `xml:"jid,attr,omitempty"`
|
||||||
Reason string `xml:"reason,omitempty"`
|
Reason string `xml:"reason,omitempty"`
|
||||||
|
Actor Actor `xml:"actor,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Actor struct {
|
||||||
|
JID string `xml:"jid,attr"`
|
||||||
|
Nick string `xml:"nick,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{Space: "http://jabber.org/protocol/muc#user", Local: "x"}, MucUser{})
|
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{})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// Implementation of XEP-0461
|
|
||||||
// https://xmpp.org/extensions/xep-0461.html#business-id
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/xml"
|
|
||||||
"gosrc.io/xmpp/stanza"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Reply struct {
|
|
||||||
stanza.MsgExtension
|
|
||||||
XMLName xml.Name `xml:"urn:xmpp:reply:0 reply"`
|
|
||||||
To string `xml:"to,attr"`
|
|
||||||
ID string `xml:"id,attr"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:reply:0", Local: "reply"}, Reply{})
|
|
||||||
}
|
|
||||||
21
xmpp-webxdc.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
// Implementation of XEP-0491: WebXDC
|
||||||
|
// https://xmpp.org/extensions/xep-0491.html
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"gosrc.io/xmpp/stanza"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XDCEl struct {
|
||||||
|
stanza.MsgExtension
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:webxdc:0 x"`
|
||||||
|
Document string `xml:"document"`
|
||||||
|
Summary string `xml:"summary"`
|
||||||
|
Payload string `xml:"urn:xmpp:json:0 json"` // TODO: Independent JSOn container type (XEP-0335)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:webxdc:0", Local: "x"}, XDCEl{})
|
||||||
|
}
|
||||||