mirror of
https://github.com/stryan/mumble-discord-bridge.git
synced 2024-11-14 19:45:41 -05:00
initial changes, make it manual linking, show mumble users in status
This commit is contained in:
parent
d9d30929ff
commit
5a96c488ee
57
config.go
Normal file
57
config.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"layeh.com/gumble/gumble"
|
||||||
|
)
|
||||||
|
|
||||||
|
type YammerConfig struct {
|
||||||
|
Config *gumble.Config
|
||||||
|
MumbleAddr string
|
||||||
|
MumbleInsecure bool
|
||||||
|
Die chan bool
|
||||||
|
ActiveConns map[string]chan 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
|
||||||
|
}
|
91
handlers.go
Normal file
91
handlers.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ready(s *discordgo.Session, event *discordgo.Ready) {
|
||||||
|
log.Println("READY event registered")
|
||||||
|
}
|
||||||
|
|
||||||
|
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
|
|
||||||
|
// Ignore all messages created by the bot itself
|
||||||
|
// This isn't required in this specific example but it's a good practice.
|
||||||
|
log.Println("Checking message")
|
||||||
|
if m.Author.ID == s.State.User.ID {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(m.Content, "!yammer link") {
|
||||||
|
|
||||||
|
// Find the channel that the message came from.
|
||||||
|
c, err := s.State.Channel(m.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
// Could not find channel.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the guild for that channel.
|
||||||
|
g, err := s.State.Guild(c.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
// Could not find guild.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the message sender in that guild's current voice states.
|
||||||
|
for _, vs := range g.VoiceStates {
|
||||||
|
if vs.UserID == m.Author.ID {
|
||||||
|
log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID)
|
||||||
|
die := make(chan bool)
|
||||||
|
YBConfig.ActiveConns[vs.ChannelID] = die
|
||||||
|
go startBridge(s, g.ID, vs.ChannelID, YBConfig.Config, YBConfig.MumbleAddr, YBConfig.MumbleInsecure, die)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(m.Content, "!yammer unlink") {
|
||||||
|
|
||||||
|
// Find the channel that the message came from.
|
||||||
|
c, err := s.State.Channel(m.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
// Could not find channel.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the guild for that channel.
|
||||||
|
g, err := s.State.Guild(c.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
// Could not find guild.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for the message sender in that guild's current voice states.
|
||||||
|
for _, vs := range g.VoiceStates {
|
||||||
|
if vs.UserID == m.Author.ID {
|
||||||
|
log.Printf("Trying to leave GID %v and VID %v\n", g.ID, vs.ChannelID)
|
||||||
|
YBConfig.ActiveConns[vs.ChannelID] <- true
|
||||||
|
YBConfig.ActiveConns[vs.ChannelID] = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
|
||||||
|
|
||||||
|
if event.Guild.Unavailable {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, channel := range event.Guild.Channels {
|
||||||
|
if channel.ID == event.Guild.ID {
|
||||||
|
_, _ = s.ChannelMessageSend(channel.ID, "Mumble-Discord bridge is active")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
main.go
121
main.go
@ -1,14 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
@ -17,43 +15,7 @@ import (
|
|||||||
_ "layeh.com/gumble/opus"
|
_ "layeh.com/gumble/opus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lookupEnvOrString(key string, defaultVal string) string {
|
var YBConfig *YammerConfig
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
godotenv.Load()
|
godotenv.Load()
|
||||||
@ -98,6 +60,12 @@ func main() {
|
|||||||
|
|
||||||
// Open Websocket
|
// Open Websocket
|
||||||
discord.LogLevel = 2
|
discord.LogLevel = 2
|
||||||
|
discord.StateEnabled = true
|
||||||
|
discord.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuilds | discordgo.IntentsGuildMessages | discordgo.IntentsGuildVoiceStates)
|
||||||
|
// register handlers
|
||||||
|
discord.AddHandler(ready)
|
||||||
|
discord.AddHandler(messageCreate)
|
||||||
|
discord.AddHandler(guildCreate)
|
||||||
err = discord.Open()
|
err = discord.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
@ -106,75 +74,22 @@ func main() {
|
|||||||
defer discord.Close()
|
defer discord.Close()
|
||||||
|
|
||||||
log.Println("Discord Bot Connected")
|
log.Println("Discord Bot Connected")
|
||||||
|
|
||||||
dgv, err := discord.ChannelVoiceJoin(*discordGID, *discordCID, false, false)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer dgv.Speaking(false)
|
|
||||||
defer dgv.Close()
|
|
||||||
|
|
||||||
discord.ShouldReconnectOnError = true
|
|
||||||
|
|
||||||
// MUMBLE Setup
|
|
||||||
|
|
||||||
config := gumble.NewConfig()
|
config := gumble.NewConfig()
|
||||||
config.Username = *mumbleUsername
|
config.Username = *mumbleUsername
|
||||||
config.Password = *mumblePassword
|
config.Password = *mumblePassword
|
||||||
config.AudioInterval = time.Millisecond * 10
|
config.AudioInterval = time.Millisecond * 10
|
||||||
|
|
||||||
m := MumbleDuplex{}
|
YBConfig = &YammerConfig{
|
||||||
|
Config: config,
|
||||||
var tlsConfig tls.Config
|
MumbleAddr: *mumbleAddr + ":" + strconv.Itoa(*mumblePort),
|
||||||
if *mumbleInsecure {
|
MumbleInsecure: *mumbleInsecure,
|
||||||
tlsConfig.InsecureSkipVerify = true
|
ActiveConns: make(map[string]chan bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
mumble, err := gumble.DialWithDialer(new(net.Dialer),*mumbleAddr+":"+strconv.Itoa(*mumblePort),config, &tlsConfig)
|
//go startBridge(discord, *discordGID, *discordCID, config, *mumbleAddr+":"+strconv.Itoa(*mumblePort), *mumbleInsecure, die)
|
||||||
|
|
||||||
if err != nil {
|
sc := make(chan os.Signal, 1)
|
||||||
log.Println(err)
|
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||||||
return
|
<-sc
|
||||||
}
|
log.Println("Bot shutting down")
|
||||||
defer mumble.Disconnect()
|
|
||||||
|
|
||||||
// Shared Channels
|
|
||||||
// Shared channels pass PCM information in 10ms chunks [480]int16
|
|
||||||
var toMumble = mumble.AudioOutgoing()
|
|
||||||
var toDiscord = make(chan []int16, 100)
|
|
||||||
var die = make(chan bool)
|
|
||||||
|
|
||||||
log.Println("Mumble Connected")
|
|
||||||
|
|
||||||
// Start Passing Between
|
|
||||||
// Mumble
|
|
||||||
go m.fromMumbleMixer(toDiscord)
|
|
||||||
config.AudioListeners.Attach(m)
|
|
||||||
//Discord
|
|
||||||
go discordReceivePCM(dgv, die)
|
|
||||||
go fromDiscordMixer(toMumble)
|
|
||||||
go discordSendPCM(dgv, toDiscord, die)
|
|
||||||
|
|
||||||
// Wait for Exit Signal
|
|
||||||
c := make(chan os.Signal)
|
|
||||||
signal.Notify(c, os.Interrupt)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
ticker := time.NewTicker(500 * time.Millisecond)
|
|
||||||
for {
|
|
||||||
<-ticker.C
|
|
||||||
if mumble.State() != 2 {
|
|
||||||
log.Println("Lost mumble connection " + strconv.Itoa(int(mumble.State())))
|
|
||||||
die <- true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case sig := <-c:
|
|
||||||
log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig)
|
|
||||||
case <-die:
|
|
||||||
log.Println("\nGot internal die request. Terminating Mumble-Bridge")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
119
yammer.go
Normal file
119
yammer.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"layeh.com/gumble/gumble"
|
||||||
|
)
|
||||||
|
|
||||||
|
func startBridge(discord *discordgo.Session, discordGID string, discordCID string, config *gumble.Config, mumbleAddr string, mumbleInsecure bool, die chan bool) {
|
||||||
|
dgv, err := discord.ChannelVoiceJoin(discordGID, discordCID, false, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer dgv.Speaking(false)
|
||||||
|
defer dgv.Close()
|
||||||
|
|
||||||
|
discord.ShouldReconnectOnError = true
|
||||||
|
|
||||||
|
// MUMBLE Setup
|
||||||
|
|
||||||
|
m := MumbleDuplex{}
|
||||||
|
|
||||||
|
var tlsConfig tls.Config
|
||||||
|
if mumbleInsecure {
|
||||||
|
tlsConfig.InsecureSkipVerify = true
|
||||||
|
}
|
||||||
|
|
||||||
|
mumble, err := gumble.DialWithDialer(new(net.Dialer), mumbleAddr, config, &tlsConfig)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer mumble.Disconnect()
|
||||||
|
|
||||||
|
// Shared Channels
|
||||||
|
// Shared channels pass PCM information in 10ms chunks [480]int16
|
||||||
|
var toMumble = mumble.AudioOutgoing()
|
||||||
|
var toDiscord = make(chan []int16, 100)
|
||||||
|
|
||||||
|
log.Println("Mumble Connected")
|
||||||
|
|
||||||
|
// Start Passing Between
|
||||||
|
// Mumble
|
||||||
|
go m.fromMumbleMixer(toDiscord)
|
||||||
|
config.AudioListeners.Attach(m)
|
||||||
|
//Discord
|
||||||
|
go discordReceivePCM(dgv, die)
|
||||||
|
go fromDiscordMixer(toMumble)
|
||||||
|
go discordSendPCM(dgv, toDiscord, die)
|
||||||
|
c := make(chan os.Signal)
|
||||||
|
signal.Notify(c, os.Interrupt)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(500 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
<-ticker.C
|
||||||
|
if mumble.State() != 2 {
|
||||||
|
log.Println("Lost mumble connection " + strconv.Itoa(int(mumble.State())))
|
||||||
|
die <- true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case sig := <-c:
|
||||||
|
log.Printf("\nGot %s signal. Terminating Mumble-Bridge\n", sig)
|
||||||
|
case <-die:
|
||||||
|
log.Println("\nGot internal die request. Terminating Mumble-Bridge")
|
||||||
|
dgv.Disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pingMumble(host, port string, c chan int) {
|
||||||
|
m, _ := time.ParseDuration("30s")
|
||||||
|
curr := 0
|
||||||
|
log.Println("Started mumble ping loop")
|
||||||
|
for {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
resp, err := gumble.Ping(host+":"+port, -1, m)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if resp.ConnectedUsers-1 != curr {
|
||||||
|
curr = resp.ConnectedUsers - 1
|
||||||
|
log.Printf("Now %v users in mumble\n", curr)
|
||||||
|
if curr > 0 {
|
||||||
|
c <- curr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Println("Mumble ping loop broken")
|
||||||
|
}
|
||||||
|
|
||||||
|
func discordStatusUpdate(dg *discordgo.Session, c chan int) {
|
||||||
|
status := ""
|
||||||
|
curr := 0
|
||||||
|
log.Println("Started discord control loop")
|
||||||
|
for {
|
||||||
|
curr = <-c
|
||||||
|
log.Println("Updating discord status")
|
||||||
|
if curr == 0 {
|
||||||
|
status = ""
|
||||||
|
} else {
|
||||||
|
status = fmt.Sprintf("%v users in Mumble\n", curr)
|
||||||
|
}
|
||||||
|
dg.UpdateListeningStatus(status)
|
||||||
|
}
|
||||||
|
log.Println("Discord control loop broken")
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user