mirror of
https://github.com/stryan/mumble-discord-bridge.git
synced 2025-02-05 14:39:23 -05:00
commit
2888928c51
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,8 +2,8 @@
|
|||||||
main
|
main
|
||||||
mumble-discord-bridge
|
mumble-discord-bridge
|
||||||
dist
|
dist
|
||||||
bridge
|
|
||||||
*.prof
|
*.prof
|
||||||
*.out
|
*.out
|
||||||
*.test
|
*.test
|
||||||
cert.pem
|
cert.pem
|
||||||
|
*.gob
|
@ -7,7 +7,8 @@ before:
|
|||||||
# you may remove this if you don't need go generate
|
# you may remove this if you don't need go generate
|
||||||
# - go generate ./...
|
# - go generate ./...
|
||||||
builds:
|
builds:
|
||||||
- ldflags: '-s -w -linkmode external -extldflags "-static" -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser'
|
- main: ./cmd/mumble-discord-bridge
|
||||||
|
ldflags: '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser'
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=1
|
- CGO_ENABLED=1
|
||||||
flags:
|
flags:
|
||||||
|
11
Dockerfile
11
Dockerfile
@ -2,18 +2,25 @@
|
|||||||
|
|
||||||
# Stage 1
|
# Stage 1
|
||||||
|
|
||||||
FROM golang:1.15 as builder
|
FROM golang:1.16 as builder
|
||||||
WORKDIR /go/src/app
|
WORKDIR /go/src/app
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | sh
|
RUN curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | sh
|
||||||
|
RUN apt update && apt install -y libopus-dev
|
||||||
RUN ./bin/goreleaser build --skip-validate
|
RUN ./bin/goreleaser build --skip-validate
|
||||||
|
|
||||||
# Stage 2
|
# Stage 2
|
||||||
|
|
||||||
FROM alpine:latest as static
|
FROM alpine:latest as final
|
||||||
WORKDIR /opt/
|
WORKDIR /opt/
|
||||||
RUN apk add opus
|
RUN apk add opus
|
||||||
|
RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
|
||||||
COPY --from=builder /go/src/app/dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge .
|
COPY --from=builder /go/src/app/dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge .
|
||||||
|
|
||||||
|
# FROM ubuntu:latest as final
|
||||||
|
# WORKDIR /opt/
|
||||||
|
# RUN apt update && apt install -y libopus0 ca-certificates && apt clean
|
||||||
|
# COPY --from=builder /go/src/app/dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge .
|
||||||
|
|
||||||
# Entry Point
|
# Entry Point
|
||||||
CMD ["/opt/mumble-discord-bridge"]
|
CMD ["/opt/mumble-discord-bridge"]
|
||||||
|
19
Makefile
19
Makefile
@ -1,26 +1,29 @@
|
|||||||
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)
|
mumble-discord-bridge: $(GOFILES) .goreleaser.yml
|
||||||
goreleaser build --skip-validate --rm-dist
|
goreleaser build --skip-validate --rm-dist
|
||||||
|
|
||||||
dev: $(GOFILES)
|
dev: $(GOFILES) .goreleaser.yml
|
||||||
goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge
|
goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge
|
||||||
|
|
||||||
dev-race: $(GOFILES)
|
dev-race: $(GOFILES) .goreleaser.yml
|
||||||
go run -race $(GOFILES)
|
go run -race ./cmd/mumble-discord-bridge
|
||||||
|
|
||||||
dev-profile: $(GOFILES)
|
dev-profile: $(GOFILES) .goreleaser.yml
|
||||||
goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge -cpuprofile cpu.prof
|
goreleaser build --skip-validate --rm-dist && sudo ./dist/mumble-discord-bridge_linux_amd64/mumble-discord-bridge -cpuprofile cpu.prof
|
||||||
|
|
||||||
test-chart: SHELL:=/bin/bash
|
test-chart: SHELL:=/bin/bash
|
||||||
test-chart:
|
test-chart:
|
||||||
go test &
|
go test ./test &
|
||||||
until pidof mumble-discord-bridge.test; do continue; done;
|
until pidof test.test; do continue; done;
|
||||||
psrecord --plot docs/test-cpu-memory.png $$(pidof mumble-discord-bridge.test)
|
psrecord --plot docs/test-cpu-memory.png $$(pidof mumble-discord-bridge.test)
|
||||||
|
|
||||||
docker-latest:
|
docker-latest:
|
||||||
docker build -t stieneee/mumble-discord-bridge:latest .
|
docker build -t stieneee/mumble-discord-bridge:latest .
|
||||||
|
|
||||||
|
docker-latest-run:
|
||||||
|
docker run --env-file .env -it stieneee/mumble-discord-bridge:latest
|
||||||
|
|
||||||
docker-next:
|
docker-next:
|
||||||
docker build -t stieneee/mumble-discord-bridge:next .
|
docker build -t stieneee/mumble-discord-bridge:next .
|
||||||
docker push stieneee/mumble-discord-bridge:next
|
docker push stieneee/mumble-discord-bridge:next
|
||||||
|
74
config.go
74
config.go
@ -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
|
|
||||||
}
|
|
9
go.mod
9
go.mod
@ -3,14 +3,11 @@ module github.com/stieneee/mumble-discord-bridge
|
|||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bwmarrin/discordgo v0.23.2
|
github.com/bwmarrin/discordgo v0.23.3-0.20210512035133-7d7206b01bb5
|
||||||
github.com/golang/protobuf v1.4.3 // indirect
|
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
|
github.com/stieneee/gopus v0.0.0-20210424193312-6d10f6090335
|
||||||
|
github.com/stieneee/gumble v0.0.0-20210424210604-732f48b5e0de
|
||||||
github.com/stieneee/tickerct v0.0.0-20210420020607-d1b092aa40e9
|
github.com/stieneee/tickerct v0.0.0-20210420020607-d1b092aa40e9
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
|
||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 // indirect
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 // indirect
|
||||||
google.golang.org/protobuf v1.25.0 // indirect
|
|
||||||
layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa
|
|
||||||
layeh.com/gumble v0.0.0-20200818122324-146f9205029b
|
|
||||||
)
|
)
|
||||||
|
109
go.sum
109
go.sum
@ -1,79 +1,29 @@
|
|||||||
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
github.com/bwmarrin/discordgo v0.23.3-0.20210512035133-7d7206b01bb5 h1:VtiZMSjY2N6XpM1luSchBVX76QURpS0HA7BffVuHOCo=
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
github.com/bwmarrin/discordgo v0.23.3-0.20210512035133-7d7206b01bb5/go.mod h1:OMKxbTmkKofBjBi4/yidO3ItxbJ6PUfEUkjchM4En8c=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
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/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
|
|
||||||
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/dchote/go-openal v0.0.0-20171116030048-f4a9a141d372 h1:tz3KnXWtRZR0RWOfcMNOw+HHezWLQa7vfSOWTtKjchI=
|
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/dchote/go-openal v0.0.0-20171116030048-f4a9a141d372/go.mod h1:74z+CYu2/mx4N+mcIS/rsvfAxBPBV9uv8zRAnwyFkdI=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
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/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
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/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
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/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
github.com/stieneee/gopus v0.0.0-20210424193312-6d10f6090335 h1:yzwz6AqGKysli5du4CrQ48BMGUCSkrl7V7Kbo9VaG8w=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/stieneee/gopus v0.0.0-20210424193312-6d10f6090335/go.mod h1:tAKYr3fSBJGold7c9DMPlhupn9oy8hTgl3cZ0hoyRQs=
|
||||||
|
github.com/stieneee/gumble v0.0.0-20210424210604-732f48b5e0de h1:4dWOeXRnba4jHVa3KuWf7i/GOIAlBMR3euVTUXOey2I=
|
||||||
|
github.com/stieneee/gumble v0.0.0-20210424210604-732f48b5e0de/go.mod h1:hVIsmrlrudlx2HJbsDkIZI4crkv6NHSau0ldEWbQI/Y=
|
||||||
github.com/stieneee/tickerct v0.0.0-20210420020607-d1b092aa40e9 h1:0l2H6Oj6JHMmkqm9xaBMQA5MOGhPT+Nn/thlTUcD9Iw=
|
github.com/stieneee/tickerct v0.0.0-20210420020607-d1b092aa40e9 h1:0l2H6Oj6JHMmkqm9xaBMQA5MOGhPT+Nn/thlTUcD9Iw=
|
||||||
github.com/stieneee/tickerct v0.0.0-20210420020607-d1b092aa40e9/go.mod h1:54+oZlabriEpT52rPAjAeEWUFgYqv325LrS3hNxHGFE=
|
github.com/stieneee/tickerct v0.0.0-20210420020607-d1b092aa40e9/go.mod h1:54+oZlabriEpT52rPAjAeEWUFgYqv325LrS3hNxHGFE=
|
||||||
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=
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
|
||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -81,39 +31,8 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0J
|
|||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
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=
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package bridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -12,15 +12,39 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
"layeh.com/gumble/gumble"
|
"github.com/stieneee/gumble/gumble"
|
||||||
)
|
)
|
||||||
|
|
||||||
type discordUser struct {
|
type DiscordUser struct {
|
||||||
username string
|
username string
|
||||||
seen bool
|
seen bool
|
||||||
dm *discordgo.Channel
|
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
|
//BridgeState manages dynamic information about the bridge during runtime
|
||||||
type BridgeState struct {
|
type BridgeState struct {
|
||||||
// The configuration data for this bridge
|
// The configuration data for this bridge
|
||||||
@ -42,7 +66,7 @@ type BridgeState struct {
|
|||||||
Connected bool
|
Connected bool
|
||||||
|
|
||||||
// The bridge mode constant, auto, manual. Default is constant.
|
// The bridge mode constant, auto, manual. Default is constant.
|
||||||
Mode bridgeMode
|
Mode BridgeMode
|
||||||
|
|
||||||
// Discord session. This is created and outside the bridge state
|
// Discord session. This is created and outside the bridge state
|
||||||
DiscordSession *discordgo.Session
|
DiscordSession *discordgo.Session
|
||||||
@ -54,7 +78,7 @@ type BridgeState struct {
|
|||||||
MumbleClient *gumble.Client
|
MumbleClient *gumble.Client
|
||||||
|
|
||||||
// Map of Discord users tracked by this bridge.
|
// Map of Discord users tracked by this bridge.
|
||||||
DiscordUsers map[string]discordUser
|
DiscordUsers map[string]DiscordUser
|
||||||
DiscordUsersMutex sync.Mutex
|
DiscordUsersMutex sync.Mutex
|
||||||
|
|
||||||
// Map of Mumble users tracked by this bridge
|
// Map of Mumble users tracked by this bridge
|
||||||
@ -80,7 +104,7 @@ type BridgeState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startBridge established the voice connection
|
// startBridge established the voice connection
|
||||||
func (b *BridgeState) startBridge() {
|
func (b *BridgeState) StartBridge() {
|
||||||
b.lock.Lock()
|
b.lock.Lock()
|
||||||
defer b.lock.Unlock()
|
defer b.lock.Unlock()
|
||||||
|
|
||||||
@ -214,10 +238,10 @@ func (b *BridgeState) startBridge() {
|
|||||||
b.MumbleUsersMutex.Lock()
|
b.MumbleUsersMutex.Lock()
|
||||||
b.MumbleUsers = make(map[string]bool)
|
b.MumbleUsers = make(map[string]bool)
|
||||||
b.MumbleUsersMutex.Unlock()
|
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")
|
m, _ := time.ParseDuration("30s")
|
||||||
for {
|
for {
|
||||||
time.Sleep(3 * time.Second)
|
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 is at least one user on both, starts up the bridge
|
||||||
// when there are no users on either side, kills the bridge
|
// when there are no users on either side, kills the bridge
|
||||||
func (b *BridgeState) AutoBridge() {
|
func (b *BridgeState) AutoBridge() {
|
||||||
log.Println("beginning auto mode")
|
log.Println("Beginning auto mode")
|
||||||
ticker := time.NewTicker(3 * time.Second)
|
ticker := time.NewTicker(3 * time.Second)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
case <-b.AutoChanDie:
|
case <-b.AutoChanDie:
|
||||||
log.Println("ending automode")
|
log.Println("Ending automode")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,11 +294,11 @@ func (b *BridgeState) AutoBridge() {
|
|||||||
b.BridgeMutex.Lock()
|
b.BridgeMutex.Lock()
|
||||||
|
|
||||||
if !b.Connected && b.MumbleUserCount > 0 && len(b.DiscordUsers) > 0 {
|
if !b.Connected && b.MumbleUserCount > 0 && len(b.DiscordUsers) > 0 {
|
||||||
log.Println("users detected in mumble and discord, bridging")
|
log.Println("Users detected in mumble and discord, bridging")
|
||||||
go b.startBridge()
|
go b.StartBridge()
|
||||||
}
|
}
|
||||||
if b.Connected && b.MumbleUserCount == 0 && len(b.DiscordUsers) <= 1 {
|
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
|
b.BridgeDie <- true
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package bridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -15,11 +15,11 @@ type DiscordListener struct {
|
|||||||
Bridge *BridgeState
|
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")
|
log.Println("CREATE event registered")
|
||||||
|
|
||||||
if event.ID != l.Bridge.BridgeConfig.GID {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ func (l *DiscordListener) guildCreate(s *discordgo.Session, event *discordgo.Gui
|
|||||||
|
|
||||||
u, err := s.User(vs.UserID)
|
u, err := s.User(vs.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error looking up username")
|
log.Println("Error looking up username")
|
||||||
}
|
}
|
||||||
|
|
||||||
dm, err := s.UserChannelCreate(u.ID)
|
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.DiscordUsersMutex.Lock()
|
||||||
l.Bridge.DiscordUsers[vs.UserID] = discordUser{
|
l.Bridge.DiscordUsers[vs.UserID] = DiscordUser{
|
||||||
username: u.Username,
|
username: u.Username,
|
||||||
seen: true,
|
seen: true,
|
||||||
dm: dm,
|
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
|
// Ignore all messages created by the bot itself
|
||||||
if m.Author.ID == s.State.User.ID {
|
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
|
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")
|
l.Bridge.DiscordSession.ChannelMessageSend(m.ChannelID, "Constant mode enabled, manual commands can not be entered")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
|
|||||||
if vs.UserID == m.Author.ID {
|
if vs.UserID == m.Author.ID {
|
||||||
log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID)
|
log.Printf("Trying to join GID %v and VID %v\n", g.ID, vs.ChannelID)
|
||||||
l.Bridge.DiscordChannelID = vs.ChannelID
|
l.Bridge.DiscordChannelID = vs.ChannelID
|
||||||
go l.Bridge.startBridge()
|
go l.Bridge.StartBridge()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,16 +135,16 @@ func (l *DiscordListener) messageCreate(s *discordgo.Session, m *discordgo.Messa
|
|||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
go l.Bridge.startBridge()
|
go l.Bridge.StartBridge()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(m.Content, prefix+" auto") {
|
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.DiscordSession.ChannelMessageSend(m.ChannelID, "Auto mode enabled")
|
||||||
l.Bridge.Mode = bridgeModeAuto
|
l.Bridge.Mode = BridgeModeAuto
|
||||||
l.Bridge.DiscordChannelID = l.Bridge.BridgeConfig.CID
|
l.Bridge.DiscordChannelID = l.Bridge.BridgeConfig.CID
|
||||||
l.Bridge.AutoChanDie = make(chan bool)
|
l.Bridge.AutoChanDie = make(chan bool)
|
||||||
go l.Bridge.AutoBridge()
|
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.DiscordSession.ChannelMessageSend(m.ChannelID, "Auto mode disabled")
|
||||||
l.Bridge.DiscordChannelID = ""
|
l.Bridge.DiscordChannelID = ""
|
||||||
l.Bridge.AutoChanDie <- true
|
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()
|
l.Bridge.DiscordUsersMutex.Lock()
|
||||||
defer l.Bridge.DiscordUsersMutex.Unlock()
|
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)
|
g, err := s.State.Guild(l.Bridge.BridgeConfig.GID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error finding guild")
|
log.Println("Error finding guild")
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
|
|||||||
|
|
||||||
u, err := s.User(vs.UserID)
|
u, err := s.User(vs.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error looking up username")
|
log.Println("Error looking up username")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ func (l *DiscordListener) voiceUpdate(s *discordgo.Session, event *discordgo.Voi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error creating private channel for", u.Username)
|
log.Println("Error creating private channel for", u.Username)
|
||||||
}
|
}
|
||||||
l.Bridge.DiscordUsers[vs.UserID] = discordUser{
|
l.Bridge.DiscordUsers[vs.UserID] = DiscordUser{
|
||||||
username: u.Username,
|
username: u.Username,
|
||||||
seen: true,
|
seen: true,
|
||||||
dm: dm,
|
dm: dm,
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package bridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -8,15 +8,18 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
"layeh.com/gopus"
|
"github.com/stieneee/gopus"
|
||||||
"layeh.com/gumble/gumble"
|
"github.com/stieneee/gumble/gumble"
|
||||||
_ "layeh.com/gumble/opus"
|
"github.com/stieneee/mumble-discord-bridge/pkg/sleepct"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fromDiscord struct {
|
type fromDiscord struct {
|
||||||
decoder *gopus.Decoder
|
decoder *gopus.Decoder
|
||||||
pcm chan []int16
|
pcm chan []int16
|
||||||
streaming bool
|
receiving bool // is used to to track the assumption that we are streaming a continuos stream form discord
|
||||||
|
streaming bool // The buffer streaming is streaming out
|
||||||
|
lastSequence uint16
|
||||||
|
lastTimeStamp uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscordDuplex Handle discord voice stream
|
// DiscordDuplex Handle discord voice stream
|
||||||
@ -57,15 +60,9 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup,
|
|||||||
|
|
||||||
// Generate Opus Silence Frame
|
// Generate Opus Silence Frame
|
||||||
opusSilence := []byte{0xf8, 0xff, 0xfe}
|
opusSilence := []byte{0xf8, 0xff, 0xfe}
|
||||||
for i := 3; i < frameSize; i++ {
|
|
||||||
opusSilence = append(opusSilence, 0x00)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ticker := NewTickerCT(20 * time.Millisecond)
|
sleepTick := sleepct.SleepCT{}
|
||||||
sleepTick := SleepCT{
|
sleepTick.Start(20 * time.Millisecond)
|
||||||
d: 20 * time.Millisecond,
|
|
||||||
t: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
lastReady := true
|
lastReady := true
|
||||||
var readyTimeout *time.Timer
|
var readyTimeout *time.Timer
|
||||||
@ -79,7 +76,7 @@ func (dd *DiscordDuplex) discordSendPCM(ctx context.Context, wg *sync.WaitGroup,
|
|||||||
if lastReady {
|
if lastReady {
|
||||||
OnError(fmt.Sprintf("Discordgo not ready for opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil)
|
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() {
|
readyTimeout = time.AfterFunc(30*time.Second, func() {
|
||||||
log.Println("set ready timeout")
|
log.Println("Debug: Set ready timeout")
|
||||||
cancel()
|
cancel()
|
||||||
})
|
})
|
||||||
lastReady = false
|
lastReady = false
|
||||||
@ -164,7 +161,7 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro
|
|||||||
if lastReady {
|
if lastReady {
|
||||||
OnError(fmt.Sprintf("Discordgo not to receive opus packets. %+v : %+v", dd.Bridge.DiscordVoice.Ready, dd.Bridge.DiscordVoice.OpusSend), nil)
|
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() {
|
readyTimeout = time.AfterFunc(30*time.Second, func() {
|
||||||
log.Println("set ready timeout")
|
log.Println("Debug: Set ready timeout")
|
||||||
cancel()
|
cancel()
|
||||||
})
|
})
|
||||||
lastReady = false
|
lastReady = false
|
||||||
@ -193,46 +190,79 @@ func (dd *DiscordDuplex) discordReceivePCM(ctx context.Context, wg *sync.WaitGro
|
|||||||
}
|
}
|
||||||
|
|
||||||
dd.discordMutex.Lock()
|
dd.discordMutex.Lock()
|
||||||
|
|
||||||
_, ok = dd.fromDiscordMap[p.SSRC]
|
_, ok = dd.fromDiscordMap[p.SSRC]
|
||||||
dd.discordMutex.Unlock()
|
|
||||||
if !ok {
|
if !ok {
|
||||||
newStream := fromDiscord{}
|
newStream := fromDiscord{}
|
||||||
newStream.pcm = make(chan []int16, 100)
|
newStream.pcm = make(chan []int16, 100)
|
||||||
|
newStream.receiving = false
|
||||||
newStream.streaming = false
|
newStream.streaming = false
|
||||||
newStream.decoder, err = gopus.NewDecoder(48000, 1)
|
newStream.decoder, err = gopus.NewDecoder(48000, 1) // Decode into mono
|
||||||
if err != nil {
|
if err != nil {
|
||||||
OnError("error creating opus decoder", err)
|
OnError("error creating opus decoder", err)
|
||||||
|
dd.discordMutex.Unlock()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
dd.discordMutex.Lock()
|
|
||||||
dd.fromDiscordMap[p.SSRC] = newStream
|
dd.fromDiscordMap[p.SSRC] = newStream
|
||||||
dd.discordMutex.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dd.discordMutex.Lock()
|
s := dd.fromDiscordMap[p.SSRC]
|
||||||
p.PCM, err = dd.fromDiscordMap[p.SSRC].decoder.Decode(p.Opus, 960, false)
|
|
||||||
|
deltaT := int(p.Timestamp - s.lastTimeStamp)
|
||||||
|
if p.Sequence-s.lastSequence != 1 {
|
||||||
|
s.decoder.ResetState()
|
||||||
|
}
|
||||||
|
|
||||||
|
// oldReceiving := s.receiving
|
||||||
|
|
||||||
|
if !s.receiving || deltaT < 1 || deltaT > 960*25 {
|
||||||
|
// First packet assume deltaT
|
||||||
|
fmt.Println("replacing", deltaT, deltaT, 960)
|
||||||
|
deltaT = 960
|
||||||
|
s.receiving = true
|
||||||
|
}
|
||||||
|
|
||||||
|
s.lastTimeStamp = p.Timestamp
|
||||||
|
s.lastSequence = p.Sequence
|
||||||
|
|
||||||
|
dd.fromDiscordMap[p.SSRC] = s
|
||||||
dd.discordMutex.Unlock()
|
dd.discordMutex.Unlock()
|
||||||
|
|
||||||
|
p.PCM, err = s.decoder.Decode(p.Opus, deltaT*5, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
OnError("Error decoding opus data", err)
|
OnError("Error decoding opus data", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(p.PCM) != 960 {
|
|
||||||
log.Println("Opus size error")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// fmt.Println(p.SSRC, p.Type, deltaT, p.Sequence, p.Sequence-s.lastSequence, oldReceiving, s.streaming, len(p.Opus), len(p.PCM))
|
||||||
|
|
||||||
|
// Stereo to Mono - Testing
|
||||||
|
// if len(p.PCM) != 0 {
|
||||||
|
// mono := make([]int16, len(p.PCM)/2)
|
||||||
|
// x := 0
|
||||||
|
// for i := 0; i < len(p.PCM); i = i + 2 {
|
||||||
|
// mono[x] = (p.PCM[i] / 2) + (p.PCM[i+1] / 2)
|
||||||
|
// x++
|
||||||
|
// }
|
||||||
|
// p.PCM = mono[:]
|
||||||
|
// // fmt.Println("mono resample", len(p.PCM))
|
||||||
|
// } else if len(p.PCM) == 0 {
|
||||||
|
// p.PCM = zeros[:]
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Push data into pcm channel in 10ms chunks of mono pcm data
|
||||||
dd.discordMutex.Lock()
|
dd.discordMutex.Lock()
|
||||||
select {
|
for l := 0; l < deltaT; l = l + 480 {
|
||||||
case dd.fromDiscordMap[p.SSRC].pcm <- p.PCM[0:480]:
|
var next []int16
|
||||||
default:
|
u := l + 480
|
||||||
log.Println("fromDiscordMap buffer full. Dropping packet")
|
next = p.PCM[l:u]
|
||||||
dd.discordMutex.Unlock()
|
|
||||||
continue
|
select {
|
||||||
}
|
case dd.fromDiscordMap[p.SSRC].pcm <- next:
|
||||||
select {
|
default:
|
||||||
case dd.fromDiscordMap[p.SSRC].pcm <- p.PCM[480:960]:
|
log.Println("From Discord buffer full. Dropping packet")
|
||||||
default:
|
}
|
||||||
log.Println("fromDiscordMap buffer full. Dropping packet")
|
|
||||||
}
|
}
|
||||||
dd.discordMutex.Unlock()
|
dd.discordMutex.Unlock()
|
||||||
}
|
}
|
||||||
@ -244,10 +274,10 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
|
|||||||
mumbleSilence = append(mumbleSilence, 0x00)
|
mumbleSilence = append(mumbleSilence, 0x00)
|
||||||
}
|
}
|
||||||
var speakingStart time.Time
|
var speakingStart time.Time
|
||||||
sleepTick := SleepCT{
|
|
||||||
d: 10 * time.Millisecond,
|
sleepTick := sleepct.SleepCT{}
|
||||||
t: time.Now(),
|
sleepTick.Start(10 * time.Millisecond)
|
||||||
}
|
|
||||||
sendAudio := false
|
sendAudio := false
|
||||||
toMumbleStreaming := false
|
toMumbleStreaming := false
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
@ -271,7 +301,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
|
|||||||
for i := range dd.fromDiscordMap {
|
for i := range dd.fromDiscordMap {
|
||||||
bufferLength := len(dd.fromDiscordMap[i].pcm)
|
bufferLength := len(dd.fromDiscordMap[i].pcm)
|
||||||
isStreaming := dd.fromDiscordMap[i].streaming
|
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 {
|
if !toMumbleStreaming {
|
||||||
speakingStart = time.Now()
|
speakingStart = time.Now()
|
||||||
toMumbleStreaming = true
|
toMumbleStreaming = true
|
||||||
@ -290,6 +320,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
|
|||||||
if dd.fromDiscordMap[i].streaming {
|
if dd.fromDiscordMap[i].streaming {
|
||||||
x := dd.fromDiscordMap[i]
|
x := dd.fromDiscordMap[i]
|
||||||
x.streaming = false
|
x.streaming = false
|
||||||
|
x.receiving = false // toggle this here is not optimal but there is no better location atm.
|
||||||
dd.fromDiscordMap[i] = x
|
dd.fromDiscordMap[i] = x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,7 +338,7 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
|
|||||||
select {
|
select {
|
||||||
case toMumble <- outBuf:
|
case toMumble <- outBuf:
|
||||||
case <-timeout:
|
case <-timeout:
|
||||||
log.Println("toMumble timeout. Dropping packet")
|
log.Println("To Mumble timeout. Dropping packet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -315,8 +346,8 @@ func (dd *DiscordDuplex) fromDiscordMixer(ctx context.Context, wg *sync.WaitGrou
|
|||||||
// Regular send mixed audio
|
// Regular send mixed audio
|
||||||
outBuf := make([]int16, 480)
|
outBuf := make([]int16, 480)
|
||||||
|
|
||||||
for i := 0; i < len(outBuf); i++ {
|
for j := 0; j < len(internalMixerArr); j++ {
|
||||||
for j := 0; j < len(internalMixerArr); j++ {
|
for i := 0; i < len(internalMixerArr[j]); i++ {
|
||||||
outBuf[i] += (internalMixerArr[j])[i]
|
outBuf[i] += (internalMixerArr[j])[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,10 @@
|
|||||||
package main
|
package bridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"layeh.com/gumble/gumble"
|
"github.com/stieneee/gumble/gumble"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MumbleListener Handle mumble events
|
// MumbleListener Handle mumble events
|
||||||
@ -12,7 +12,7 @@ type MumbleListener struct {
|
|||||||
Bridge *BridgeState
|
Bridge *BridgeState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *MumbleListener) mumbleConnect(e *gumble.ConnectEvent) {
|
func (l *MumbleListener) MumbleConnect(e *gumble.ConnectEvent) {
|
||||||
//join specified channel
|
//join specified channel
|
||||||
startingChannel := e.Client.Channels.Find(l.Bridge.BridgeConfig.MumbleChannel...)
|
startingChannel := e.Client.Channels.Find(l.Bridge.BridgeConfig.MumbleChannel...)
|
||||||
if startingChannel != nil {
|
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()
|
l.Bridge.MumbleUsersMutex.Lock()
|
||||||
if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) {
|
if e.Type.Has(gumble.UserChangeConnected) || e.Type.Has(gumble.UserChangeChannel) || e.Type.Has(gumble.UserChangeDisconnected) {
|
||||||
l.Bridge.MumbleUsers = make(map[string]bool)
|
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)
|
log.Println("User connected to mumble " + e.User.Name)
|
||||||
|
|
||||||
if !l.Bridge.BridgeConfig.MumbleDisableText {
|
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
|
// Tell the user who is connected to discord
|
||||||
l.Bridge.DiscordUsersMutex.Lock()
|
l.Bridge.DiscordUsersMutex.Lock()
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package bridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -6,8 +6,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"layeh.com/gumble/gumble"
|
"github.com/stieneee/gumble/gumble"
|
||||||
_ "layeh.com/gumble/opus"
|
_ "github.com/stieneee/gumble/opus"
|
||||||
|
"github.com/stieneee/mumble-discord-bridge/pkg/sleepct"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mutex sync.Mutex
|
var mutex sync.Mutex
|
||||||
@ -30,7 +31,7 @@ func (m MumbleDuplex) OnAudioStream(e *gumble.AudioStreamEvent) {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
name := e.User.Name
|
name := e.User.Name
|
||||||
log.Println("new mumble audio stream", name)
|
log.Println("New mumble audio stream", name)
|
||||||
for p := range e.C {
|
for p := range e.C {
|
||||||
// log.Println("audio packet", p.Sender.Name, len(p.AudioBuffer))
|
// 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)]
|
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) {
|
func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, toDiscord chan []int16) {
|
||||||
sleepTick := SleepCT{
|
sleepTick := sleepct.SleepCT{}
|
||||||
d: 10 * time.Millisecond,
|
sleepTick.Start(10 * time.Millisecond)
|
||||||
t: time.Now(),
|
|
||||||
}
|
|
||||||
sendAudio := false
|
sendAudio := false
|
||||||
bufferWarning := false
|
bufferWarning := false
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, t
|
|||||||
sendAudio = true
|
sendAudio = true
|
||||||
if !mumbleStreamingArr[i] {
|
if !mumbleStreamingArr[i] {
|
||||||
mumbleStreamingArr[i] = true
|
mumbleStreamingArr[i] = true
|
||||||
// log.Println("mumble starting", i)
|
// log.Println("Mumble starting", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
x1 := (<-fromMumbleArr[i])
|
x1 := (<-fromMumbleArr[i])
|
||||||
@ -82,7 +82,7 @@ func (m MumbleDuplex) fromMumbleMixer(ctx context.Context, wg *sync.WaitGroup, t
|
|||||||
} else {
|
} else {
|
||||||
if mumbleStreamingArr[i] {
|
if mumbleStreamingArr[i] {
|
||||||
mumbleStreamingArr[i] = false
|
mumbleStreamingArr[i] = false
|
||||||
// log.Println("mumble stopping", i)
|
// log.Println("Mumble stopping", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
234
main.go
234
main.go
@ -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()
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package sleepct
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -14,6 +14,15 @@ type SleepCT struct {
|
|||||||
t time.Time // last time target
|
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() {
|
func (s *SleepCT) SleepNextTarget() {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stieneee/mumble-discord-bridge/pkg/sleepct"
|
||||||
"github.com/stieneee/tickerct"
|
"github.com/stieneee/tickerct"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -107,10 +108,8 @@ func testSleepCT(wg *sync.WaitGroup) {
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
start := now
|
start := now
|
||||||
// start the ticker
|
// start the ticker
|
||||||
s := SleepCT{
|
s := sleepct.SleepCT{}
|
||||||
d: interval,
|
s.Start(interval)
|
||||||
t: time.Now(),
|
|
||||||
}
|
|
||||||
var i int64
|
var i int64
|
||||||
for i = 0; i < testCount; i++ {
|
for i = 0; i < testCount; i++ {
|
||||||
if i+1 < testCount {
|
if i+1 < testCount {
|
Loading…
x
Reference in New Issue
Block a user