initial commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
*.dll
|
||||
lambda.toml
|
||||
lambda-im
|
||||
lambda-im.exe
|
||||
*.exe
|
||||
101
cache.go
Normal file
101
cache.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
// This file caches images in memory so we are not loading them from disk every time we need them
|
||||
// It also does the same for images that need to be grabbed from HTTP.
|
||||
|
||||
import (
|
||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// global or app-level map/cache
|
||||
var textureCache = make(map[string]gdk.Paintabler)
|
||||
|
||||
|
||||
|
||||
func getTexture(path string) gdk.Paintabler {
|
||||
if tex, exists := textureCache[path]; exists {
|
||||
return tex
|
||||
}
|
||||
tex, err := gdk.NewTextureFromFilename(path) // load once
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
textureCache[path] = tex
|
||||
return tex
|
||||
}
|
||||
|
||||
func newPictureFromPath(path string) *gtk.Picture {
|
||||
tex := getTexture(path)
|
||||
img := gtk.NewPictureForPaintable(tex)
|
||||
return img
|
||||
}
|
||||
|
||||
func newImageFromPath(path string) *gtk.Image {
|
||||
tex := getTexture(path)
|
||||
img := gtk.NewImageFromPaintable(tex)
|
||||
return img
|
||||
}
|
||||
|
||||
func newPictureFromWeb(url string) *gtk.Picture {
|
||||
// step 1: get a sha256 sum of the URL
|
||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||
|
||||
p, ok := textureCache[sum]
|
||||
if ok {
|
||||
return gtk.NewPictureForPaintable(p)
|
||||
}
|
||||
|
||||
// step 2: download it
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// step 3: save it
|
||||
err = os.WriteFile(sum, b, 0644)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return newPictureFromPath(sum)
|
||||
}
|
||||
|
||||
func newImageFromWeb(url string) *gtk.Image {
|
||||
// step 1: get a sha256 sum of the URL
|
||||
sum := fmt.Sprintf("%x", sha256.Sum256([]byte(url)))
|
||||
|
||||
p, ok := textureCache[sum]
|
||||
if ok {
|
||||
return gtk.NewImageFromPaintable(p)
|
||||
}
|
||||
|
||||
// step 2: download it
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// step 3: save it
|
||||
err = os.WriteFile(sum, b, 0644)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return newImageFromPath(sum)
|
||||
}
|
||||
26
go.mod
Normal file
26
go.mod
Normal file
@@ -0,0 +1,26 @@
|
||||
module lambda-im
|
||||
|
||||
go 1.25.6
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.6.0
|
||||
github.com/diamondburned/gotk4/pkg v0.3.1
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030
|
||||
github.com/sqweek/dialog v0.0.0-20260123140253-64c163d53aac
|
||||
gosrc.io/xmpp v0.5.1
|
||||
mellium.im/xmpp v0.22.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KarpelesLab/weak v0.1.1 // indirect
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 // indirect
|
||||
mellium.im/reader v0.1.0 // indirect
|
||||
mellium.im/xmlstream v0.15.4 // indirect
|
||||
nhooyr.io/websocket v1.6.5 // indirect
|
||||
)
|
||||
142
go.sum
Normal file
142
go.sum
Normal file
@@ -0,0 +1,142 @@
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
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/go.mod h1:pzXsWs5f2bf+fpgHayTlBE1qJpO3MpJKo5sRaLu1XNw=
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf h1:FPsprx82rdrX2jiKyS17BH6IrTmUBYqZa/CXT4uvb+I=
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
|
||||
github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=
|
||||
github.com/chromedp/cdproto v0.0.0-20190614062957-d6d2f92b486d/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
|
||||
github.com/chromedp/cdproto v0.0.0-20190621002710-8cbd498dd7a0/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw=
|
||||
github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
|
||||
github.com/chromedp/cdproto v0.0.0-20190926234355-1b4886c6fad6/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0=
|
||||
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/davecgh/go-spew v1.1.0/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/go.mod h1:DqeOW+MxSZFg9OO+esk4JgQk0TiUJJUBfMltKhG+ub4=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/fatih/color v1.6.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/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/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/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030 h1:NFCJG3BerP/5ZLXwu08x9xDs+9p7AYFMeo5IXjGANxw=
|
||||
github.com/jasonlovesdoggo/gopen v0.0.0-20250130105607-39c98c645030/go.mod h1:+YdGDBjXJho3QTsEntqzdm0YaiALOsz3sL6b67QLC8M=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
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.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
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/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.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/sqweek/dialog v0.0.0-20260123140253-64c163d53aac h1:/QqP+ajFMma4hNWQyBDVaQQhz9Z1kDyXScNWMO3owx0=
|
||||
github.com/sqweek/dialog v0.0.0-20260123140253-64c163d53aac/go.mod h1:/qNPSY91qTz/8TgHEMioAUc6q7+3SOybeKczHMXFcXw=
|
||||
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/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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
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.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 h1:lGdhQUN/cnWdSH3291CUuxSEqc+AsGTiDxPP3r2J0l4=
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||
golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
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/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
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/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
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-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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.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-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-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190618155005-516e3c20635f/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-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
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/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gosrc.io/xmpp v0.5.1 h1:Rgrm5s2rt+npGggJH3HakQxQXR8ZZz3+QRzakRQqaq4=
|
||||
gosrc.io/xmpp v0.5.1/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
|
||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
|
||||
mellium.im/reader v0.1.0 h1:UUEMev16gdvaxxZC7fC08j7IzuDKh310nB6BlwnxTww=
|
||||
mellium.im/reader v0.1.0/go.mod h1:F+X5HXpkIfJ9EE1zHQG9lM/hO946iYAmU7xjg5dsQHI=
|
||||
mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
|
||||
mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
|
||||
mellium.im/xmlstream v0.15.4 h1:gLKxcWl4rLMUpKgtzrTBvr4OexPeO/edYus+uK3F6ZI=
|
||||
mellium.im/xmlstream v0.15.4/go.mod h1:yXaCW2++fmVO4L9piKVkyLDqnCmictVYF7FDQW8prb4=
|
||||
mellium.im/xmpp v0.22.0 h1:UthQVSwEAr7SNrmyc90c2ykGpVHxjn/3yw8Ey4+Im8s=
|
||||
mellium.im/xmpp v0.22.0/go.mod h1:WSjq12nhREFD88Vy/0WD6Q8inE8t6a8w7QjzwivWitw=
|
||||
mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
|
||||
nhooyr.io/websocket v1.6.5 h1:8TzpkldRfefda5JST+CnOH135bzVPz5uzfn/AF+gVKg=
|
||||
nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=
|
||||
38
gtk-helpers.go
Normal file
38
gtk-helpers.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
"github.com/sqweek/dialog"
|
||||
)
|
||||
|
||||
func scrollToBottomAfterUpdate(scrolledWindow *gtk.ScrolledWindow) {
|
||||
glib.IdleAdd(func() bool {
|
||||
vAdj := scrolledWindow.VAdjustment()
|
||||
vAdj.SetValue(vAdj.Upper() - vAdj.PageSize())
|
||||
return false // Return false to run only once
|
||||
})
|
||||
}
|
||||
|
||||
func createTab(jid string, isMuc bool) {
|
||||
_, ok := tabs[jid]
|
||||
if !ok {
|
||||
newTab := new(chatTab)
|
||||
newTab.isMuc = isMuc
|
||||
newTab.msgs = gtk.NewListBox()
|
||||
newTab.msgs.SetVExpand(true)
|
||||
newTab.msgs.SetShowSeparators(true)
|
||||
|
||||
newTab.msgs.Append(gtk.NewButtonWithLabel("Get past messages..."))
|
||||
tabs[jid] = newTab
|
||||
}
|
||||
}
|
||||
|
||||
func switchToTab(jid string) {
|
||||
current = jid
|
||||
scroller.SetChild(tabs[current].msgs)
|
||||
}
|
||||
|
||||
func showErrorDialog(err error) {
|
||||
dialog.Message("%s", err.Error()).Title("Error").Error()
|
||||
}
|
||||
112
gtk-message.go
Normal file
112
gtk-message.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
// This file contains the function that generates message widgets depending on message stanza
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
"mellium.im/xmpp/jid"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jasonlovesdoggo/gopen"
|
||||
)
|
||||
|
||||
func generateMessageWidget(p stanza.Packet) gtk.Widgetter {
|
||||
m, ok := p.(stanza.Message)
|
||||
if !ok {
|
||||
return gtk.NewLabel("Unsupported message.")
|
||||
}
|
||||
sid := StanzaID{}
|
||||
m.Get(&sid)
|
||||
|
||||
mainBox := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
gesture := gtk.NewGestureClick()
|
||||
gesture.SetButton(3) // Right click
|
||||
|
||||
vis := false
|
||||
reactions := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
reactions.SetVisible(false)
|
||||
|
||||
reaction := []string{"👍", "👎", "♥️", "🤣", "😭",}
|
||||
for _, v := range reaction {
|
||||
like := gtk.NewButton()
|
||||
like.SetLabel(v)
|
||||
like.SetHExpand(true)
|
||||
like.ConnectClicked(func() {
|
||||
fmt.Println("licked")
|
||||
client.SendRaw(fmt.Sprintf(`
|
||||
<message from='%s' to='%s' id='%s' type='%s'>
|
||||
<reactions id='%s' xmlns='urn:xmpp:reactions:0'>
|
||||
<reaction>%s</reaction>
|
||||
</reactions>
|
||||
</message>
|
||||
`, m.To, jid.MustParse(m.From).Bare().String(), uuid.New().String(), m.Type, sid.ID, v))
|
||||
})
|
||||
reactions.Append(like)
|
||||
}
|
||||
|
||||
gesture.Connect("pressed", func(n_press, x, y int) {
|
||||
if !vis {
|
||||
vis = true
|
||||
reactions.SetVisible(true)
|
||||
} else {
|
||||
vis = false
|
||||
reactions.SetVisible(false)
|
||||
}
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
authorBox := gtk.NewBox(gtk.OrientationHorizontal, 10)
|
||||
contentBox := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
im := newImageFromPath("debug.png")
|
||||
|
||||
im.SetPixelSize(40)
|
||||
authorBox.Append(im)
|
||||
al := gtk.NewLabel(jid.MustParse(m.From).Resourcepart())
|
||||
al.AddCSSClass("author")
|
||||
|
||||
authorBox.Append(al)
|
||||
mlabel := gtk.NewLabel(m.Body)
|
||||
// mlabel.SetMarkup(convertXEPToPango(m.Body))
|
||||
mlabel.SetWrap(true)
|
||||
mlabel.SetSelectable(true)
|
||||
mlabel.SetHAlign(gtk.AlignFill)
|
||||
|
||||
contentBox.Append(mlabel)
|
||||
|
||||
mainBox.Append(authorBox)
|
||||
mainBox.Append(contentBox)
|
||||
|
||||
mainBox.Append(reactions)
|
||||
|
||||
oob := stanza.OOB{}
|
||||
ok = m.Get(&oob)
|
||||
if ok {
|
||||
// media := newPictureFromWeb(oob.URL)
|
||||
// media.SetCanShrink(false)
|
||||
// mainBox.Append(media)
|
||||
// media.AddCSSClass("chat_image")
|
||||
mbtn := gtk.NewButtonWithLabel("🖼️")
|
||||
mbtn.ConnectClicked(func(){
|
||||
gopen.Open(oob.URL)
|
||||
})
|
||||
authorBox.Append(mbtn)
|
||||
}
|
||||
|
||||
return mainBox
|
||||
}
|
||||
|
||||
func getVAdjustment(scrolledWindow *gtk.ScrolledWindow) *gtk.Adjustment {
|
||||
val := scrolledWindow.ObjectProperty("vadjustment").(*gtk.Adjustment)
|
||||
return val
|
||||
}
|
||||
19
gtk-xmpp-markup.go
Normal file
19
gtk-xmpp-markup.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "regexp"
|
||||
)
|
||||
|
||||
// This file implements XEP-0393 partially by providing a function to get a Pango string.
|
||||
// https://xmpp.org/extensions/xep-0393.html
|
||||
|
||||
|
||||
func convertXEPToPango(text string) string {
|
||||
/* FIXME this RegEx causes certain strings to appear invisible
|
||||
text = regexp.MustCompile(`\*([^*]+)\*`).ReplaceAllString(text, "<b>$1</b>")
|
||||
text = regexp.MustCompile(`_([^_]+)_`).ReplaceAllString(text, "<i>$1</i>")
|
||||
text = regexp.MustCompile(`~([^~]+)~`).ReplaceAllString(text, "<s>$1</s>")
|
||||
text = regexp.MustCompile("`([^`]+)`").ReplaceAllString(text, "<tt>$1</tt>")
|
||||
*/
|
||||
return text
|
||||
}
|
||||
317
main.go
Normal file
317
main.go
Normal file
@@ -0,0 +1,317 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||
"github.com/diamondburned/gotk4/pkg/gio/v2"
|
||||
"github.com/diamondburned/gotk4/pkg/glib/v2"
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"gosrc.io/xmpp"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
"mellium.im/xmpp/jid"
|
||||
"time"
|
||||
|
||||
_ "embed"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var loadedConfig lambdaConfig
|
||||
|
||||
var empty_dialog *gtk.Label
|
||||
|
||||
// var msgs *gtk.ListBox
|
||||
var content *gtk.Widgetter
|
||||
|
||||
var tabs map[string]*chatTab = make(map[string]*chatTab)
|
||||
var current string
|
||||
|
||||
var scroller *gtk.ScrolledWindow
|
||||
|
||||
//go:embed style.css
|
||||
var styleCSS string
|
||||
var client xmpp.Sender
|
||||
var clientroot *xmpp.Client
|
||||
|
||||
var uiQueue = make(chan func(), 100)
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
for fn := range uiQueue {
|
||||
glib.IdleAdd(func() bool {
|
||||
fn()
|
||||
return false
|
||||
})
|
||||
time.Sleep(10 * time.Millisecond) // Small delay between updates
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
b, err := os.ReadFile("lambda.toml")
|
||||
if err != nil {
|
||||
showErrorDialog(err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
_, err = toml.Decode(string(b), &loadedConfig)
|
||||
|
||||
config := xmpp.Config{
|
||||
TransportConfiguration: xmpp.TransportConfiguration{
|
||||
Address: loadedConfig.Server,
|
||||
},
|
||||
Jid: loadedConfig.Username,
|
||||
Credential: xmpp.Password(loadedConfig.Password),
|
||||
Insecure: loadedConfig.Insecure,
|
||||
StreamLogger: os.Stdout,
|
||||
}
|
||||
router := xmpp.NewRouter()
|
||||
|
||||
router.NewRoute().IQNamespaces(stanza.NSDiscoInfo).HandlerFunc(func(s xmpp.Sender, p stanza.Packet) {
|
||||
iq, ok := p.(*stanza.IQ)
|
||||
if !ok || iq.Type != stanza.IQTypeGet {
|
||||
return
|
||||
}
|
||||
|
||||
iqResp, err := stanza.NewIQ(stanza.Attrs{Type: "result", From: iq.To, To: iq.From, Id: iq.Id})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
identity := stanza.Identity{
|
||||
Name: "Lambda",
|
||||
Category: "client", // TODO: Allow spoofing on user request
|
||||
Type: "pc",
|
||||
}
|
||||
payload := stanza.DiscoInfo{
|
||||
XMLName: xml.Name{
|
||||
Space: stanza.NSDiscoInfo,
|
||||
Local: "query",
|
||||
},
|
||||
Identity: []stanza.Identity{identity},
|
||||
Features: []stanza.Feature{
|
||||
{Var: stanza.NSDiscoInfo},
|
||||
{Var: stanza.NSDiscoItems},
|
||||
{Var: "jabber:iq:version"},
|
||||
{Var: "urn:xmpp:delegation:1"},
|
||||
{Var: "http://jabber.org/protocol/muc"},
|
||||
{Var: "urn:xmpp:reply:0"},
|
||||
},
|
||||
}
|
||||
iqResp.Payload = &payload
|
||||
s.Send(iqResp)
|
||||
})
|
||||
|
||||
router.NewRoute().IQNamespaces("jabber:iq:version").HandlerFunc(func(s xmpp.Sender, p stanza.Packet) {
|
||||
iq, ok := p.(*stanza.IQ)
|
||||
if !ok || iq.Type != stanza.IQTypeGet {
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
iqResp, err := stanza.NewIQ(stanza.Attrs{Type: "result", From: iq.To, To: iq.From, Id: iq.Id})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
v := &stanza.Version{}
|
||||
v = v.SetInfo("Lambda", "1.0", "Windows") // TODO: Allow spoofing and report correct information
|
||||
|
||||
iqResp.Payload = v
|
||||
s.Send(iqResp)
|
||||
})
|
||||
|
||||
router.HandleFunc("message", func(s xmpp.Sender, p stanza.Packet) {
|
||||
m, ok := p.(stanza.Message)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if m.Body == "" {
|
||||
return
|
||||
}
|
||||
|
||||
originator := jid.MustParse(m.From).Bare().String()
|
||||
|
||||
glib.IdleAdd(func() {
|
||||
uiQueue <- func() {
|
||||
b := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
ba, ok := generateMessageWidget(p).(*gtk.Box)
|
||||
if ok {
|
||||
b = ba
|
||||
}
|
||||
|
||||
_, ok = tabs[originator]
|
||||
|
||||
if ok {
|
||||
fmt.Println("valid")
|
||||
tabs[originator].msgs.Append(b)
|
||||
scrollToBottomAfterUpdate(scroller)
|
||||
} else {
|
||||
fmt.Println("Got message when the tab does not exist!")
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
c, err := xmpp.NewClient(&config, router, func(err error) {
|
||||
showErrorDialog(err)
|
||||
panic(err)
|
||||
})
|
||||
if err != nil {
|
||||
showErrorDialog(err)
|
||||
panic(err)
|
||||
}
|
||||
client = c
|
||||
clientroot = c
|
||||
|
||||
cm := xmpp.NewStreamManager(c, func(c xmpp.Sender) {
|
||||
fmt.Println("XMPP client connected")
|
||||
/*
|
||||
*/
|
||||
})
|
||||
|
||||
go func() {
|
||||
err = cm.Run()
|
||||
if err != nil {
|
||||
showErrorDialog(err)
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
app := gtk.NewApplication("net.sunglocto.lambda", gio.ApplicationFlagsNone)
|
||||
app.ConnectActivate(func() { activate(app) })
|
||||
|
||||
if code := app.Run(os.Args); code > 0 {
|
||||
os.Exit(code)
|
||||
}
|
||||
}
|
||||
|
||||
func activate(app *gtk.Application) {
|
||||
// Load the CSS and apply it globally.
|
||||
gtk.StyleContextAddProviderForDisplay(
|
||||
gdk.DisplayGetDefault(), loadCSS(styleCSS),
|
||||
gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
)
|
||||
|
||||
window := gtk.NewApplicationWindow(app)
|
||||
app.SetMenubar(gio.NewMenu())
|
||||
|
||||
window.SetTitle("Lambda")
|
||||
menu := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
/*
|
||||
f_menu := gtk.NewMenuButton()
|
||||
f_menu.SetLabel("File")
|
||||
f_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
e_menu := gtk.NewMenuButton()
|
||||
e_menu.SetLabel("Edit")
|
||||
e_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
v_menu := gtk.NewMenuButton()
|
||||
v_menu.SetLabel("View")
|
||||
v_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
b_menu := gtk.NewMenuButton()
|
||||
b_menu.SetLabel("Bookmarks")
|
||||
b_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
h_menu := gtk.NewMenuButton()
|
||||
h_menu.SetLabel("Help")
|
||||
h_menu.SetAlwaysShowArrow(false)
|
||||
|
||||
menu.Append(f_menu)
|
||||
menu.Append(e_menu)
|
||||
menu.Append(v_menu)
|
||||
menu.Append(b_menu)
|
||||
menu.Append(h_menu)
|
||||
*/
|
||||
|
||||
empty_dialog = gtk.NewLabel("You are not focused on any chats.")
|
||||
empty_dialog.SetVExpand(true)
|
||||
|
||||
scroller = gtk.NewScrolledWindow()
|
||||
|
||||
box := gtk.NewBox(gtk.OrientationVertical, 0)
|
||||
// scroller.SetChild(empty_dialog)
|
||||
scroller.SetChild(empty_dialog)
|
||||
menu_scroll := gtk.NewScrolledWindow()
|
||||
menu_scroll.SetChild(menu)
|
||||
box.Append(menu_scroll)
|
||||
box.Append(scroller)
|
||||
|
||||
entry_box := gtk.NewBox(gtk.OrientationHorizontal, 0)
|
||||
|
||||
en := gtk.NewEntry()
|
||||
en.SetPlaceholderText("Say something, what else are you gonna do here?")
|
||||
b := gtk.NewButtonWithLabel("Send")
|
||||
|
||||
sendtxt := func() {
|
||||
t := en.Text()
|
||||
if t == "" {
|
||||
dialog := >k.AlertDialog{}
|
||||
dialog.SetDetail("detail")
|
||||
dialog.SetMessage("message")
|
||||
dialog.SetButtons([]string{"yes, no"})
|
||||
dialog.Choose(context.TODO(), &window.Window, nil)
|
||||
}
|
||||
|
||||
err := sendMessage(client, current, stanza.MessageTypeGroupchat, t, "", "")
|
||||
if err != nil {
|
||||
panic(err) // TODO: Show error message via GTK
|
||||
}
|
||||
en.SetText("")
|
||||
scrollToBottomAfterUpdate(scroller)
|
||||
}
|
||||
|
||||
en.Connect("activate", sendtxt)
|
||||
|
||||
b.ConnectClicked(sendtxt)
|
||||
|
||||
en.SetHExpand(true)
|
||||
|
||||
entry_box.Append(en)
|
||||
entry_box.Append(b)
|
||||
|
||||
debug_btn := gtk.NewButtonWithLabel("Join muc")
|
||||
|
||||
debug_btn.ConnectClicked(func() {
|
||||
showErrorDialog(errors.New("test error"))
|
||||
t := en.Text()
|
||||
_, ok := tabs[t]
|
||||
if !ok {
|
||||
err := joinMuc(client, clientroot.Session.BindJid, t, loadedConfig.Nick)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
createTab(t, true)
|
||||
b := gtk.NewButtonWithLabel(t)
|
||||
b.ConnectClicked(func() {
|
||||
b.AddCSSClass("accent")
|
||||
switchToTab(t)
|
||||
})
|
||||
menu.Append(b)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
entry_box.Append(debug_btn)
|
||||
|
||||
box.Append(entry_box)
|
||||
|
||||
window.SetChild(box)
|
||||
|
||||
window.SetVisible(true)
|
||||
}
|
||||
|
||||
func loadCSS(content string) *gtk.CSSProvider {
|
||||
prov := gtk.NewCSSProvider()
|
||||
prov.LoadFromString(content)
|
||||
return prov
|
||||
}
|
||||
8
style.css
Normal file
8
style.css
Normal file
@@ -0,0 +1,8 @@
|
||||
.author {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
.chat_img {
|
||||
max-width: 50px;
|
||||
max-height: 50px;
|
||||
}
|
||||
19
types.go
Normal file
19
types.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||
)
|
||||
|
||||
type chatTab struct {
|
||||
isMuc bool
|
||||
msgs *gtk.ListBox
|
||||
}
|
||||
|
||||
type lambdaConfig struct {
|
||||
Server string
|
||||
Username string
|
||||
Password string
|
||||
Insecure bool
|
||||
Nick string
|
||||
}
|
||||
49
xmpp-helpers.go
Normal file
49
xmpp-helpers.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gosrc.io/xmpp"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
)
|
||||
|
||||
// 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
|
||||
func sendMessage(c xmpp.Sender, sendTo string, msgType stanza.StanzaType, body string, subject string, thread string) error {
|
||||
m := stanza.Message{
|
||||
Attrs: stanza.Attrs{
|
||||
To: sendTo,
|
||||
Type: msgType,
|
||||
},
|
||||
Body: body,
|
||||
Subject: subject,
|
||||
Thread: thread,
|
||||
}
|
||||
err := c.Send(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Joins a MUC
|
||||
func joinMuc(c xmpp.Sender, jid string, muc string, nick string) error{
|
||||
addr := muc + "/" + nick
|
||||
fmt.Println(addr)
|
||||
joinPresence := stanza.Presence{
|
||||
Attrs: stanza.Attrs{
|
||||
From: jid,
|
||||
To: addr,
|
||||
},
|
||||
Extensions: []stanza.PresExtension{
|
||||
&stanza.MucPresence{
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := client.Send(joinPresence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
26
xmpp-mucuser.go
Normal file
26
xmpp-mucuser.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
// Implementation of XEP-0045: Multi User Chat 7.2.2
|
||||
// https://xmpp.org/extensions/xep-0045.html#enter-pres
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
)
|
||||
|
||||
type MucUser struct {
|
||||
stanza.PresExtension
|
||||
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
||||
MucUserItem MucUserItem `xml:"item,omitempty"`
|
||||
}
|
||||
|
||||
type MucUserItem struct {
|
||||
XMLName xml.Name
|
||||
Affiliation string `xml:"affiliation,attr,omitempty"` // TODO: Use enum
|
||||
Role string `xml:"role,attr,omitempty"` // TODO: Use enum
|
||||
JID string `xml:"jid,attr,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{Space: "http://jabber.org/protocol/muc#user", Local: "x"}, MucUser{})
|
||||
}
|
||||
21
xmpp-occupantid.go
Normal file
21
xmpp-occupantid.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
)
|
||||
|
||||
// Implementation of XEP-0421: Occupant identifiers for semi-anonymous MUCs
|
||||
// https://xmpp.org/extensions/xep-0421.html
|
||||
|
||||
type OccupantID struct {
|
||||
stanza.PresExtension
|
||||
stanza.MsgExtension
|
||||
XMLName xml.Name `xml:"urn:xmpp:occupant-id:0 occupant-id"`
|
||||
ID string `xml:"id,attr"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:occupant-id:0", Local: "occupant-id"}, OccupantID{})
|
||||
stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{Space: "urn:xmpp:occupant-id:0", Local: "occupant-id"}, OccupantID{})
|
||||
}
|
||||
20
xmpp-reply.go
Normal file
20
xmpp-reply.go
Normal file
@@ -0,0 +1,20 @@
|
||||
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{})
|
||||
}
|
||||
19
xmpp-sid.go
Normal file
19
xmpp-sid.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
)
|
||||
|
||||
// Implementation of XEP-0359: Unique and Stable Stanza IDs
|
||||
|
||||
type StanzaID struct {
|
||||
stanza.MsgExtension
|
||||
XMLName xml.Name `xml:"urn:xmpp:sid:0 stanza-id"`
|
||||
By string `xml:"by,attr"`
|
||||
ID string `xml:"id,attr"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{Space: "urn:xmpp:sid:0", Local: "stanza-id"}, StanzaID{})
|
||||
}
|
||||
Reference in New Issue
Block a user