1
0
mirror of https://github.com/stryan/mumble-discord-bridge.git synced 2024-12-27 17:05:40 -05:00

restrucutre into go standard folder strucutre

This commit is contained in:
Tyler Stiene 2021-04-24 14:36:34 -04:00
parent 5bc118c97c
commit a963d2caa1
11 changed files with 93 additions and 372 deletions

1
.gitignore vendored
View File

@ -2,7 +2,6 @@
main
mumble-discord-bridge
dist
bridge
*.prof
*.out
*.test

View File

@ -1,4 +1,4 @@
GOFILES=main.go mumble.go discord.go bridge.go config.go mumble-handlers.go discord-handlers.go sleepct.go
GOFILES=$(shell find ./ -type f -name '*.go')
mumble-discord-bridge: $(GOFILES)
goreleaser build --skip-validate --rm-dist

View File

@ -1,74 +0,0 @@
package main
import (
"flag"
"fmt"
"log"
"os"
"strconv"
"layeh.com/gumble/gumble"
)
type bridgeMode int
const (
bridgeModeAuto bridgeMode = iota
bridgeModeManual
bridgeModeConstant
)
//BridgeConfig holds configuration information set at startup
//It should not change during runtime
type BridgeConfig struct {
MumbleConfig *gumble.Config
MumbleAddr string
MumbleInsecure bool
MumbleCertificate string
MumbleChannel []string
mumbleStartStreamCount int
MumbleDisableText bool
Command string
GID string
CID string
DiscordStartStreamingCount int
DiscordDisableText bool
}
func lookupEnvOrString(key string, defaultVal string) string {
if val, ok := os.LookupEnv(key); ok {
return val
}
return defaultVal
}
func lookupEnvOrInt(key string, defaultVal int) int {
if val, ok := os.LookupEnv(key); ok {
v, err := strconv.Atoi(val)
if err != nil {
log.Fatalf("LookupEnvOrInt[%s]: %v", key, err)
}
return v
}
return defaultVal
}
func lookupEnvOrBool(key string, defaultVal bool) bool {
if val, ok := os.LookupEnv(key); ok {
v, err := strconv.ParseBool(val)
if err != nil {
log.Fatalf("LookupEnvOrInt[%s]: %v", key, err)
}
return v
}
return defaultVal
}
func getConfig(fs *flag.FlagSet) []string {
cfg := make([]string, 0, 10)
fs.VisitAll(func(f *flag.Flag) {
cfg = append(cfg, fmt.Sprintf("%s:%q", f.Name, f.Value.String()))
})
return cfg
}

View File

@ -1,4 +1,4 @@
package main
package bridge
import (
"context"
@ -15,12 +15,36 @@ import (
"layeh.com/gumble/gumble"
)
type discordUser struct {
type DiscordUser struct {
username string
seen bool
dm *discordgo.Channel
}
type BridgeMode int
const (
BridgeModeAuto BridgeMode = iota
BridgeModeManual
BridgeModeConstant
)
type BridgeConfig struct {
MumbleConfig *gumble.Config
MumbleAddr string
MumbleInsecure bool
MumbleCertificate string
MumbleChannel []string
MumbleStartStreamCount int
MumbleDisableText bool
Command string
GID string
CID string
DiscordStartStreamingCount int
DiscordDisableText bool
Version string
}
//BridgeState manages dynamic information about the bridge during runtime
type BridgeState struct {
// The configuration data for this bridge
@ -42,7 +66,7 @@ type BridgeState struct {
Connected bool
// The bridge mode constant, auto, manual. Default is constant.
Mode bridgeMode
Mode BridgeMode
// Discord session. This is created and outside the bridge state
DiscordSession *discordgo.Session
@ -54,7 +78,7 @@ type BridgeState struct {
MumbleClient *gumble.Client
// Map of Discord users tracked by this bridge.
DiscordUsers map[string]discordUser
DiscordUsers map[string]DiscordUser
DiscordUsersMutex sync.Mutex
// Map of Mumble users tracked by this bridge
@ -80,7 +104,7 @@ type BridgeState struct {
}
// startBridge established the voice connection
func (b *BridgeState) startBridge() {
func (b *BridgeState) StartBridge() {
b.lock.Lock()
defer b.lock.Unlock()
@ -214,10 +238,10 @@ func (b *BridgeState) startBridge() {
b.MumbleUsersMutex.Lock()
b.MumbleUsers = make(map[string]bool)
b.MumbleUsersMutex.Unlock()
b.DiscordUsers = make(map[string]discordUser)
b.DiscordUsers = make(map[string]DiscordUser)
}
func (b *BridgeState) discordStatusUpdate() {
func (b *BridgeState) DiscordStatusUpdate() {
m, _ := time.ParseDuration("30s")
for {
time.Sleep(3 * time.Second)
@ -254,14 +278,14 @@ func (b *BridgeState) discordStatusUpdate() {
// when there is at least one user on both, starts up the bridge
// when there are no users on either side, kills the bridge
func (b *BridgeState) AutoBridge() {
log.Println("beginning auto mode")
log.Println("Beginning auto mode")
ticker := time.NewTicker(3 * time.Second)
for {
select {
case <-ticker.C:
case <-b.AutoChanDie:
log.Println("ending automode")
log.Println("Ending automode")
return
}
@ -270,11 +294,11 @@ func (b *BridgeState) AutoBridge() {
b.BridgeMutex.Lock()
if !b.Connected && b.MumbleUserCount > 0 && len(b.DiscordUsers) > 0 {
log.Println("users detected in mumble and discord, bridging")
go b.startBridge()
log.Println("Users detected in mumble and discord, bridging")
go b.StartBridge()
}
if b.Connected && b.MumbleUserCount == 0 && len(b.DiscordUsers) <= 1 {
log.Println("no one online, killing bridge")
log.Println("No one online, killing bridge")
b.BridgeDie <- true
}

View File

@ -1,4 +1,4 @@
package main
package bridge
import (
"fmt"
@ -15,11 +15,11 @@ type DiscordListener struct {
Bridge *BridgeState
}
func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
func (l *DiscordListener) GuildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
log.Println("CREATE event registered")
if event.ID != l.Bridge.BridgeConfig.GID {
log.Println("received GuildCreate from a guild not in config")
log.Println("Received GuildCreate from a guild not in config")
return
}
@ -32,7 +32,7 @@ func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.Gui
u, err := s.User(vs.UserID)
if err != nil {
log.Println("error looking up username")
log.Println("Error looking up username")
}
dm, err := s.UserChannelCreate(u.ID)
@ -41,7 +41,7 @@ func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.Gui
}
l.Bridge.DiscordUsersMutex.Lock()
l.Bridge.DiscordUsers[vs.UserID] = discordUser{
l.Bridge.DiscordUsers[vs.UserID] = DiscordUser{
username: u.Username,
seen: true,
dm: dm,
@ -61,7 +61,7 @@ func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.Gui
}
}
func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
func (l *DiscordListener) MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
// Ignore all messages created by the bot itself
if m.Author.ID == s.State.User.ID {
@ -82,7 +82,7 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
}
prefix := "!" + l.Bridge.BridgeConfig.Command
if l.Bridge.Mode == bridgeModeConstant && strings.HasPrefix(m.Content, prefix) {
if l.Bridge.Mode == BridgeModeConstant && strings.HasPrefix(m.Content, prefix) {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Constant mode enabled, manual commands can not be entered")
return
}
@ -101,7 +101,7 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
if vs.UserID == m.Author.ID {
log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID)
l.Bridge.DiscordChannelID = vs.ChannelID
go l.Bridge.startBridge()
go l.Bridge.StartBridge()
return
}
}
@ -135,16 +135,16 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
time.Sleep(5 * time.Second)
go l.Bridge.startBridge()
go l.Bridge.StartBridge()
return
}
}
}
if strings.HasPrefix(m.Content, prefix+" auto") {
if l.Bridge.Mode != bridgeModeAuto {
if l.Bridge.Mode != BridgeModeAuto {
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Auto mode enabled")
l.Bridge.Mode = bridgeModeAuto
l.Bridge.Mode = BridgeModeAuto
l.Bridge.DiscordChannelID = l.Bridge.BridgeConfig.CID
l.Bridge.AutoChanDie = make(chan bool)
go l.Bridge.AutoBridge()
@ -152,12 +152,12 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Auto mode disabled")
l.Bridge.DiscordChannelID = ""
l.Bridge.AutoChanDie <- true
l.Bridge.Mode = bridgeModeManual
l.Bridge.Mode = BridgeModeManual
}
}
}
func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) {
func (l *DiscordListener) VoiceUpdate(s *discordgo.Session, event *discordgo.VoiceStateUpdate) {
l.Bridge.DiscordUsersMutex.Lock()
defer l.Bridge.DiscordUsersMutex.Unlock()
@ -165,7 +165,7 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
g, err := s.State.Guild(l.Bridge.BridgeConfig.GID)
if err != nil {
log.Println("error finding guild")
log.Println("Error finding guild")
panic(err)
}
@ -187,7 +187,7 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
u, err := s.User(vs.UserID)
if err != nil {
log.Println("error looking up username")
log.Println("Error looking up username")
continue
}
@ -196,7 +196,7 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
if err != nil {
log.Println("Error creating private channel for", u.Username)
}
l.Bridge.DiscordUsers[vs.UserID] = discordUser{
l.Bridge.DiscordUsers[vs.UserID] = DiscordUser{
username: u.Username,
seen: true,
dm: dm,

View File

@ -1,4 +1,4 @@
package main
package bridge
import (
"context"
@ -8,6 +8,7 @@ import (
"time"
"github.com/bwmarrin/discordgo"
"github.com/stieneee/mumble-discord-bridge/pkg/sleepct"
"layeh.com/gopus"
"layeh.com/gumble/gumble"
_ "layeh.com/gumble/opus"
@ -61,11 +62,8 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup,
opusSilence = append(opusSilence, 0x00)
}
// ticker := NewTickerCT(20 * time.Millisecond)
sleepTick := SleepCT{
d: 20 * time.Millisecond,
t: time.Now(),
}
sleepTick := sleepct.SleepCT{}
sleepTick.Start(20 * time.Millisecond)
lastReady := true
var readyTimeout *time.Timer
@ -79,7 +77,7 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup,
if lastReady {
OnError(fmt.Sprintf("Discordgo not ready for opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil)
readyTimeout = time.AfterFunc(30*time.Second, func() {
log.Println("set ready timeout")
log.Println("Debug: Set ready timeout")
cancel()
})
lastReady = false
@ -164,7 +162,7 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro
if lastReady {
OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil)
readyTimeout = time.AfterFunc(30*time.Second, func() {
log.Println("set ready timeout")
log.Println("Debug: Set ready timeout")
cancel()
})
lastReady = false
@ -225,14 +223,14 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro
select {
case dd.fromDiscordMap[p.SSRC].pcm <- p.PCM[0:480]:
default:
log.Println("fromDiscordMap buffer full. Dropping packet")
log.Println("From Discord buffer full. Dropping packet")
dd.discordMutex.Unlock()
continue
}
select {
case dd.fromDiscordMap[p.SSRC].pcm <- p.PCM[480:960]:
default:
log.Println("fromDiscordMap buffer full. Dropping packet")
log.Println("From Discord buffer full. Dropping packet")
}
dd.discordMutex.Unlock()
}
@ -244,10 +242,10 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
mumbleSilence = append(mumbleSilence, 0x00)
}
var speakingStart time.Time
sleepTick := SleepCT{
d: 10 * time.Millisecond,
t: time.Now(),
}
sleepTick := sleepct.SleepCT{}
sleepTick.Start(10 * time.Millisecond)
sendAudio := false
toMumbleStreaming := false
wg.Add(1)
@ -271,7 +269,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
for i := range dd.fromDiscordMap {
bufferLength := len(dd.fromDiscordMap[i].pcm)
isStreaming := dd.fromDiscordMap[i].streaming
if (bufferLength > 0 && isStreaming) || (bufferLength > dd.Bridge.BridgeConfig.mumbleStartStreamCount && !isStreaming) {
if (bufferLength > 0 && isStreaming) || (bufferLength > dd.Bridge.BridgeConfig.MumbleStartStreamCount && !isStreaming) {
if !toMumbleStreaming {
speakingStart = time.Now()
toMumbleStreaming = true
@ -307,7 +305,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
select {
case toMumble <- outBuf:
case <-timeout:
log.Println("toMumble timeout. Dropping packet")
log.Println("To Mumble timeout. Dropping packet")
}
}

View File

@ -1,4 +1,4 @@
package main
package bridge
import (
"log"
@ -12,7 +12,7 @@ type MumbleListener struct {
Bridge *BridgeState
}
func (l *MumbleListener) mumbleConnect(e *gumble.ConnectEvent) {
func (l *MumbleListener) MumbleConnect(e *gumble.ConnectEvent) {
//join specified channel
startingChannel := e.Client.Channels.Find(l.Bridge.BridgeConfig.MumbleChannel...)
if startingChannel != nil {
@ -20,7 +20,7 @@ func (l *MumbleListener) mumbleConnect(e *gumble.ConnectEvent) {
}
}
func (l *MumbleListener) mumbleUserChange(e *gumble.UserChangeEvent) {
func (l *MumbleListener) MumbleUserChange(e *gumble.UserChangeEvent) {
l.Bridge.MumbleUsersMutex.Lock()
if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) {
l.Bridge.MumbleUsers = make(map[string]bool)
@ -40,7 +40,7 @@ func (l *MumbleListener) mumbleUserChange(e *gumble.UserChangeEvent) {
log.Println("User connected to mumble " + e.User.Name)
if !l.Bridge.BridgeConfig.MumbleDisableText {
e.User.Send("Mumble-Discord-Bridge v" + version)
e.User.Send("Mumble-Discord-Bridge v" + l.Bridge.BridgeConfig.Version)
// Tell the user who is connected to discord
l.Bridge.DiscordUsersMutex.Lock()

View File

@ -1,4 +1,4 @@
package main
package bridge
import (
"context"
@ -6,6 +6,7 @@ import (
"sync"
"time"
"github.com/stieneee/mumble-discord-bridge/pkg/sleepct"
"layeh.com/gumble/gumble"
_ "layeh.com/gumble/opus"
)
@ -30,7 +31,7 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) {
go func() {
name := e.User.Name
log.Println("new mumble audio stream", name)
log.Println("New mumble audio stream", name)
for p := range e.C {
// log.Println("audio packet", p.Sender.Name, len(p.AudioBuffer))
@ -39,15 +40,14 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) {
localMumbleArray <- p.AudioBuffer[480*i : 480*(i+1)]
}
}
log.Println("mumble audio stream ended", name)
log.Println("Mumble audio stream ended", name)
}()
}
func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, toDiscord chan []int16) {
sleepTick := SleepCT{
d: 10 * time.Millisecond,
t: time.Now(),
}
sleepTick := sleepct.SleepCT{}
sleepTick.Start(10 * time.Millisecond)
sendAudio := false
bufferWarning := false
@ -74,7 +74,7 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, t
sendAudio = true
if !mumbleStreamingArr[i] {
mumbleStreamingArr[i] = true
// log.Println("mumble starting", i)
// log.Println("Mumble starting", i)
}
x1 := (<-fromMumbleArr[i])
@ -82,7 +82,7 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, t
} else {
if mumbleStreamingArr[i] {
mumbleStreamingArr[i] = false
// log.Println("mumble stopping", i)
// log.Println("Mumble stopping", i)
}
}
}

234
main.go
View File

@ -1,234 +0,0 @@
package main
import (
"flag"
"fmt"
"log"
"math"
"os"
"os/signal"
"runtime/pprof"
"strconv"
"strings"
"syscall"
"time"
"github.com/bwmarrin/discordgo"
"github.com/joho/godotenv"
"layeh.com/gumble/gumble"
"layeh.com/gumble/gumbleutil"
_ "layeh.com/gumble/opus"
)
var (
// Build vars
version string
commit string
date string
)
func main() {
var err error
fmt.Println("Mumble-Discord-Bridge")
fmt.Println("v" + version + " " + commit + " " + date)
godotenv.Load()
mumbleAddr := flag.String("mumble-address", lookupEnvOrString("MUMBLE_ADDRESS", ""), "MUMBLE_ADDRESS, mumble server address, example example.com, required")
mumblePort := flag.Int("mumble-port", lookupEnvOrInt("MUMBLE_PORT", 64738), "MUMBLE_PORT, mumble port, (default 64738)")
mumbleUsername := flag.String("mumble-username", lookupEnvOrString("MUMBLE_USERNAME", "Discord"), "MUMBLE_USERNAME, mumble username, (default: discord)")
mumblePassword := flag.String("mumble-password", lookupEnvOrString("MUMBLE_PASSWORD", ""), "MUMBLE_PASSWORD, mumble password, optional")
mumbleInsecure := flag.Bool("mumble-insecure", lookupEnvOrBool("MUMBLE_INSECURE", false), " MUMBLE_INSECURE, mumble insecure, optional")
mumbleCertificate := flag.String("mumble-certificate", lookupEnvOrString("MUMBLE_CERTIFICATE", ""), "MUMBLE_CERTIFICATE, client certificate to use when connecting to the Mumble server")
mumbleChannel := flag.String("mumble-channel", lookupEnvOrString("MUMBLE_CHANNEL", ""), "MUMBLE_CHANNEL, mumble channel to start in, using '/' to separate nested channels, optional")
mumbleSendBuffer := flag.Int("to-mumble-buffer", lookupEnvOrInt("TO_MUMBLE_BUFFER", 50), "TO_MUMBLE_BUFFER, Jitter buffer from Discord to Mumble to absorb timing issues related to network, OS and hardware quality. (Increments of 10ms)")
mumbleDisableText := flag.Bool("mumble-disable-text", lookupEnvOrBool("MUMBLE_DISABLE_TEXT", false), "MUMBLE_DISABLE_TEXT, disable sending text to mumble, (default false)")
discordToken := flag.String("discord-token", lookupEnvOrString("DISCORD_TOKEN", ""), "DISCORD_TOKEN, discord bot token, required")
discordGID := flag.String("discord-gid", lookupEnvOrString("DISCORD_GID", ""), "DISCORD_GID, discord gid, required")
discordCID := flag.String("discord-cid", lookupEnvOrString("DISCORD_CID", ""), "DISCORD_CID, discord cid, required")
discordSendBuffer := flag.Int("to-discord-buffer", lookupEnvOrInt("TO_DISCORD_BUFFER", 50), "TO_DISCORD_BUFFER, Jitter buffer from Mumble to Discord to absorb timing issues related to network, OS and hardware quality. (Increments of 10ms)")
discordCommand := flag.String("discord-command", lookupEnvOrString("DISCORD_COMMAND", "mumble-discord"), "DISCORD_COMMAND, Discord command string, env alt DISCORD_COMMAND, optional, (defaults mumble-discord)")
discordDisableText := flag.Bool("discord-disable-text", lookupEnvOrBool("DISCORD_DISABLE_TEXT", false), "DISCORD_DISABLE_TEXT, disable sending direct messages to discord, (default false)")
mode := flag.String("mode", lookupEnvOrString("MODE", "constant"), "MODE, [constant, manual, auto] determine which mode the bridge starts in, (default constant)")
nice := flag.Bool("nice", lookupEnvOrBool("NICE", false), "NICE, whether the bridge should automatically try to 'nice' itself, (default false)")
debug := flag.Int("debug-level", lookupEnvOrInt("DEBUG", 1), "DEBUG_LEVEL, Discord debug level, optional, (default 1)")
cpuprofile := flag.String("cpuprofile", "", "write cpu profile to `file`")
flag.Parse()
log.Printf("app.config %v\n", getConfig(flag.CommandLine))
if *mumbleAddr == "" {
log.Fatalln("missing mumble address")
}
if *mumbleUsername == "" {
log.Fatalln("missing mumble username")
}
if *discordToken == "" {
log.Fatalln("missing discord bot token")
}
if *discordGID == "" {
log.Fatalln("missing discord gid")
}
if *discordCID == "" {
log.Fatalln("missing discord cid")
}
if *mode == "" {
log.Fatalln("missing mode set")
}
if *nice {
err := syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -5)
if err != nil {
log.Println("Unable to set priority. ", err)
}
}
// Optional CPU Profiling
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close() // error handling omitted for example
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
// Buffer Math
if *discordSendBuffer < 10 {
*discordSendBuffer = 10
}
if *mumbleSendBuffer < 10 {
*mumbleSendBuffer = 10
}
var discordStartStreamingCount int = int(math.Round(float64(*discordSendBuffer) / 10.0))
log.Println("To Discord Jitter Buffer: ", discordStartStreamingCount*10, " ms")
var mumbleStartStreamCount int = int(math.Round(float64(*mumbleSendBuffer) / 10.0))
log.Println("To Mumble Jitter Buffer: ", mumbleStartStreamCount*10, " ms")
// BRIDGE SETUP
Bridge := &BridgeState{
BridgeConfig: &BridgeConfig{
// MumbleConfig: config,
MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort),
MumbleInsecure: *mumbleInsecure,
MumbleCertificate: *mumbleCertificate,
MumbleChannel: strings.Split(*mumbleChannel, "/"),
mumbleStartStreamCount: mumbleStartStreamCount,
MumbleDisableText: *mumbleDisableText,
Command: *discordCommand,
GID: *discordGID,
CID: *discordCID,
DiscordStartStreamingCount: discordStartStreamingCount,
DiscordDisableText: *discordDisableText,
},
Connected: false,
DiscordUsers: make(map[string]discordUser),
MumbleUsers: make(map[string]bool),
}
// MUMBLE SETUP
Bridge.BridgeConfig.MumbleConfig = gumble.NewConfig()
Bridge.BridgeConfig.MumbleConfig.Username = *mumbleUsername
Bridge.BridgeConfig.MumbleConfig.Password = *mumblePassword
Bridge.BridgeConfig.MumbleConfig.AudioInterval = time.Millisecond * 10
Bridge.MumbleListener = &MumbleListener{
Bridge: Bridge,
}
Bridge.BridgeConfig.MumbleConfig.Attach(gumbleutil.Listener{
Connect: Bridge.MumbleListener.mumbleConnect,
UserChange: Bridge.MumbleListener.mumbleUserChange,
})
// DISCORD SETUP
//Connect to discord
Bridge.DiscordSession, err = discordgo.New("Bot " + *discordToken)
if err != nil {
log.Println(err)
return
}
Bridge.DiscordSession.LogLevel = *debug
Bridge.DiscordSession.StateEnabled = true
Bridge.DiscordSession.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged)
Bridge.DiscordSession.ShouldReconnectOnError = true
// register handlers
Bridge.DiscordListener = &DiscordListener{
Bridge: Bridge,
}
Bridge.DiscordSession.AddHandler(Bridge.DiscordListener.messageCreate)
Bridge.DiscordSession.AddHandler(Bridge.DiscordListener.guildCreate)
Bridge.DiscordSession.AddHandler(Bridge.DiscordListener.voiceUpdate)
// Open Discord websocket
err = Bridge.DiscordSession.Open()
if err != nil {
log.Println(err)
return
}
defer Bridge.DiscordSession.Close()
log.Println("Discord Bot Connected")
log.Printf("Discord bot looking for command !%v", *discordCommand)
switch *mode {
case "auto":
log.Println("bridge starting in automatic mode")
Bridge.AutoChanDie = make(chan bool)
Bridge.Mode = bridgeModeAuto
Bridge.DiscordChannelID = Bridge.BridgeConfig.CID
go Bridge.AutoBridge()
case "manual":
log.Println("bridge starting in manual mode")
Bridge.Mode = bridgeModeManual
case "constant":
log.Println("bridge starting in constant mode")
Bridge.Mode = bridgeModeConstant
Bridge.DiscordChannelID = Bridge.BridgeConfig.CID
go func() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Bridge paniced", r)
}
}()
for {
Bridge.startBridge()
log.Println("Bridge died")
time.Sleep(5 * time.Second)
log.Println("Restarting")
}
}()
default:
Bridge.DiscordSession.Close()
log.Fatalln("invalid bridge mode set")
}
go Bridge.discordStatusUpdate()
// Shutdown on OS signal
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
<-sc
log.Println("OS Signal. Bot shutting down")
// Wait or the bridge to exit cleanly
Bridge.BridgeMutex.Lock()
if Bridge.Connected {
//TODO BridgeDie occasionally panics on send to closed channel
Bridge.BridgeDie <- true
Bridge.WaitExit.Wait()
}
Bridge.BridgeMutex.Unlock()
}

View File

@ -1,4 +1,4 @@
package main
package sleepct
import (
"fmt"
@ -14,6 +14,15 @@ type SleepCT struct {
t time.Time // last time target
}
func (s *SleepCT) Start(d time.Duration) {
if s.t.IsZero() {
s.d = d
s.t = time.Now()
} else {
panic("SleepCT already started")
}
}
func (s *SleepCT) SleepNextTarget() {
s.Lock()

View File

@ -9,6 +9,7 @@ import (
"testing"
"time"
"github.com/stieneee/mumble-discord-bridge/pkg/sleepct"
"github.com/stieneee/tickerct"
)
@ -107,10 +108,8 @@ func testSleepCT(wg *sync.WaitGroup) {
now := time.Now()
start := now
// start the ticker
s := SleepCT{
d: interval,
t: time.Now(),
}
s := sleepct.SleepCT{}
s.Start(interval)
var i int64
for i = 0; i < testCount; i++ {
if i+1 < testCount {