]> git.openfl.eu Git - va4s.git/commitdiff
initial discord voice join main
authorFl_GUI <flor.guilini@hotmail.com>
Fri, 14 Nov 2025 19:30:21 +0000 (20:30 +0100)
committerFl_GUI <flor.guilini@hotmail.com>
Fri, 14 Nov 2025 19:30:21 +0000 (20:30 +0100)
channelSelect/channelselect.go [new file with mode: 0644]
go.mod [new file with mode: 0644]
go.sum [new file with mode: 0644]
main.go [new file with mode: 0644]
sessionProvider/provider.go [new file with mode: 0644]
voiceActivity/activity.go [new file with mode: 0644]

diff --git a/channelSelect/channelselect.go b/channelSelect/channelselect.go
new file mode 100644 (file)
index 0000000..76b8802
--- /dev/null
@@ -0,0 +1,143 @@
+package channelselect
+
+import (
+       "context"
+       "fmt"
+       "sync"
+
+       "fyne.io/fyne/v2"
+       "fyne.io/fyne/v2/container"
+       "fyne.io/fyne/v2/layout"
+       "fyne.io/fyne/v2/widget"
+       "github.com/bwmarrin/discordgo"
+)
+
+type ChannelSelect struct {
+       Container *fyne.Container
+       OnVoice   func(*discordgo.VoiceConnection)
+}
+
+func NewChannelSelect(session *discordgo.Session) *ChannelSelect {
+       csel := new(ChannelSelect)
+
+       mapping, guilds, channels := allChannels(session)
+
+       tree := widget.NewTree(
+               func(id widget.TreeNodeID) []widget.TreeNodeID {
+                       channels, _ := mapping[id]
+                       return channels
+               },
+               func(id widget.TreeNodeID) bool {
+                       _, ok := mapping[id]
+                       return ok
+               },
+               func(isBranch bool) fyne.CanvasObject {
+                       if isBranch {
+                               return newGuildTemplate()
+                       } else {
+                               return newChannelTemplate()
+                       }
+               },
+               func(id widget.TreeNodeID, isBranch bool, obj fyne.CanvasObject) {
+                       if isBranch {
+                               updateGuildTemplate(guilds[id], obj)
+                       } else {
+                               ch := channels[id]
+                               onSelect := func() {
+                                       conn, err := session.ChannelVoiceJoin(
+                                               context.Background(),
+                                               ch.GuildID,
+                                               ch.ID,
+                                               true,
+                                               false,
+                                       )
+                                       if err != nil {
+                                               if _, ok := session.VoiceConnections[ch.GuildID]; !ok {
+                                                       panic(fmt.Errorf("Fl_GUI forgot to handle this error: %s", err))
+                                               }
+                                       }
+                                       csel.OnVoice(conn)
+                               }
+                               updateChannelTemplate(ch, obj, onSelect)
+                       }
+               },
+       )
+       csel.Container = container.NewMax(tree)
+       return csel
+}
+
+func newGuildTemplate() fyne.CanvasObject {
+       return widget.NewLabel("guild")
+}
+
+func updateGuildTemplate(guild *discordgo.UserGuild, obj fyne.CanvasObject) {
+       obj.(*widget.Label).Text = guild.Name
+}
+
+func newChannelTemplate() fyne.CanvasObject {
+       return container.NewHBox(
+               widget.NewLabel("channel"),
+               layout.NewSpacer(),
+               widget.NewButton("join", func() {}),
+       )
+}
+
+func updateChannelTemplate(channel *discordgo.Channel, obj fyne.CanvasObject, onSelect func()) {
+       container := obj.(*fyne.Container)
+       container.Objects[0].(*widget.Label).Text = channel.Name
+       container.Objects[2].(*widget.Button).OnTapped = onSelect
+}
+
+func allChannels(session *discordgo.Session) (channelStructure map[string][]string, guilds map[string]*discordgo.UserGuild, channels map[string]*discordgo.Channel) {
+       // get all guids
+       userguilds, err := session.UserGuilds(10, "", "", false)
+       if err != nil {
+               // better error handling
+               panic(fmt.Errorf("Fl_GUI forgot to handle this error: %s", err))
+       }
+
+       // get all channels
+       allChannels := make([][]*discordgo.Channel, len(userguilds))
+       var wg sync.WaitGroup
+       for i, guild := range userguilds {
+               wg.Add(1)
+               go func() {
+                       defer wg.Done()
+                       channels, err := session.GuildChannels(guild.ID)
+                       if err == nil {
+                               voiceChannels := make([]*discordgo.Channel, 0)
+                               for _, c := range channels {
+                                       if c.Type == discordgo.ChannelTypeGuildVoice {
+                                               voiceChannels = append(voiceChannels, c)
+                                       }
+                               }
+                               allChannels[i] = voiceChannels
+                       }
+               }()
+       }
+       wg.Wait()
+
+       // collect result
+       channelStructure = make(map[string][]string)
+       guilds = make(map[string]*discordgo.UserGuild)
+       channels = make(map[string]*discordgo.Channel)
+
+       guildIds := make([]string, len(userguilds))
+
+       for i, gld := range userguilds {
+               guildIds[i] = gld.ID
+               guilds[gld.ID] = gld
+               guildChannels := allChannels[i]
+               channelIds := make([]string, len(guildChannels))
+               for j, ch := range guildChannels {
+                       channels[ch.ID] = ch
+                       channelIds[j] = ch.ID
+               }
+               channelStructure[gld.ID] = channelIds
+       }
+
+       // "" is used as the root of a fyne/v2/widget.Tree
+       channelStructure[""] = guildIds
+
+       return
+}
diff --git a/go.mod b/go.mod
new file mode 100644 (file)
index 0000000..5e7d770
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,47 @@
+module va4s
+
+go 1.25.4
+
+require (
+       fyne.io/fyne/v2 v2.7.0
+       github.com/bwmarrin/discordgo v0.29.0
+)
+
+require (
+       fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 // indirect
+       github.com/BurntSushi/toml v1.5.0 // indirect
+       github.com/davecgh/go-spew v1.1.1 // indirect
+       github.com/fredbi/uri v1.1.1 // indirect
+       github.com/fsnotify/fsnotify v1.9.0 // indirect
+       github.com/fyne-io/gl-js v0.2.0 // indirect
+       github.com/fyne-io/glfw-js v0.3.0 // indirect
+       github.com/fyne-io/image v0.1.1 // indirect
+       github.com/fyne-io/oksvg v0.2.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-text/render v0.2.0 // indirect
+       github.com/go-text/typesetting v0.2.1 // indirect
+       github.com/godbus/dbus/v5 v5.1.0 // indirect
+       github.com/gorilla/websocket v1.4.2 // indirect
+       github.com/hack-pad/go-indexeddb v0.3.2 // indirect
+       github.com/hack-pad/safejs v0.1.0 // indirect
+       github.com/jeandeaual/go-locale v0.0.0-20250612000132-0ef82f21eade // indirect
+       github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
+       github.com/kr/text v0.1.0 // indirect
+       github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
+       github.com/nicksnyder/go-i18n/v2 v2.5.1 // indirect
+       github.com/pmezard/go-difflib v1.0.0 // 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.11.1 // indirect
+       github.com/yuin/goldmark v1.7.8 // indirect
+       golang.org/x/crypto v0.33.0 // indirect
+       golang.org/x/image v0.24.0 // indirect
+       golang.org/x/net v0.35.0 // indirect
+       golang.org/x/sys v0.30.0 // indirect
+       golang.org/x/text v0.22.0 // indirect
+       gopkg.in/yaml.v3 v3.0.1 // indirect
+)
+
+replace github.com/bwmarrin/discordgo v0.29.0 => github.com/ozraru/discordgo v0.26.2-0.20251101184423-6792228f3271
diff --git a/go.sum b/go.sum
new file mode 100644 (file)
index 0000000..02de60c
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,92 @@
+fyne.io/fyne/v2 v2.7.0 h1:GvZSpE3X0liU/fqstInVvRsaboIVpIWQ4/sfjDGIGGQ=
+fyne.io/fyne/v2 v2.7.0/go.mod h1:xClVlrhxl7D+LT+BWYmcrW4Nf+dJTvkhnPgji7spAwE=
+fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58 h1:eA5/u2XRd8OUkoMqEv3IBlFYSruNlXD8bRHDiqm0VNI=
+fyne.io/systray v1.11.1-0.20250603113521-ca66a66d8b58/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs=
+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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
+github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
+github.com/fredbi/uri v1.1.1 h1:xZHJC08GZNIUhbP5ImTHnt5Ya0T8FI2VAwI/37kh2Ko=
+github.com/fredbi/uri v1.1.1/go.mod h1:4+DZQ5zBjEwQCDmXW5JdIjz0PUA+yJbvtBv+u+adr5o=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fyne-io/gl-js v0.2.0 h1:+EXMLVEa18EfkXBVKhifYB6OGs3HwKO3lUElA0LlAjs=
+github.com/fyne-io/gl-js v0.2.0/go.mod h1:ZcepK8vmOYLu96JoxbCKJy2ybr+g1pTnaBDdl7c3ajI=
+github.com/fyne-io/glfw-js v0.3.0 h1:d8k2+Y7l+zy2pc7wlGRyPfTgZoqDf3AI4G+2zOWhWUk=
+github.com/fyne-io/glfw-js v0.3.0/go.mod h1:Ri6te7rdZtBgBpxLW19uBpp3Dl6K9K/bRaYdJ22G8Jk=
+github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
+github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
+github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
+github.com/fyne-io/oksvg v0.2.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-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-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/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/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+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/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=
+github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+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/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/ozraru/discordgo v0.26.2-0.20251101184423-6792228f3271 h1:iV0N6GNraEyE1hPNYZpq7eib4phq74P6SXH9+NEBOB8=
+github.com/ozraru/discordgo v0.26.2-0.20251101184423-6792228f3271/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
+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.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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+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.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
+golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
+golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
+golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+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=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/main.go b/main.go
new file mode 100644 (file)
index 0000000..bf1f58c
--- /dev/null
+++ b/main.go
@@ -0,0 +1,116 @@
+package main
+
+import (
+       "context"
+       channelselect "va4s/channelSelect"
+       "va4s/sessionProvider"
+       voiceactivity "va4s/voiceActivity"
+
+       "fyne.io/fyne/v2"
+       "fyne.io/fyne/v2/app"
+       "fyne.io/fyne/v2/container"
+       "fyne.io/fyne/v2/widget"
+       "github.com/bwmarrin/discordgo"
+)
+
+type Va4s struct {
+       app     fyne.App
+       window  fyne.Window
+       session *discordgo.Session
+       voice   *discordgo.VoiceConnection
+}
+
+func main() {
+       va4s := new(Va4s)
+       va4s.Init()
+
+       // #1 discord session provider
+       va4s.SetTokenEntry()
+
+       va4s.window.SetOnClosed(func() {
+               va4s.Close()
+       })
+
+       va4s.window.ShowAndRun()
+}
+
+func (v *Va4s) Init() {
+       v.app = app.New()
+       v.window = v.app.NewWindow("Voice activity 4 Sammi CORE")
+       v.window.SetContent(container.NewBorder(
+               container.NewStack(widget.NewButton("back", func() {})),
+               nil,
+               nil,
+               container.NewStack(),
+       ))
+}
+
+func (v *Va4s) setContent(obj fyne.CanvasObject) {
+       cont := v.window.Content()
+       cont.(*fyne.Container).Objects[1] = obj
+       cont.Refresh()
+}
+
+func (v *Va4s) setBack(backAction func()) {
+       cont := v.window.Content()
+       stack := cont.(*fyne.Container).Objects[0].(*fyne.Container)
+       stack.Objects[0].(*widget.Button).OnTapped = backAction
+}
+
+func (v *Va4s) SetTokenEntry() {
+       prov := sessionProvider.NewProvider()
+
+       v.setContent(prov.Container)
+       v.setBack(func() {
+               v.app.Quit()
+       })
+       prov.OnSession = func(s *discordgo.Session) {
+               v.session = s
+               // #2 channel selector
+               v.SetChannelSelect()
+       }
+}
+
+func (v *Va4s) SetChannelSelect() {
+       if v.session == nil {
+               v.SetTokenEntry()
+               return
+       }
+
+       channelSelect := channelselect.NewChannelSelect(v.session)
+       v.setContent(channelSelect.Container)
+       v.setBack(func() {
+               v.session.Close()
+               v.session = nil
+               v.SetTokenEntry()
+       })
+       channelSelect.OnVoice = func(vc *discordgo.VoiceConnection) {
+               v.voice = vc
+               // #3 display voice activity
+               v.SetVoiceActivity()
+       }
+}
+
+func (v *Va4s) SetVoiceActivity() {
+       if v.voice == nil {
+               v.SetChannelSelect()
+               return
+       }
+
+       activity := voiceactivity.NewVoiceActivity(v.voice)
+       v.setContent(activity.Container)
+       v.setBack(func() {
+               v.voice.Disconnect(context.Background())
+               v.voice = nil
+               v.SetChannelSelect()
+       })
+}
+
+func (v *Va4s) Close() {
+       if v.voice != nil {
+               v.voice.Disconnect(context.Background())
+       }
+       if v.session != nil {
+               v.session.Close()
+       }
+}
diff --git a/sessionProvider/provider.go b/sessionProvider/provider.go
new file mode 100644 (file)
index 0000000..260edca
--- /dev/null
@@ -0,0 +1,56 @@
+package sessionProvider
+
+import (
+       "fmt"
+
+       "fyne.io/fyne/v2"
+       "fyne.io/fyne/v2/container"
+       "fyne.io/fyne/v2/layout"
+       "fyne.io/fyne/v2/widget"
+       "github.com/bwmarrin/discordgo"
+)
+
+type Provider struct {
+       Container *fyne.Container
+       OnSession func(*discordgo.Session)
+}
+
+func NewProvider() *Provider {
+       prov := new(Provider)
+
+       tokenEntry := widget.NewEntry()
+       tokenEntry.MultiLine = false
+       tokenEntry.PlaceHolder = "6mNvJRX3iwoEP7ZFiu4qdveqx52pyYKLYTelHzY.COYQ_Z.R9VrMznE1d1G1yo+v814BJdrH/leiX7a"
+       tokenEntry.MultiLine = false
+       tokenEntry.AlwaysShowValidationError = true
+       tokenEntry.Wrapping = fyne.TextWrapOff
+       tokenEntry.Scroll = fyne.ScrollNone
+
+       tokenEntry.OnChanged = func(s string) {
+               tokenEntry.SetValidationError(nil)
+       }
+
+       form := widget.NewForm(
+               widget.NewFormItem("Bot token", tokenEntry),
+       )
+
+       form.OnSubmit = func() {
+               session, err := discordgo.New(fmt.Sprintf("Bot %s", tokenEntry.Text))
+               if err != nil {
+                       tokenEntry.SetValidationError(fmt.Errorf("Bad discord bot token: %s", err))
+                       return
+               }
+               err = session.Open()
+               if err != nil {
+                       tokenEntry.SetValidationError(fmt.Errorf("Could not open connection: %s", err))
+                       return
+               }
+               prov.OnSession(session)
+       }
+
+       // TODO remove this
+       tokenEntry.Text = "NjMwMTA3MzA1OTExOTEwNDUx.GwWhIb.09Och4l8wI7eD4Ff9cbKWITnj-cyMnMsxbCwxQ"
+
+       prov.Container = container.NewVBox(layout.NewSpacer(), form, layout.NewSpacer())
+       return prov
+}
diff --git a/voiceActivity/activity.go b/voiceActivity/activity.go
new file mode 100644 (file)
index 0000000..cb2825b
--- /dev/null
@@ -0,0 +1,29 @@
+package voiceactivity
+
+import (
+       "fmt"
+
+       "fyne.io/fyne/v2"
+       "fyne.io/fyne/v2/container"
+       "fyne.io/fyne/v2/widget"
+       "github.com/bwmarrin/discordgo"
+)
+
+type VoiceActivity struct {
+       Container *fyne.Container
+}
+
+func NewVoiceActivity(vc *discordgo.VoiceConnection) *VoiceActivity {
+       va := new(VoiceActivity)
+       va.Container = container.NewStack(widget.NewLabel("todo"))
+       vc.AddHandler(va.handleVoiceUpdate)
+       return va
+}
+
+func (va *VoiceActivity) handleVoiceUpdate(vc *discordgo.VoiceConnection, vs *discordgo.VoiceSpeakingUpdate) {
+       if vs.Speaking {
+               fmt.Printf("%s is speaking\n", vs.UserID)
+       } else {
+               fmt.Printf("%s is not speaking\n", vs.UserID)
+       }
+}