Skip to content

Commit

Permalink
first working commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Stieneee committed Oct 29, 2020
1 parent c1125a5 commit 10a4b17
Show file tree
Hide file tree
Showing 11 changed files with 551 additions and 502 deletions.
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# An env file can be used to in the working folder to set the environment variables

MUMBLE_ADDRESS=
MUMBLE_USERNAME=
MUMBLE_PASSWORD=

DISCORD_TOKEN=
DISCORD_GID=
DISCORD_CID=
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env
main
mumble-discord-bridge
19 changes: 19 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# syntax=docker/dockerfile:experimental

# Stage 1

FROM golang:latest as builder
WORKDIR /go/src/app
COPY . .

RUN go build -o mumble-discord-bridge -ldflags="-extldflags=-static" *.go

# Stage 2

FROM alpine:latest as static
WORKDIR /opt/
RUN apk add opus
COPY --from=builder /go/src/app/mumble-discord-bridge .

# Entry Point
CMD ["/opt/mumble-discord-bridge"]
505 changes: 4 additions & 501 deletions LICENSE

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
GOFILES=main.go mumble.go discord.go

mumble-discord-bridge: $(GOFILES)
go build -o $@ $(GOFILES)
100 changes: 99 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,99 @@
# mumble-discord-bridge
# Mumble Discord Bridge

Mumble Discord Bridge is an open source Go application to bridge the audio between Mumble and Discord.

It was built with the hope that people can continue to use the voice application of their choice.

## Usage

Several configuration variables must be set for the binary to function correctly.
All variables can be set using flags or in the environment.
The binary will also attempt to load .env file located in the working directory.

```bash
Usage of mumble-discord-bridge:
-discord-cid string
DISCORD_CID, discord cid
-discord-gid string
DISCORD_GID, discord gid
-discord-token string
DISCORD_TOKEN, discord bot token
-mumble-address string
MUMBLE_ADDRESS, mumble server address, example example.com
-mumble-password string
MUMBLE_PASSWORD, mumble password, optional
-mumble-port int
MUMBLE_PORT mumble port (default 64738)
-mumble-username string
MUMBLE_USERNAME, mumble username (default "discord-bridge")
```

## Setup

### Creating a Discord Bot

A Discord bot is required to authenticate this application with Discord.
The guide below provides information on how to setup a Discord bot.

[Create a Discord Bot](https://discordpy.readthedocs.io/en/latest/discord.html)

Individual Discord servers need to invite the bot before it can connect.

### Binary

Prebuilt binaries are available.

```bash
curl ...
```

### Docker

This project is built and distributed in a docker container.
A sample docker command can be copied from below.
This service command will always attempt to restart the service due to the `--restart=always` flag even when the server is restarted.
For testing purposes it may be best to remove the restart flag.

Replace the environment variables with variable for the desired mumble server, discord bot and discord server/channel.

```bash
# Sample for testing
docker docker run -e MUMBLE_ADDRESS=example.com -e MUMBLE_PASSWORD=optional -e DISCORD_TOKEN=TOKEN -e DISCORD_GID=GID -e DISCORD_CID=CID stieneee/mumble-discord-bridge

# Run as a service
docker docker run -e MUMBLE_ADDRESS=example.com -e MUMBLE_PASSWORD=optional -e DISCORD_TOKEN=TOKEN -e DISCORD_GID=GID -e DISCORD_CID=CID --restart=always --name=mumble-discord-bridge -d stieneee/mumble-discord-bridge

# Stop the service
docker stop mumble-discord-bridge && docker rm mumble-discord-bridge
```

## Building From Source

This project requires Golang to build from source.
A simple go build command is all that is needed.
Ensure the opus library is installed.

```bash
go build *.go -o mumble-discord-bridge
```

## Known Issues

Currently there is an issue opening the discord voice channel.
It is a known issue with a dependency of this project.

## License

Distributed under the MIT License. See LICENSE for more information.

## Contributing

Issues and PRs are welcome and encouraged.
Please consider opening an issue to discuss features and ideas.

## Acknowledgement

The project would not have been possible without:

- [gumble](https://github.com/layeh/gumble)
- [discordgo](https://github.com/bwmarrin/discordgo)
114 changes: 114 additions & 0 deletions discord.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package main

import (
"fmt"
"os"
"time"

"github.com/bwmarrin/discordgo"
"layeh.com/gopus"
"layeh.com/gumble/gumble"
_ "layeh.com/gumble/opus"
)

// OnError gets called by dgvoice when an error is encountered.
// By default logs to STDERR
var OnError = func(str string, err error) {
prefix := "dgVoice: " + str

if err != nil {
os.Stderr.WriteString(prefix + ": " + err.Error())
} else {
os.Stderr.WriteString(prefix)
}
}

// SendPCM will receive on the provied channel encode
// received PCM data into Opus then send that to Discordgo
func discordSendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16) {
const channels int = 1
const frameRate int = 48000 // audio sampling rate
const frameSize int = 960 // uint16 size of each audio frame
const maxBytes int = (frameSize * 2) * 2 // max size of opus data

streaming := false

opusEncoder, err := gopus.NewEncoder(frameRate, channels, gopus.Audio)
if err != nil {
OnError("NewEncoder Error", err)
return
}

ticker := time.NewTicker(20 * time.Millisecond)

for {
<-ticker.C

if len(pcm) > 1 {
if !streaming {
v.Speaking(true)
streaming = true
}

r1 := <-pcm
r2 := <-pcm

// try encoding pcm frame with Opus
opus, err := opusEncoder.Encode(append(r1, r2...), frameSize, maxBytes)
if err != nil {
OnError("Encoding Error", err)
return
}

if v.Ready == false || v.OpusSend == nil {
OnError(fmt.Sprintf("Discordgo not ready for opus packets. %+v : %+v", v.Ready, v.OpusSend), nil)
return
}

v.OpusSend <- opus
} else {
if streaming {
v.Speaking(false)
streaming = false
}
}
}
}

// ReceivePCM will receive on the the Discordgo OpusRecv channel and decode
// the opus audio into PCM then send it on the provided channel.
func discordReceivePCM(v *discordgo.VoiceConnection, toMumble chan gumble.AudioBuffer) {
var err error
speakers := make(map[uint32]*gopus.Decoder)

for {
if v.Ready == false || v.OpusRecv == nil {
OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", v.Ready, v.OpusSend), nil)
return
}

p, ok := <-v.OpusRecv
if !ok {
fmt.Println("Opus not ok")
return
}

_, ok = speakers[p.SSRC]
if !ok {
speakers[p.SSRC], err = gopus.NewDecoder(48000, 1)
if err != nil {
OnError("error creating opus decoder", err)
continue
}
}

p.PCM, err = speakers[p.SSRC].Decode(p.Opus, 960, false)
if err != nil {
OnError("Error decoding opus data", err)
continue
}

toMumble <- p.PCM[0:480]
toMumble <- p.PCM[480:960]
}
}
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module github.com/stieneee/mumble-discord-bridge

go 1.15

require (
github.com/bwmarrin/discordgo v0.22.0
github.com/joho/godotenv v1.3.0
layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa
layeh.com/gumble v0.0.0-20200818122324-146f9205029b
)
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
github.com/bwmarrin/discordgo v0.22.0 h1:uBxY1HmlVCsW1IuaPjpCGT6A2DBwRn0nvOguQIxDdFM=
github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/dchote/go-openal v0.0.0-20171116030048-f4a9a141d372 h1:tz3KnXWtRZR0RWOfcMNOw+HHezWLQa7vfSOWTtKjchI=
github.com/dchote/go-openal v0.0.0-20171116030048-f4a9a141d372/go.mod h1:74z+CYu2/mx4N+mcIS/rsvfAxBPBV9uv8zRAnwyFkdI=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa h1:WNU4LYsgD2UHxgKgB36mL6iMAMOvr127alafSlgBbiA=
layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa/go.mod h1:AOef7vHz0+v4sWwJnr0jSyHiX/1NgsMoaxl+rEPz/I0=
layeh.com/gumble v0.0.0-20200818122324-146f9205029b h1:Kne6wkHqbqrygRsqs5XUNhSs84DFG5TYMeCkCbM56sY=
layeh.com/gumble v0.0.0-20200818122324-146f9205029b/go.mod h1:tWPVA9ZAfImNwabjcd9uDE+Mtz0Hfs7a7G3vxrnrwyc=
Loading

0 comments on commit 10a4b17

Please sign in to comment.