Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
bf8c4bd51e | |||
467ae7e108 | |||
a3203ef08c | |||
8e386ebf50 | |||
b8e271e0b9 | |||
![]() |
a144a065f4 | ||
5317400b47 | |||
69b726b130 | |||
![]() |
4136889709 | ||
612eccddf2 | |||
f9281c9075 | |||
![]() |
8c721af1f3 | ||
![]() |
4fd82f1ef9 | ||
![]() |
838c8b6eeb | ||
![]() |
255bc3749c | ||
![]() |
d466c297f0 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -24,8 +24,6 @@ profile.cov
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
go.sum
|
||||
|
||||
# env file
|
||||
.env
|
||||
pi.json
|
||||
|
54
README.md
54
README.md
@@ -1,6 +1,10 @@
|
||||
<center>
|
||||
<img src="https://github.com/sunglocto/pi/blob/255bc3749c089e3945871ddf19dd17d14a83f9ff/pi.png">
|
||||
</center>
|
||||
|
||||
# π
|
||||
[](https://github.com/sunglocto/pi/actions/workflows/go.yml)
|
||||
|
||||
## the XMPP client from hell
|
||||
|
||||
Experimental and extremely weird XMPP client made with Go. No solicitors.
|
||||
|
||||
@@ -14,17 +18,29 @@ pi is an extremely opinionated client. It aims to have as little extra windows a
|
||||
## διαμόρφωση
|
||||
(configuration)
|
||||
|
||||
In order to use pi, you currently have to create a `pi.json` file in the working directory of the executable. Here is how one looks like:
|
||||
When you launch pi, you will be greeted with a create account screen. You will then be able to enter your XMPP account details and then relaunch the application to log in.
|
||||
|
||||
If you want to add MUCs or DMs, you must configure the program. Here is the general idea:
|
||||
|
||||
```json
|
||||
{
|
||||
"Host":"example.com:5222",
|
||||
"User":"user@example.com",
|
||||
"Password":"123456",
|
||||
"DisplayName":"user",
|
||||
"NoTLS":false,
|
||||
"StartTLS":true,
|
||||
"Mucs":["room@muc.example.com"]
|
||||
"Login": {
|
||||
"Host": "example.com:5222",
|
||||
"User": "user@example.com",
|
||||
"Password": "123456",
|
||||
"DisplayName": "user",
|
||||
"NoTLS": false,
|
||||
"StartTLS": true,
|
||||
"Mucs": [
|
||||
"room1@group.example.com",
|
||||
"room2@group.example.com"
|
||||
]
|
||||
},
|
||||
"DMs": [
|
||||
"mike@example.com",
|
||||
"louis@example.com"
|
||||
],
|
||||
"Notifications": false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,6 +67,9 @@ go build .
|
||||
vim pi.json
|
||||
./pi
|
||||
```
|
||||
> Uh, Windows???
|
||||
|
||||
Eventually. Don't count on it.
|
||||
|
||||
Static executable snapshots are also provided for GNU/Linux systems.
|
||||
|
||||
@@ -58,3 +77,20 @@ Static executable snapshots are also provided for GNU/Linux systems.
|
||||
(usage)
|
||||
|
||||
TODO
|
||||
|
||||
# επιπλέον
|
||||
(extra)
|
||||
|
||||
Pi version numbers are the digits of Pi followed by a letter indicating the phase of development the program is in.
|
||||
|
||||
For example, the version string:
|
||||
|
||||
`3.14a`
|
||||
|
||||
Is the third version produced in the alpha phase.
|
||||
|
||||
The digits of Pi will reset back to `3` when moving to a new phase.
|
||||
|
||||
If the number gets too long, it will reset to one digit of 2π. Once that gets to long, it will be digits of 3π and etc.
|
||||
|
||||
Named after [Psi](https://github.com/psi-im/psi).
|
||||
|
33
go.mod
33
go.mod
@@ -7,12 +7,12 @@ require (
|
||||
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb
|
||||
github.com/mbaklor/fyne-catppuccin v0.0.2
|
||||
mellium.im/xmpp v0.22.0
|
||||
pain.agency/oasis-sdk v0.0.0-20250803100711-2ed1355344d4
|
||||
pain.agency/oasis-sdk v0.0.0-20250805052243-df6be3f9f629
|
||||
)
|
||||
|
||||
require (
|
||||
fyne.io/systray v1.11.0 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/catppuccin/go v0.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/fredbi/uri v1.1.0 // indirect
|
||||
@@ -22,31 +22,30 @@ require (
|
||||
github.com/fyne-io/image v0.1.1 // indirect
|
||||
github.com/fyne-io/oksvg v0.1.0 // indirect
|
||||
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 // indirect
|
||||
github.com/go-text/render v0.2.0 // indirect
|
||||
github.com/go-text/typesetting v0.2.1 // indirect
|
||||
github.com/go-text/typesetting v0.3.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hack-pad/go-indexeddb v0.3.2 // indirect
|
||||
github.com/hack-pad/safejs v0.1.0 // indirect
|
||||
github.com/hack-pad/safejs v0.1.1 // indirect
|
||||
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rymdport/portal v0.4.1 // indirect
|
||||
github.com/rymdport/portal v0.4.2 // indirect
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.8 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/image v0.24.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.37.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/tools v0.25.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.13 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/image v0.29.0 // indirect
|
||||
golang.org/x/mod v0.26.0 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/tools v0.35.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
mellium.im/reader v0.1.0 // indirect
|
||||
mellium.im/sasl v0.3.2 // indirect
|
||||
|
68
go.sum
68
go.sum
@@ -4,8 +4,8 @@ fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg=
|
||||
fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
|
||||
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb h1:2BazNmb/kwgqRdvE9L+NgW8sfoWGn3iy1Ox8R4+CSmc=
|
||||
fyne.io/x/fyne v0.0.0-20250418202416-58a230ad1acb/go.mod h1:u3LF1EkElytjOT8OHxft16trctGndF9qpsoH6YIDOUU=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
|
||||
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -26,24 +26,24 @@ github.com/fyne-io/oksvg v0.1.0 h1:7EUKk3HV3Y2E+qypp3nWqMXD7mum0hCw2KEGhI1fnBw=
|
||||
github.com/fyne-io/oksvg v0.1.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
|
||||
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
|
||||
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+xzxf1jTJKMKZw3H0swfWk9RpWbBbDK5+0=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728 h1:RkGhqHxEVAvPM0/R+8g7XRwQnHatO0KAuVcwHo8q9W8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20250301202403-da16c1255728/go.mod h1:SyRD8YfuKk+ZXlDqYiqe1qMSqjNgtHzBTG810KUagMc=
|
||||
github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc=
|
||||
github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU=
|
||||
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
||||
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
||||
github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4=
|
||||
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||
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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hack-pad/go-indexeddb v0.3.2 h1:DTqeJJYc1usa45Q5r52t01KhvlSN02+Oq+tQbSBI91A=
|
||||
github.com/hack-pad/go-indexeddb v0.3.2/go.mod h1:QvfTevpDVlkfomY498LhstjwbPW6QC4VC/lxYb0Kom0=
|
||||
github.com/hack-pad/safejs v0.1.0 h1:qPS6vjreAqh2amUqj4WNG1zIw7qlRQJ9K10eDKMCnE8=
|
||||
github.com/hack-pad/safejs v0.1.0/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
||||
github.com/hack-pad/safejs v0.1.1 h1:d5qPO0iQ7h2oVtpzGnLExE+Wn9AtytxIfltcS2b9KD8=
|
||||
github.com/hack-pad/safejs v0.1.1/go.mod h1:HdS+bKF1NrE72VoXZeWzxFOVQVUSqZJAG0xNCnb+Tio=
|
||||
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade h1:FmusiCI1wHw+XQbvL9M+1r/C3SPqKrmBaIOYwVfQoDE=
|
||||
github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade/go.mod h1:ZDXo8KHryOWSIqnsb/CiDq7hQUYryCgdVnxbj8tDG7o=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
|
||||
@@ -54,40 +54,40 @@ github.com/mbaklor/fyne-catppuccin v0.0.2 h1:yMNnYkmFwjKJkFQvCd1uNKZDs07ZC85wTke
|
||||
github.com/mbaklor/fyne-catppuccin v0.0.2/go.mod h1:ZBIy4dV1yMj+7oEaZYkXm5OfYESmXuPWwNcuUmD1Njo=
|
||||
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/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||
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/rymdport/portal v0.4.1 h1:2dnZhjf5uEaeDjeF/yBIeeRo6pNI2QAKm7kq1w/kbnA=
|
||||
github.com/rymdport/portal v0.4.1/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||
github.com/rymdport/portal v0.4.2 h1:7jKRSemwlTyVHHrTGgQg7gmNPJs88xkbKcIL3NlcmSU=
|
||||
github.com/rymdport/portal v0.4.2/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
|
||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
|
||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||
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.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
||||
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/image v0.29.0 h1:HcdsyR4Gsuys/Axh0rDEmlBmB68rW1U9BUdB3UVHsas=
|
||||
golang.org/x/image v0.29.0/go.mod h1:RVJROnf3SLK8d26OW91j4FrIHGbsJ8QnbEocVTOWQDA=
|
||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -101,5 +101,5 @@ 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=
|
||||
pain.agency/oasis-sdk v0.0.0-20250803100711-2ed1355344d4 h1:xnVzsKcBlIrGVBijWXDPy69AYjaWp1/pssURj2KZInk=
|
||||
pain.agency/oasis-sdk v0.0.0-20250803100711-2ed1355344d4/go.mod h1:H5t2HizGTrQbu+e2Q6YJcRzMOlP1kC52p+0a+N/efiQ=
|
||||
pain.agency/oasis-sdk v0.0.0-20250805052243-df6be3f9f629 h1:NE+Z2HQzc77nw7l7DsSDSi0x9l+YfLfXBYerK+GsPrQ=
|
||||
pain.agency/oasis-sdk v0.0.0-20250805052243-df6be3f9f629/go.mod h1:eyvDgfpHo+9bdB/AkMEMZ3ETeoSONTULVx9X4w9kGAU=
|
||||
|
283
main.go
283
main.go
@@ -2,45 +2,42 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"io"
|
||||
_ "io/fs"
|
||||
"log"
|
||||
"net/url"
|
||||
_ "net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/app"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
_ "fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/storage"
|
||||
_ "fyne.io/fyne/v2/storage"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
_ "fyne.io/x/fyne/theme"
|
||||
catppuccin "github.com/mbaklor/fyne-catppuccin"
|
||||
"mellium.im/xmpp/jid"
|
||||
"mellium.im/xmpp/muc"
|
||||
"mellium.im/xmpp/stanza"
|
||||
oasisSdk "pain.agency/oasis-sdk"
|
||||
)
|
||||
|
||||
var version string = "3a"
|
||||
var version string = "3.1a"
|
||||
|
||||
// by sunglocto
|
||||
// license AGPL
|
||||
|
||||
type Message struct {
|
||||
Author string
|
||||
Content string
|
||||
ID string
|
||||
ReplyID string
|
||||
Author string
|
||||
Content string
|
||||
ID string
|
||||
ReplyID string
|
||||
ImageURL string
|
||||
Raw oasisSdk.XMPPChatMessage
|
||||
Raw oasisSdk.XMPPChatMessage
|
||||
}
|
||||
|
||||
type MucTab struct {
|
||||
@@ -51,11 +48,21 @@ type MucTab struct {
|
||||
isMuc bool
|
||||
}
|
||||
|
||||
type piConfig struct {
|
||||
Login oasisSdk.LoginInfo
|
||||
DMs []string
|
||||
Notifications bool
|
||||
}
|
||||
|
||||
var config piConfig
|
||||
var login oasisSdk.LoginInfo
|
||||
var DMs []string
|
||||
|
||||
var chatTabs = make(map[string]*MucTab)
|
||||
var tabs *container.AppTabs
|
||||
var selectedId widget.ListItemID
|
||||
var replying bool = false
|
||||
var notifications bool = true
|
||||
var notifications bool
|
||||
var connection bool = true
|
||||
|
||||
type myTheme struct{}
|
||||
@@ -117,18 +124,19 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
||||
vbox := co.(*fyne.Container)
|
||||
author := vbox.Objects[0].(*widget.Label)
|
||||
content := vbox.Objects[1].(*widget.RichText)
|
||||
//image := vbox.Objects[2].(*canvas.Image)
|
||||
btn := vbox.Objects[2].(*widget.Button)
|
||||
btn.Hidden = true // Hide by default
|
||||
msgContent := tabData.Messages[i].Content
|
||||
if tabData.Messages[i].ImageURL != "" {
|
||||
btn.Hidden = false
|
||||
btn.OnTapped = func(){fyne.Do(func() {
|
||||
u, _ := storage.ParseURI(tabData.Messages[i].ImageURL)
|
||||
image := canvas.NewImageFromURI(u)
|
||||
image.FillMode = canvas.ImageFillOriginal
|
||||
dialog.ShowCustom("Image", "Close", image, w)
|
||||
})}
|
||||
btn.Hidden = false
|
||||
btn.OnTapped = func() {
|
||||
fyne.Do(func() {
|
||||
u, _ := storage.ParseURI(tabData.Messages[i].ImageURL)
|
||||
image := canvas.NewImageFromURI(u)
|
||||
image.FillMode = canvas.ImageFillOriginal
|
||||
dialog.ShowCustom("Image", "Close", image, w)
|
||||
})
|
||||
}
|
||||
}
|
||||
// Check if the message is a quote
|
||||
lines := strings.Split(msgContent, "\n")
|
||||
@@ -160,31 +168,84 @@ func addChatTab(isMuc bool, chatJid jid.JID, nick string) {
|
||||
tabs.Append(tabItem)
|
||||
}
|
||||
|
||||
func main() {
|
||||
login := oasisSdk.LoginInfo{}
|
||||
func dropToSignInPage(reason string) {
|
||||
a = app.New()
|
||||
w = a.NewWindow("Welcome to Pi")
|
||||
w.Resize(fyne.NewSize(500, 500))
|
||||
rt := widget.NewRichTextFromMarkdown("# Welcome to pi\nIt appears you do not have a valid account configured. Let's create one!")
|
||||
footer := widget.NewRichTextFromMarkdown(fmt.Sprintf("Reason for being dropped to the sign-in page:\n\n```%s```", reason))
|
||||
userEntry := widget.NewEntry()
|
||||
userEntry.SetPlaceHolder("Your JID")
|
||||
serverEntry := widget.NewEntry()
|
||||
serverEntry.SetPlaceHolder("Server and port")
|
||||
passwordEntry := widget.NewPasswordEntry()
|
||||
passwordEntry.SetPlaceHolder("Your Password")
|
||||
nicknameEntry := widget.NewEntry()
|
||||
nicknameEntry.SetPlaceHolder("Your Nickname")
|
||||
|
||||
DMs := []string{}
|
||||
userView := widget.NewFormItem("", userEntry)
|
||||
serverView := widget.NewFormItem("", serverEntry)
|
||||
passwordView := widget.NewFormItem("", passwordEntry)
|
||||
nicknameView := widget.NewFormItem("", nicknameEntry)
|
||||
items := []*widget.FormItem{
|
||||
serverView,
|
||||
userView,
|
||||
passwordView,
|
||||
nicknameView,
|
||||
}
|
||||
|
||||
btn := widget.NewButton("Create an account", func() {
|
||||
dialog.ShowForm("Create an account", "Create", "Dismiss", items, func(b bool) {
|
||||
if b {
|
||||
config := piConfig{}
|
||||
config.Login.Host = serverEntry.Text
|
||||
config.Login.User = userEntry.Text
|
||||
config.Login.Password = passwordEntry.Text
|
||||
config.Login.DisplayName = nicknameEntry.Text
|
||||
config.Notifications = true
|
||||
|
||||
bytes, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
|
||||
os.Create("pi.json")
|
||||
os.WriteFile("pi.json", bytes, os.FileMode(os.O_RDWR)) // TODO: See if this works on non-unix like systems
|
||||
a.SendNotification(fyne.NewNotification("Done", "Relaunch the application"))
|
||||
w.Close()
|
||||
}
|
||||
}, w)
|
||||
})
|
||||
btn2 := widget.NewButton("Close pi", func() {
|
||||
w.Close()
|
||||
})
|
||||
w.SetContent(container.NewVBox(rt, btn, btn2,footer))
|
||||
w.ShowAndRun()
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
config = piConfig{}
|
||||
|
||||
bytes, err := os.ReadFile("./pi.json")
|
||||
if err != nil {
|
||||
a = app.New()
|
||||
w = a.NewWindow("Error")
|
||||
w.Resize(fyne.NewSize(500, 500))
|
||||
dialog.ShowInformation("Error", fmt.Sprintf("Please make sure there is a file named pi.json in the same directory you are running this executable...\n%s", err.Error()), w)
|
||||
w.ShowAndRun()
|
||||
dropToSignInPage(err.Error())
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(bytes, &login)
|
||||
if err != nil {
|
||||
fyne.Do(func() {
|
||||
a = app.New()
|
||||
w = a.NewWindow("Error")
|
||||
w.Resize(fyne.NewSize(500, 500))
|
||||
dialog.ShowError(err, w)
|
||||
w.ShowAndRun()
|
||||
})
|
||||
|
||||
|
||||
err = json.Unmarshal(bytes, &config)
|
||||
if err != nil {
|
||||
dropToSignInPage(fmt.Sprintf("Your pi.json file is invalid:\n%s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
login = config.Login
|
||||
DMs = config.DMs
|
||||
notifications = config.Notifications
|
||||
|
||||
client, err := oasisSdk.CreateClient(
|
||||
&login,
|
||||
func(client *oasisSdk.XmppClient, msg *oasisSdk.XMPPChatMessage) {
|
||||
@@ -199,7 +260,7 @@ func main() {
|
||||
}
|
||||
var img string = ""
|
||||
if strings.Contains(str, "https://") {
|
||||
lines := strings.Split(str, " ")
|
||||
lines := strings.Split(str, "\n")
|
||||
for i, line := range lines {
|
||||
s := strings.Split(line, " ")
|
||||
for j, v := range s {
|
||||
@@ -220,11 +281,11 @@ func main() {
|
||||
replyID = msg.Reply.ID
|
||||
}
|
||||
myMessage := Message{
|
||||
Author: msg.From.Resourcepart(),
|
||||
Content: str,
|
||||
ID: msg.ID,
|
||||
ReplyID: replyID,
|
||||
Raw: *msg,
|
||||
Author: msg.From.Resourcepart(),
|
||||
Content: str,
|
||||
ID: msg.ID,
|
||||
ReplyID: replyID,
|
||||
Raw: *msg,
|
||||
ImageURL: img,
|
||||
}
|
||||
|
||||
@@ -238,6 +299,7 @@ func main() {
|
||||
}
|
||||
},
|
||||
func(client *oasisSdk.XmppClient, _ *muc.Channel, msg *oasisSdk.XMPPChatMessage) {
|
||||
var ImageID string = ""
|
||||
mucJidStr := msg.From.Bare().String()
|
||||
if tab, ok := chatTabs[mucJidStr]; ok {
|
||||
|
||||
@@ -248,13 +310,16 @@ func main() {
|
||||
}
|
||||
}
|
||||
if strings.Contains(str, "https://") {
|
||||
lines := strings.Split(str, " ")
|
||||
lines := strings.Split(str, "\n")
|
||||
for i, line := range lines {
|
||||
s := strings.Split(line, " ")
|
||||
for j, v := range s {
|
||||
_, err := url.Parse(v)
|
||||
if err == nil && strings.HasPrefix(v, "https://") {
|
||||
s[j] = fmt.Sprintf("[%s](%s)", v, v)
|
||||
if strings.HasSuffix(v, ".png") || strings.HasSuffix(v, ".jp") || strings.HasSuffix(v, ".webp") {
|
||||
ImageID = v
|
||||
}
|
||||
}
|
||||
}
|
||||
lines[i] = strings.Join(s, " ")
|
||||
@@ -275,6 +340,7 @@ func main() {
|
||||
ID: msg.ID,
|
||||
ReplyID: replyID,
|
||||
Raw: *msg,
|
||||
ImageURL: ImageID,
|
||||
}
|
||||
tab.Messages = append(tab.Messages, myMessage)
|
||||
fyne.Do(func() {
|
||||
@@ -286,7 +352,6 @@ func main() {
|
||||
}
|
||||
},
|
||||
func(_ *oasisSdk.XmppClient, from jid.JID, state oasisSdk.ChatState) {
|
||||
//fromStr := from.String()
|
||||
switch state {
|
||||
case oasisSdk.ChatStateActive:
|
||||
case oasisSdk.ChatStateComposing:
|
||||
@@ -367,22 +432,8 @@ func main() {
|
||||
client.ReplyToEvent(&m, text)
|
||||
return
|
||||
}
|
||||
var typ stanza.MessageType
|
||||
if isMuc {
|
||||
typ = stanza.GroupChatMessage
|
||||
} else {
|
||||
typ = stanza.ChatMessage
|
||||
}
|
||||
msg := oasisSdk.XMPPChatMessage{
|
||||
Message: stanza.Message{
|
||||
To: jid.MustParse(activeMucJid),
|
||||
Type: typ,
|
||||
},
|
||||
ChatMessageBody: oasisSdk.ChatMessageBody{
|
||||
Body: &text,
|
||||
},
|
||||
}
|
||||
err := client.Session.Encode(client.Ctx, msg)
|
||||
|
||||
err = client.SendText(jid.MustParse(activeMucJid), text)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
}
|
||||
@@ -402,11 +453,21 @@ func main() {
|
||||
entry.SetText("")
|
||||
})
|
||||
|
||||
|
||||
mit := fyne.NewMenuItem("about pi", func() {
|
||||
dialog.ShowInformation("about pi", fmt.Sprintf("the XMPP client from hell\n\npi is an experimental XMPP client\nwritten by Sunglocto in Go.\n\nVersion %s", version), w)
|
||||
})
|
||||
|
||||
reconnect := fyne.NewMenuItem("reconnect", func() {
|
||||
go func(){
|
||||
err := client.Connect()
|
||||
if err != nil {
|
||||
fyne.Do(func(){
|
||||
dialog.ShowError(err, w)
|
||||
})
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
mia := fyne.NewMenuItem("configure message view", func() {
|
||||
ch := widget.NewCheck("", func(b bool) {})
|
||||
ch2 := widget.NewCheck("", func(b bool) {})
|
||||
@@ -433,7 +494,23 @@ func main() {
|
||||
}
|
||||
}, w)
|
||||
})
|
||||
mib := fyne.NewMenuItem("Join a room", func() {
|
||||
|
||||
jtb := fyne.NewMenuItem("jump to bottom", func() {
|
||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
selectedScroller.ScrollToBottom()
|
||||
})
|
||||
|
||||
jtt := fyne.NewMenuItem("jump to top", func() {
|
||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
selectedScroller.ScrollToTop()
|
||||
})
|
||||
/*mib := fyne.NewMenuItem("Join a room", func() {
|
||||
nickEntry := widget.NewEntry()
|
||||
nickEntry.SetText(login.DisplayName)
|
||||
roomEntry := widget.NewEntry()
|
||||
@@ -460,27 +537,59 @@ func main() {
|
||||
addChatTab(true, roomJid, nick)
|
||||
}
|
||||
}, w)
|
||||
})
|
||||
})*/
|
||||
|
||||
mic := fyne.NewMenuItem("upload a file", func() {
|
||||
var link string
|
||||
var bytes []byte
|
||||
var toperr error
|
||||
var topreader fyne.URIReadCloser
|
||||
dialog.ShowFileOpen(func(reader fyne.URIReadCloser, err error) {
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
}
|
||||
bytes, err := io.ReadAll(reader)
|
||||
link, err := client.UploadFileFromBytes(reader.URI().String(), bytes)
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
if reader == nil {
|
||||
return
|
||||
}
|
||||
bytes, toperr = io.ReadAll(reader)
|
||||
topreader = reader
|
||||
|
||||
if toperr != nil {
|
||||
dialog.ShowError(toperr, w)
|
||||
return
|
||||
}
|
||||
|
||||
progress := make(chan oasisSdk.UploadProgress)
|
||||
myprogressbar := widget.NewProgressBar()
|
||||
dialog.ShowCustom("Uploading file", "Hide", myprogressbar, w)
|
||||
go func() {
|
||||
|
||||
client.UploadFileFromBytes(client.Ctx, topreader.URI().Name(), bytes, progress)
|
||||
}()
|
||||
for update := range progress {
|
||||
myprogressbar.Value = float64(update.Percentage)
|
||||
myprogressbar.Refresh()
|
||||
|
||||
if update.Error != nil {
|
||||
dialog.ShowError(update.Error, w)
|
||||
return
|
||||
}
|
||||
|
||||
if update.GetURL != "" {
|
||||
link = update.GetURL
|
||||
}
|
||||
}
|
||||
|
||||
a.Clipboard().SetContent(link)
|
||||
dialog.ShowInformation("file successfully uploaded\nURL copied to your clipboard", link, w)
|
||||
|
||||
}, w)
|
||||
})
|
||||
|
||||
menu_help := fyne.NewMenu("π", mit)
|
||||
menu_changeroom := fyne.NewMenu("β", mib, mic)
|
||||
menu_configureview := fyne.NewMenu("γ", mia, mis)
|
||||
menu_help := fyne.NewMenu("π", mit, reconnect)
|
||||
menu_changeroom := fyne.NewMenu("β", mic)
|
||||
menu_configureview := fyne.NewMenu("γ", mia, mis, jtt, jtb)
|
||||
bit := fyne.NewMenuItem("mark selected message as read", func() {
|
||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
||||
if !ok {
|
||||
@@ -501,7 +610,35 @@ func main() {
|
||||
bia := fyne.NewMenuItem("toggle replying to message", func() {
|
||||
replying = !replying
|
||||
})
|
||||
menu_messageoptions := fyne.NewMenu("Σ", bit, bia)
|
||||
|
||||
bic := fyne.NewMenuItem("show message XML", func() {
|
||||
pre := widget.NewLabel("")
|
||||
|
||||
selectedScroller, ok := tabs.Selected().Content.(*widget.List)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var activeChatJid string
|
||||
for jid, tabData := range chatTabs {
|
||||
if tabData.Scroller == selectedScroller {
|
||||
activeChatJid = jid
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
m := chatTabs[activeChatJid].Messages[selectedId].Raw
|
||||
bytes, err := xml.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
dialog.ShowError(err, w)
|
||||
return
|
||||
}
|
||||
pre.SetText(string(bytes))
|
||||
pre.Selectable = true
|
||||
pre.Refresh()
|
||||
dialog.ShowCustom("Message", "Close", pre, w)
|
||||
})
|
||||
menu_messageoptions := fyne.NewMenu("Σ", bit, bia, bic)
|
||||
ma := fyne.NewMainMenu(menu_help, menu_changeroom, menu_configureview, menu_messageoptions)
|
||||
w.SetMainMenu(ma)
|
||||
|
||||
|
Reference in New Issue
Block a user