Contributions are always welcome to MumbleDJ. This document will give you some tips and guidelines to follow while implementing your contribution.
- Implementing a new command
- Implementing support for a new service
Commands are the portion of MumbleDJ that allows users to interact with the bot. Here is a step-by-step guide on how to implement a new command:
All commands possess their own command.go
file and command_test.go
file. These files must reside in the commands
directory.
Templates for both command implementations and command tests have been created to ensure consistency across the codebase. Please use these templates, they will make your implementation easier and will make the codebase much cleaner.
/*
* MumbleDJ
* By Matthieu Grieger
* commands/yournewcommand.go
* Copyright (c) 2016 Matthieu Grieger (MIT License)
*/
package commands
import (
"errors"
"fmt"
"github.com/layeh/gumble/gumble"
"github.com/matthieugrieger/mumbledj/interfaces"
"github.com/spf13/viper"
)
// YourNewCommand is a command... (put a short description of the command here)
type YourNewCommand struct{}
// Aliases returns the current aliases for the command.
func (c *YourNewCommand) Aliases() []string {
return viper.GetStringSlice("commands.yournewcommand.aliases")
}
// Description returns the description for the command.
func (c *YourNewCommand) Description() string {
return viper.GetString("commands.yournewcommand.description")
}
// IsAdminCommand returns true if the command is only for admin use, and
// returns false otherwise.
func (c *YourNewCommand) IsAdminCommand() bool {
return viper.GetBool("commands.yournewcommand.is_admin")
}
// Execute executes the command with the given user and arguments.
// Return value descriptions:
// string: A message to be returned to the user upon successful execution.
// bool: Whether the message should be private or not. true = private,
// false = public (sent to whole channel).
// error: An error message to be returned upon unsuccessful execution.
// If no error has occurred, pass nil instead.
// Example return statement:
// return "This is a private message!", true, nil
func (c *YourNewCommand) Execute(user *gumble.User, args ...string) (string, bool, error) {
}
/*
* MumbleDJ
* By Matthieu Grieger
* commands/yournewcommand_test.go
* Copyright (c) 2016 Matthieu Grieger (MIT License)
*/
package commands
import (
"testing"
"github.com/layeh/gumble/gumbleffmpeg"
"github.com/matthieugrieger/mumbledj/bot"
"github.com/spf13/viper"
"github.com/stretchr/testify/suite"
)
type YourNewCommandTestSuite struct {
Command YourNewCommand
suite.Suite
}
func (suite *YourNewCommandTestSuite) SetupSuite() {
DJ = bot.NewMumbleDJ()
bot.DJ = DJ
viper.Set("commands.yournewcommand.aliases", []string{"yournewcommand", "c"})
viper.Set("commands.yournewcommand.description", "yournewcommand")
viper.Set("commands.yournewcommand.is_admin", false)
}
func (suite *YourNewCommandTestSuite) SetupTest() {
DJ.Queue = bot.NewQueue()
}
func (suite *YourNewCommandTestSuite) TestAliases() {
suite.Equal([]string{"yournewcommand", "c"}, suite.Command.Aliases())
}
func (suite *YourNewCommandTestSuite) TestDescription() {
suite.Equal("yournewcommand", suite.Command.Description())
}
func (suite *YourNewCommandTestSuite) TestIsAdminCommand() {
suite.False(suite.Command.IsAdminCommand())
}
// Implement more tests here as necessary! It may be helpful to take a look
// at the stretchr/testify documentation:
// https://github.com/stretchr/testify
// Remove this comment before sending a pull request.
func TestYourNewCommandTestSuite(t *testing.T) {
suite.Run(t, new(YourNewCommandTestSuite))
}
Now the fun starts! Write the implementation for your command in the Execute()
method. Then, write tests for your new command, making sure to test each possible execution flow of your command.
For writing the implementation and unit tests for your new command, it may be helpful to look at previously created commands.
Make sure to rename the example names to represent your new command!
commands/pkg_init.go
contains a slice of enabled commands. If you do not put your command in this slice, your command will not be enabled.
Please keep the commands in alphabetical order!
Go to config.yaml
and bot/config.go
and add the necessary configuration values for your new command.
Please keep the commands in alphabetical order!
This step is very easy, but is very important. This allows the bot to store a copy of the new config.yaml
internally and use it to write to disk.
Simply execute make bindata
and this step will be taken care of!
Make sure to put information in README.md
about your new command. It would be a shame for your new command to go unnoticed!
Please keep the commands in alphabetical order!
Services are the portion of MumbleDJ that allows the bot to interact with various media services. Here is a step-by-step guide on how to implement support for a new service:
All services possess their own service.go
file. This file must reside in the services
directory.
A template for service implementations has been created to ensure consistency across the codebase. Please use this template, it will make your implementation easier and will make the codebase much cleaner.
/*
* MumbleDJ
* By Matthieu Grieger
* services/yournewservice.go
* Copyright (c) 2016 Matthieu Grieger (MIT License)
*/
package services
import (
"fmt"
"net/http"
"regexp"
"strings"
"time"
"github.com/antonholmquist/jason"
"github.com/layeh/gumble/gumble"
"github.com/matthieugrieger/mumbledj/bot"
"github.com/matthieugrieger/mumbledj/interfaces"
)
// YourNewService is a... (description here)
type YourNewService struct {
*GenericService
}
// NewYourNewServiceService returns an initialized YourNewService service object.
func NewYourNewServiceService() *YourNewService {
return &YourNewService{
&GenericService{
ReadableName: "Your new service",
Format: "bestaudio",
TrackRegex: []*regexp.Regexp{
regexp.MustCompile(`regex for track URLs in your new service`),
},
PlaylistRegex: []*regexp.Regexp{
regexp.MustCompile(`regex for playlist URLs in your new service`),
},
},
}
}
// CheckAPIKey performs a test API call with the API key
// provided in the configuration file to determine if the
// service should be enabled.
func (yn *YourNewService) CheckAPIKey() error {
}
// GetTracks uses the passed URL to find and return
// tracks associated with the URL. An error is returned
// if any error occurs during the API call.
func (yn *YourNewService) GetTracks(url string, submitter *gumble.User) ([]interfaces.Track, error) {
}
Now the fun starts! Implement CheckAPIKey()
and GetTracks()
.
For writing the implementation for your new service, it may be helpful to look at previously created service wrappers.
Make sure to rename the example names to represent your new service!
services/pkg_init.go
contains a slice of enabled services. If you do not put your service in this slice, your service will not be enabled.
Please keep the services in alphabetical order!
Some services will require an API key for users to interact with their service. If an API key is required for your service, add it to the configuration file and config.go
and run make bindata
to regenerate the bindata.go
file.
In sections of README.md
that describe which services are supported, add your new service to the list. It would be a shame for your new service to go unnoticed!
Also, if your service requires an API key, make sure to document the steps to retrieve an API key in the "Requirements" section of the README
.