Add system tray icon, file logging, and fix Windows hotkey conflict
- Adds fyne.io/systray with a programmatic monkey face icon and Quit menu item - Logs to discord-lobby-mute.log next to the exe so startup errors are visible without a console window - Windows now uses raw Win32 RegisterHotKey in a locked goroutine instead of golang.design/x/hotkey, avoiding a deadlock where systray and the hotkey library both try to own the main thread message loop - Build with: GOOS=windows GOARCH=amd64 go build -ldflags "-H windowsgui" -o discord-lobby-mute.exe . Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,14 +3,15 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"fyne.io/systray"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"golang.design/x/hotkey"
|
||||
"golang.design/x/mainthread"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -107,21 +108,27 @@ func toggleMute(dg *discordgo.Session, cfg *Config, muted bool) {
|
||||
for _, vs := range guild.VoiceStates {
|
||||
if vs.ChannelID == cfg.ChannelID && !exempt[vs.UserID] {
|
||||
if err := dg.GuildMemberMute(cfg.GuildID, vs.UserID, muted); err != nil {
|
||||
log.Printf(" warning: user %s: %v", vs.UserID, err)
|
||||
log.Printf("warning: user %s: %v", vs.UserID, err)
|
||||
}
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
fmt.Println(" (no users found in channel)")
|
||||
log.Println("(no users found in channel)")
|
||||
} else {
|
||||
fmt.Printf(" %d user(s) affected\n", count)
|
||||
log.Printf("%d user(s) affected", count)
|
||||
}
|
||||
}
|
||||
|
||||
func main() { mainthread.Init(run) }
|
||||
|
||||
func run() {
|
||||
// Log to file next to the executable so errors are visible even without a console.
|
||||
if exe, err := os.Executable(); err == nil {
|
||||
logPath := filepath.Join(filepath.Dir(exe), "discord-lobby-mute.log")
|
||||
if f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err == nil {
|
||||
log.SetOutput(io.MultiWriter(os.Stderr, f))
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := loadConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -144,29 +151,38 @@ func run() {
|
||||
defer dg.Close()
|
||||
|
||||
<-ready
|
||||
fmt.Println("Connected to Discord.")
|
||||
log.Println("Connected to Discord.")
|
||||
|
||||
mods, key, err := parseKeybind(cfg.Keybind)
|
||||
keydownCh, err := startHotkey(cfg.Keybind)
|
||||
if err != nil {
|
||||
log.Fatalf("keybind: %v", err)
|
||||
}
|
||||
|
||||
hk := hotkey.New(mods, key)
|
||||
if err := hk.Register(); err != nil {
|
||||
log.Fatalf("register hotkey %q: %v", cfg.Keybind, err)
|
||||
}
|
||||
defer hk.Unregister()
|
||||
|
||||
fmt.Printf("Listening on %s — toggles server mute for channel %s\n", cfg.Keybind, cfg.ChannelID)
|
||||
log.Printf("Listening on %s — toggles server mute for channel %s", cfg.Keybind, cfg.ChannelID)
|
||||
|
||||
muted := false
|
||||
for range hk.Keydown() {
|
||||
muted = !muted
|
||||
if muted {
|
||||
fmt.Print("Muting all users... ")
|
||||
} else {
|
||||
fmt.Print("Unmuting all users... ")
|
||||
go func() {
|
||||
muted := false
|
||||
for range keydownCh {
|
||||
muted = !muted
|
||||
if muted {
|
||||
log.Print("Muting all users...")
|
||||
} else {
|
||||
log.Print("Unmuting all users...")
|
||||
}
|
||||
toggleMute(dg, cfg, muted)
|
||||
}
|
||||
toggleMute(dg, cfg, muted)
|
||||
}
|
||||
}()
|
||||
|
||||
systray.Run(
|
||||
func() {
|
||||
systray.SetIcon(monkeyIcon())
|
||||
systray.SetTooltip("Discord Lobby Mute — " + cfg.Keybind)
|
||||
mQuit := systray.AddMenuItem("Quit", "Quit Discord Lobby Mute")
|
||||
go func() {
|
||||
<-mQuit.ClickedCh
|
||||
systray.Quit()
|
||||
}()
|
||||
},
|
||||
func() {},
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user