mirror of
https://github.com/mgerb/go-discord-bot
synced 2026-01-11 09:32:50 +00:00
expanded permissions functionality - added packr for bundling static assets
This commit is contained in:
@@ -4,15 +4,9 @@ import jwt_decode from 'jwt-decode';
|
|||||||
import { StorageService } from '../../services';
|
import { StorageService } from '../../services';
|
||||||
import './Navbar.scss';
|
import './Navbar.scss';
|
||||||
|
|
||||||
let oauthUrl: string;
|
const baseUrl = window.location.origin + '/oauth';
|
||||||
|
|
||||||
if (!process.env.NODE_ENV) {
|
const oauthUrl = `https://discordapp.com/api/oauth2/authorize?client_id=410818759746650140&redirect_uri=${baseUrl}&response_type=code&scope=identify%20guilds`;
|
||||||
// dev
|
|
||||||
oauthUrl = `https://discordapp.com/api/oauth2/authorize?client_id=410818759746650140&redirect_uri=https%3A%2F%2Flocalhost%2Foauth&response_type=code&scope=identify%20guilds`;
|
|
||||||
} else {
|
|
||||||
// prod
|
|
||||||
oauthUrl = `https://discordapp.com/api/oauth2/authorize?client_id=271998875802402816&redirect_uri=https%3A%2F%2Fcashdiscord.com%2Foauth&response_type=code&scope=identify%20guilds%20email`;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {}
|
interface Props {}
|
||||||
|
|
||||||
@@ -47,7 +41,7 @@ export class Navbar extends React.Component<Props, State> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="Navbar">
|
<div className="Navbar">
|
||||||
<div className="Navbar__header">Cash</div>
|
<div className="Navbar__header">Sound Bot</div>
|
||||||
<NavLink exact to="/" className="Navbar__item" activeClassName="Navbar__item--active">
|
<NavLink exact to="/" className="Navbar__item" activeClassName="Navbar__item--active">
|
||||||
Home
|
Home
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export class Stats extends Component<any, IState> {
|
|||||||
return { username: k, count: v };
|
return { username: k, count: v };
|
||||||
})
|
})
|
||||||
.orderBy(v => v.count, 'desc')
|
.orderBy(v => v.count, 'desc')
|
||||||
|
.slice(0, 10)
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
this.setState({ data });
|
this.setState({ data });
|
||||||
@@ -61,8 +62,8 @@ export class Stats extends Component<any, IState> {
|
|||||||
return (
|
return (
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<div className="card" style={{ maxWidth: '1000px' }}>
|
<div className="card" style={{ maxWidth: '1000px' }}>
|
||||||
<div className="card__header">Shitposts</div>
|
<div className="card__header">Posts containing links</div>
|
||||||
<HorizontalBar data={data} height={500} />
|
<HorizontalBar data={data} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"bot_prefix": "#",
|
"bot_prefix": "#",
|
||||||
|
|
||||||
"admin_emails": ["mail@example.com"],
|
"admin_emails": ["mail@example.com"],
|
||||||
|
"mod_emails": ["mail@example.com"],
|
||||||
|
|
||||||
"jwt_key": "",
|
"jwt_key": "",
|
||||||
"server_addr": "0.0.0.0:80",
|
"server_addr": "0.0.0.0:80",
|
||||||
|
|||||||
4
makefile
4
makefile
@@ -5,7 +5,7 @@ install:
|
|||||||
go get && cd client && npm install
|
go get && cd client && npm install
|
||||||
|
|
||||||
build-server:
|
build-server:
|
||||||
go build -o bot ./main.go
|
packr build -o bot ./main.go && packr install
|
||||||
|
|
||||||
build-client:
|
build-client:
|
||||||
cd client && npm run build
|
cd client && npm run build
|
||||||
@@ -13,4 +13,4 @@ build-client:
|
|||||||
clean:
|
clean:
|
||||||
rm -rf bot ./dist
|
rm -rf bot ./dist
|
||||||
|
|
||||||
all: install build-server build-client
|
all: install build-client build-server
|
||||||
|
|||||||
93
readme.md
93
readme.md
@@ -1,51 +1,88 @@
|
|||||||
# Discord Sound Bot
|
# Discord Sound Bot
|
||||||
|
|
||||||
This is a soundboard bot for discord. The back end is in GoLang and the front end uses React.
|
A soundboard bot for discord with a Go back end and React front end.
|
||||||
|
|
||||||
<img src="http://i.imgur.com/jtAyJZ1.png"/>
|

|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
NOTE: Currently the binaries in the release package only run on linux. Check them out [here](https://github.com/mgerb/go-discord-bot/releases)
|
* [Download latest release here](https://github.com/mgerb/go-discord-bot/releases)
|
||||||
|
* Install [youtube-dl](https://github.com/rg3/youtube-dl/blob/master/README.md#installation)
|
||||||
|
* Install [ffmpeg](https://www.ffmpeg.org/download.html)
|
||||||
|
* edit your config.json file
|
||||||
|
* run the executable
|
||||||
|
|
||||||
- download bot.zip and extract everything
|
### Commands
|
||||||
- rename config.template.json to config.json
|
|
||||||
- add your bot token and preferred upload password (leave as is for no password)
|
|
||||||
- run the bot with `./bot` (you may need to use sudo if you leave it on port 80)
|
|
||||||
|
|
||||||
## Flags
|
* `clip` - clips the past minute of audio
|
||||||
|
* `summon` - summons the bot to your current channel
|
||||||
|
* `dismiss` - dismisses the bot from the server
|
||||||
|
* `<audio clip>` - play a named audio clip
|
||||||
|
|
||||||
> -p, run in production mode
|
### Uploading files
|
||||||
|
|
||||||
> -tls, run with auto tls
|
Discord oauth is used to authenticate users in order to upload files.
|
||||||
|
To get oauth working you must set up your bot client secret/id in the config.
|
||||||
|
You must also set up the redirect URI. This is needed so discord can redirect
|
||||||
|
back to your site after authentication. Discord doesn't like insecure redirects
|
||||||
|
so you will have to use a proxy for this. I prefer using [caddy](https://github.com/mholt/caddy)
|
||||||
|
with the following config.
|
||||||
|
|
||||||
## Setting up Youtube downloader
|
```
|
||||||
|
https://localhost {
|
||||||
|
tls self_signed
|
||||||
|
proxy / http://localhost:8080 {
|
||||||
|
transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
- Install [youtube-dl](https://github.com/rg3/youtube-dl/blob/master/README.md#installation)
|
For public hosting you will want to use something like this.
|
||||||
|
|
||||||
### NOTE
|
```
|
||||||
|
https://<your domain name> {
|
||||||
|
tls <your email>
|
||||||
|
proxy / http://localhost:8080 {
|
||||||
|
transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If you get a permissions error with ffmpeg on mac or linux:
|
### Clipping audio
|
||||||
`sudo chmod +x dist/ffmpeg_linux`
|
|
||||||
|
|
||||||
Sounds are stored in the `sounds` directory. You may copy files directly to this folder rather than uploading through the site.
|
If the bot is in a channel it listens to all audio. Use the `clip` command
|
||||||
|
to record the past minute of conversation. Access all clips in the "Clips"
|
||||||
|
section of the site.
|
||||||
|
|
||||||
|
### Stats
|
||||||
|
|
||||||
|
If logging is enabled the bot will log all messages and store in a database file. Currently the bot keeps track of
|
||||||
|
all messages that contain links in them. I added this because it's something we use in my discord.
|
||||||
|
Check it out in the "Stats" page on the site.
|
||||||
|
|
||||||
## Building from Source
|
## Building from Source
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
- Go
|
|
||||||
- node/npm
|
* Go
|
||||||
- make
|
* node/npm
|
||||||
|
* make
|
||||||
|
|
||||||
### Compiling
|
### Compiling
|
||||||
- Make sure dependencies are installed
|
|
||||||
- `make all`
|
* Make sure dependencies are installed
|
||||||
- Rename the `config.template.json` to `config.json`
|
* Rename the `config.template.json` to `config.json`
|
||||||
- add configurations to `config.json`
|
* add configurations to `config.json`
|
||||||
- run the executable
|
* `cd client && npm run dev`
|
||||||
- open a browser `localhost:<port>`
|
* `go run main.go`
|
||||||
- upload files
|
* open a browser `localhost:<config_port>`
|
||||||
- success!
|
|
||||||
|
[Packr](https://github.com/gobuffalo/packr) is used to bundle the static web assets into the binary.
|
||||||
|
Use these commands to compile the project. The client must be built first.
|
||||||
|
|
||||||
|
* `packr build`
|
||||||
|
* `packr install`
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
I've only compiled and run this on linux so far, but I've recently added cross platform support.
|
|
||||||
|
I only run this on linux. I'm not sure if it will work on windows, but it should without too much work.
|
||||||
|
|||||||
@@ -33,22 +33,23 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// store our connection objects in a map tied to a guild id
|
// store our connection objects in a map tied to a guild id
|
||||||
var activeConnections = make(map[string]*audioConnection)
|
var activeConnections = make(map[string]*AudioConnection)
|
||||||
|
|
||||||
type audioConnection struct {
|
// AudioConnection -
|
||||||
guild *discordgo.Guild
|
type AudioConnection struct {
|
||||||
session *discordgo.Session
|
Guild *discordgo.Guild `json:"guild"`
|
||||||
voiceConnection *discordgo.VoiceConnection
|
Session *discordgo.Session `json:"-"`
|
||||||
currentChannel *discordgo.Channel
|
VoiceConnection *discordgo.VoiceConnection `json:"-"`
|
||||||
sounds map[string]*audioClip
|
CurrentChannel *discordgo.Channel `json:"current_channel"`
|
||||||
soundQueue chan string
|
Sounds map[string]*AudioClip `json:"-"`
|
||||||
voiceClipQueue chan *discordgo.Packet
|
SoundQueue chan string `json:"-"`
|
||||||
soundPlayingLock bool
|
VoiceClipQueue chan *discordgo.Packet `json:"-"`
|
||||||
audioListenerLock bool
|
SoundPlayingLock bool `json:"-"`
|
||||||
mutex *sync.Mutex // mutex for single audio connection
|
AudioListenerLock bool `json:"-"`
|
||||||
|
Mutex *sync.Mutex `json:"-"` // mutex for single audio connection
|
||||||
}
|
}
|
||||||
|
|
||||||
type audioClip struct {
|
type AudioClip struct {
|
||||||
Name string
|
Name string
|
||||||
Extension string
|
Extension string
|
||||||
Content [][]byte
|
Content [][]byte
|
||||||
@@ -76,13 +77,13 @@ func SoundsHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create new connection instance
|
// create new connection instance
|
||||||
newInstance := &audioConnection{
|
newInstance := &AudioConnection{
|
||||||
guild: newGuild,
|
Guild: newGuild,
|
||||||
session: s,
|
Session: s,
|
||||||
sounds: make(map[string]*audioClip, 0),
|
Sounds: make(map[string]*AudioClip, 0),
|
||||||
soundQueue: make(chan string, maxSoundQueue),
|
SoundQueue: make(chan string, maxSoundQueue),
|
||||||
mutex: &sync.Mutex{},
|
Mutex: &sync.Mutex{},
|
||||||
audioListenerLock: false,
|
AudioListenerLock: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
activeConnections[c.GuildID] = newInstance
|
activeConnections[c.GuildID] = newInstance
|
||||||
@@ -95,7 +96,7 @@ func SoundsHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|||||||
go activeConnections[c.GuildID].handleMessage(m)
|
go activeConnections[c.GuildID].handleMessage(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *audioConnection) handleMessage(m *discordgo.MessageCreate) {
|
func (conn *AudioConnection) handleMessage(m *discordgo.MessageCreate) {
|
||||||
|
|
||||||
// check if valid command
|
// check if valid command
|
||||||
if strings.HasPrefix(m.Content, config.Config.BotPrefix) {
|
if strings.HasPrefix(m.Content, config.Config.BotPrefix) {
|
||||||
@@ -120,22 +121,22 @@ func (conn *audioConnection) handleMessage(m *discordgo.MessageCreate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dismiss bot from currnet channel if it's in one
|
// dismiss bot from currnet channel if it's in one
|
||||||
func (conn *audioConnection) dismiss() {
|
func (conn *AudioConnection) dismiss() {
|
||||||
if conn.voiceConnection != nil && !conn.soundPlayingLock && len(conn.soundQueue) == 0 {
|
if conn.VoiceConnection != nil && !conn.SoundPlayingLock && len(conn.SoundQueue) == 0 {
|
||||||
conn.voiceConnection.Disconnect()
|
conn.VoiceConnection.Disconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// summon bot to channel that user is currently in
|
// summon bot to channel that user is currently in
|
||||||
func (conn *audioConnection) summon(m *discordgo.MessageCreate) {
|
func (conn *AudioConnection) summon(m *discordgo.MessageCreate) {
|
||||||
|
|
||||||
// Join the channel the user issued the command from if not in it
|
// Join the channel the user issued the command from if not in it
|
||||||
if conn.voiceConnection == nil || conn.voiceConnection.ChannelID != m.ChannelID {
|
if conn.VoiceConnection == nil || conn.VoiceConnection.ChannelID != m.ChannelID {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Find the channel that the message came from.
|
// Find the channel that the message came from.
|
||||||
c, err := conn.session.State.Channel(m.ChannelID)
|
c, err := conn.Session.State.Channel(m.ChannelID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Could not find channel.
|
// Could not find channel.
|
||||||
log.Error("User channel not found.")
|
log.Error("User channel not found.")
|
||||||
@@ -143,7 +144,7 @@ func (conn *audioConnection) summon(m *discordgo.MessageCreate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the guild for that channel.
|
// Find the guild for that channel.
|
||||||
g, err := conn.session.State.Guild(c.GuildID)
|
g, err := conn.Session.State.Guild(c.GuildID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
@@ -153,19 +154,19 @@ func (conn *audioConnection) summon(m *discordgo.MessageCreate) {
|
|||||||
for _, vs := range g.VoiceStates {
|
for _, vs := range g.VoiceStates {
|
||||||
if vs.UserID == m.Author.ID {
|
if vs.UserID == m.Author.ID {
|
||||||
|
|
||||||
conn.voiceConnection, err = conn.session.ChannelVoiceJoin(g.ID, vs.ChannelID, false, false)
|
conn.VoiceConnection, err = conn.Session.ChannelVoiceJoin(g.ID, vs.ChannelID, false, false)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the current channel
|
// set the current channel
|
||||||
conn.currentChannel = c
|
conn.CurrentChannel = c
|
||||||
|
|
||||||
// start listening to audio if not locked
|
// start listening to audio if not locked
|
||||||
if !conn.audioListenerLock {
|
if !conn.AudioListenerLock {
|
||||||
go conn.startAudioListener()
|
go conn.startAudioListener()
|
||||||
conn.audioListenerLock = true
|
conn.AudioListenerLock = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -176,10 +177,10 @@ func (conn *audioConnection) summon(m *discordgo.MessageCreate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// play audio in channel that user is in
|
// play audio in channel that user is in
|
||||||
func (conn *audioConnection) playAudio(soundName string, m *discordgo.MessageCreate) {
|
func (conn *AudioConnection) playAudio(soundName string, m *discordgo.MessageCreate) {
|
||||||
|
|
||||||
// check if sound exists in memory
|
// check if sound exists in memory
|
||||||
if _, ok := conn.sounds[soundName]; !ok {
|
if _, ok := conn.Sounds[soundName]; !ok {
|
||||||
// try to load the sound if not found in memory
|
// try to load the sound if not found in memory
|
||||||
err := conn.loadFile(soundName)
|
err := conn.loadFile(soundName)
|
||||||
|
|
||||||
@@ -194,7 +195,7 @@ func (conn *audioConnection) playAudio(soundName string, m *discordgo.MessageCre
|
|||||||
|
|
||||||
// add sound to queue if queue isn't full
|
// add sound to queue if queue isn't full
|
||||||
select {
|
select {
|
||||||
case conn.soundQueue <- soundName:
|
case conn.SoundQueue <- soundName:
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
@@ -203,7 +204,7 @@ func (conn *audioConnection) playAudio(soundName string, m *discordgo.MessageCre
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load audio file into memory
|
// load audio file into memory
|
||||||
func (conn *audioConnection) loadFile(fileName string) error {
|
func (conn *AudioConnection) loadFile(fileName string) error {
|
||||||
|
|
||||||
// scan directory for file
|
// scan directory for file
|
||||||
files, _ := ioutil.ReadDir(config.Config.SoundsPath)
|
files, _ := ioutil.ReadDir(config.Config.SoundsPath)
|
||||||
@@ -250,7 +251,7 @@ func (conn *audioConnection) loadFile(fileName string) error {
|
|||||||
return errors.New("NewEncoder error.")
|
return errors.New("NewEncoder error.")
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.sounds[fileName] = &audioClip{
|
conn.Sounds[fileName] = &AudioClip{
|
||||||
Content: make([][]byte, 0),
|
Content: make([][]byte, 0),
|
||||||
Name: fileName,
|
Name: fileName,
|
||||||
Extension: fextension,
|
Extension: fextension,
|
||||||
@@ -274,17 +275,17 @@ func (conn *audioConnection) loadFile(fileName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// append sound bytes to the content for this audio file
|
// append sound bytes to the content for this audio file
|
||||||
conn.sounds[fileName].Content = append(conn.sounds[fileName].Content, opus)
|
conn.Sounds[fileName].Content = append(conn.Sounds[fileName].Content, opus)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *audioConnection) clipAudio(m *discordgo.MessageCreate) {
|
func (conn *AudioConnection) clipAudio(m *discordgo.MessageCreate) {
|
||||||
if len(conn.voiceClipQueue) < 10 {
|
if len(conn.VoiceClipQueue) < 10 {
|
||||||
conn.session.ChannelMessageSend(m.ChannelID, "Clip failed.")
|
conn.Session.ChannelMessageSend(m.ChannelID, "Clip failed.")
|
||||||
} else {
|
} else {
|
||||||
writePacketsToFile(m.Author.Username, conn.voiceClipQueue)
|
writePacketsToFile(m.Author.Username, conn.VoiceClipQueue)
|
||||||
conn.session.ChannelMessageSend(m.ChannelID, "Sound clipped!")
|
conn.Session.ChannelMessageSend(m.ChannelID, "Sound clipped!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,10 +330,10 @@ loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start listening to the voice channel
|
// start listening to the voice channel
|
||||||
func (conn *audioConnection) startAudioListener() {
|
func (conn *AudioConnection) startAudioListener() {
|
||||||
|
|
||||||
if conn.voiceClipQueue == nil {
|
if conn.VoiceClipQueue == nil {
|
||||||
conn.voiceClipQueue = make(chan *discordgo.Packet, voiceClipQueuePacketSize)
|
conn.VoiceClipQueue = make(chan *discordgo.Packet, voiceClipQueuePacketSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
speakers := make(map[uint32]*gopus.Decoder)
|
speakers := make(map[uint32]*gopus.Decoder)
|
||||||
@@ -343,7 +344,7 @@ loop:
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
// grab incomming audio
|
// grab incomming audio
|
||||||
case opusChannel, ok := <-conn.voiceConnection.OpusRecv:
|
case opusChannel, ok := <-conn.VoiceConnection.OpusRecv:
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -365,16 +366,16 @@ loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if channel is full trim off from beginning
|
// if channel is full trim off from beginning
|
||||||
if len(conn.voiceClipQueue) == cap(conn.voiceClipQueue) {
|
if len(conn.VoiceClipQueue) == cap(conn.VoiceClipQueue) {
|
||||||
<-conn.voiceClipQueue
|
<-conn.VoiceClipQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
// add current packet to channel queue
|
// add current packet to channel queue
|
||||||
conn.voiceClipQueue <- opusChannel
|
conn.VoiceClipQueue <- opusChannel
|
||||||
|
|
||||||
// check if voice connection fails then break out of audio listener
|
// check if voice connection fails then break out of audio listener
|
||||||
default:
|
default:
|
||||||
if !conn.voiceConnection.Ready {
|
if !conn.VoiceConnection.Ready {
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,31 +386,31 @@ loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove lock upon exit
|
// remove lock upon exit
|
||||||
conn.audioListenerLock = false
|
conn.AudioListenerLock = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// playSounds - plays the current buffer to the provided channel.
|
// playSounds - plays the current buffer to the provided channel.
|
||||||
func (conn *audioConnection) playSounds() (err error) {
|
func (conn *AudioConnection) playSounds() (err error) {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
newSoundName := <-conn.soundQueue
|
newSoundName := <-conn.SoundQueue
|
||||||
|
|
||||||
conn.toggleSoundPlayingLock(true)
|
conn.toggleSoundPlayingLock(true)
|
||||||
|
|
||||||
if !conn.voiceConnection.Ready {
|
if !conn.VoiceConnection.Ready {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start speaking.
|
// Start speaking.
|
||||||
_ = conn.voiceConnection.Speaking(true)
|
_ = conn.VoiceConnection.Speaking(true)
|
||||||
|
|
||||||
// Send the buffer data.
|
// Send the buffer data.
|
||||||
for _, buff := range conn.sounds[newSoundName].Content {
|
for _, buff := range conn.Sounds[newSoundName].Content {
|
||||||
conn.voiceConnection.OpusSend <- buff
|
conn.VoiceConnection.OpusSend <- buff
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop speaking
|
// Stop speaking
|
||||||
_ = conn.voiceConnection.Speaking(false)
|
_ = conn.VoiceConnection.Speaking(false)
|
||||||
|
|
||||||
// Sleep for a specificed amount of time before ending.
|
// Sleep for a specificed amount of time before ending.
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
@@ -419,10 +420,10 @@ func (conn *audioConnection) playSounds() (err error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *audioConnection) toggleSoundPlayingLock(playing bool) {
|
func (conn *AudioConnection) toggleSoundPlayingLock(playing bool) {
|
||||||
conn.mutex.Lock()
|
conn.Mutex.Lock()
|
||||||
conn.soundPlayingLock = playing
|
conn.SoundPlayingLock = playing
|
||||||
conn.mutex.Unlock()
|
conn.Mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkErr(err error) {
|
func checkErr(err error) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ type configFile struct {
|
|||||||
ClipsPath string `json:"clips_path"`
|
ClipsPath string `json:"clips_path"`
|
||||||
|
|
||||||
AdminEmails []string `json:"admin_emails"`
|
AdminEmails []string `json:"admin_emails"`
|
||||||
|
ModEmails []string `json:"mod_emails"`
|
||||||
ServerAddr string `json:"server_addr"`
|
ServerAddr string `json:"server_addr"`
|
||||||
JWTKey string `json:"jwt_key"`
|
JWTKey string `json:"jwt_key"`
|
||||||
|
|
||||||
|
|||||||
@@ -11,27 +11,34 @@ import (
|
|||||||
"gopkg.in/dgrijalva/jwt-go.v3"
|
"gopkg.in/dgrijalva/jwt-go.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// permission levels
|
||||||
|
const (
|
||||||
|
PermAdmin = 3
|
||||||
|
PermMod = 2
|
||||||
|
PermUser = 1
|
||||||
|
)
|
||||||
|
|
||||||
// CustomClaims -
|
// CustomClaims -
|
||||||
type CustomClaims struct {
|
type CustomClaims struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Discriminator string `json:"discriminator"`
|
Discriminator string `json:"discriminator"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Permissions string `json:"permissions"`
|
Permissions int `json:"permissions"`
|
||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetJWT - get json web token
|
// GetJWT - get json web token
|
||||||
func GetJWT(user discord.User) (string, error) {
|
func GetJWT(user discord.User) (string, error) {
|
||||||
|
|
||||||
permissions := "user"
|
permissions := PermUser
|
||||||
|
|
||||||
// check if email is in config admin list
|
if checkEmailPermissions(user.Email, config.Config.ModEmails) {
|
||||||
for _, email := range config.Config.AdminEmails {
|
permissions = PermMod
|
||||||
if user.Email == email {
|
|
||||||
permissions = "admin"
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if checkEmailPermissions(user.Email, config.Config.AdminEmails) {
|
||||||
|
permissions = PermAdmin
|
||||||
}
|
}
|
||||||
|
|
||||||
claims := CustomClaims{
|
claims := CustomClaims{
|
||||||
@@ -50,6 +57,31 @@ func GetJWT(user discord.User) (string, error) {
|
|||||||
return token.SignedString([]byte(config.Config.JWTKey))
|
return token.SignedString([]byte(config.Config.JWTKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkEmailPermissions(email string, emails []string) bool {
|
||||||
|
for _, e := range emails {
|
||||||
|
if email == e {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthPermissions - secure end points based on auth levels
|
||||||
|
func AuthPermissions(p int) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
cl, _ := c.Get("claims")
|
||||||
|
|
||||||
|
if claims, ok := cl.(*CustomClaims); ok {
|
||||||
|
if p <= claims.Permissions {
|
||||||
|
c.Next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unauthorizedResponse(c, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// AuthorizedJWT - jwt middleware
|
// AuthorizedJWT - jwt middleware
|
||||||
func AuthorizedJWT() gin.HandlerFunc {
|
func AuthorizedJWT() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
@@ -58,8 +90,7 @@ func AuthorizedJWT() gin.HandlerFunc {
|
|||||||
tokenString := strings.Split(c.GetHeader("Authorization"), " ")
|
tokenString := strings.Split(c.GetHeader("Authorization"), " ")
|
||||||
|
|
||||||
if len(tokenString) != 2 {
|
if len(tokenString) != 2 {
|
||||||
c.JSON(401, "Unauthorized")
|
unauthorizedResponse(c, nil)
|
||||||
c.Abort()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,9 +100,7 @@ func AuthorizedJWT() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
unauthorizedResponse(c, err)
|
||||||
c.JSON(401, "Unauthorized")
|
|
||||||
c.Abort()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,12 +108,18 @@ func AuthorizedJWT() gin.HandlerFunc {
|
|||||||
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
|
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
|
||||||
c.Set("claims", claims)
|
c.Set("claims", claims)
|
||||||
} else {
|
} else {
|
||||||
log.Error(err)
|
unauthorizedResponse(c, err)
|
||||||
c.JSON(401, "Unauthorized")
|
|
||||||
c.Abort()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unauthorizedResponse(c *gin.Context, err error) {
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
c.JSON(401, "unauthorized")
|
||||||
|
c.Abort()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package webserver
|
package webserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gobuffalo/packr"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/mgerb/go-discord-bot/server/config"
|
"github.com/mgerb/go-discord-bot/server/config"
|
||||||
"github.com/mgerb/go-discord-bot/server/webserver/handlers"
|
"github.com/mgerb/go-discord-bot/server/webserver/handlers"
|
||||||
@@ -10,13 +12,15 @@ import (
|
|||||||
func getRouter() *gin.Engine {
|
func getRouter() *gin.Engine {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|
||||||
router.Static("/static", "./dist/static")
|
box := packr.NewBox("../../dist/static")
|
||||||
|
|
||||||
|
router.StaticFS("/static", box)
|
||||||
router.Static("/public/sounds", config.Config.SoundsPath)
|
router.Static("/public/sounds", config.Config.SoundsPath)
|
||||||
router.Static("/public/youtube", "./youtube")
|
router.Static("/public/youtube", "./youtube")
|
||||||
router.Static("/public/clips", config.Config.ClipsPath)
|
router.Static("/public/clips", config.Config.ClipsPath)
|
||||||
|
|
||||||
router.NoRoute(func(c *gin.Context) {
|
router.NoRoute(func(c *gin.Context) {
|
||||||
c.File("./dist/static/index.html")
|
c.Data(200, "text/html", box.Bytes("index.html"))
|
||||||
})
|
})
|
||||||
|
|
||||||
api := router.Group("/api")
|
api := router.Group("/api")
|
||||||
|
|||||||
Reference in New Issue
Block a user