forked from sunglocto/lambda
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