--- /dev/null
+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
+}
--- /dev/null
+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
--- /dev/null
+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=
--- /dev/null
+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()
+ }
+}
--- /dev/null
+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
+}
--- /dev/null
+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)
+ }
+}