Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Depado committed Nov 6, 2020
1 parent 9da2038 commit d3b73ce
Show file tree
Hide file tree
Showing 16 changed files with 1,391 additions and 0 deletions.
41 changes: 41 additions & 0 deletions .drone.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
kind: pipeline
type: docker
name: backend

steps:
- name: build
image: golang:latest
volumes:
- name: deps
path: /go
commands:
- make

- name: linter
image: golang:latest
volumes:
- name: deps
path: /go
commands:
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.32.2
- ./bin/golangci-lint run

- name: telegram
image: appleboy/drone-telegram
settings:
to: 790376882
format: markdown
token:
from_secret: telegram_token
message: >
*{{repo.name}}*
[Build {{build.number}}]({{build.link}}) by {{commit.author}} {{#success build.status}}succeeded{{else}}failed{{/success}} in {{buildtime build.started}}
`{{truncate commit.sha 8}}`: "{{commit.message}}"
volumes:
- name: deps
host:
path: /var/lib/cache/godeps/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@

# Dependency directories (remove the comment below to include it)
# vendor/
conf.yml
fox
39 changes: 39 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.DEFAULT_GOAL := build

export GO111MODULE=on
export CGO_ENABLED=0
export VERSION=$(shell git describe --abbrev=0 --tags 2> /dev/null || echo "0.1.0")
export BUILD=$(shell git rev-parse HEAD 2> /dev/null || echo "undefined")
BINARY=fox
LDFLAGS=-ldflags "-X main.Version=$(VERSION) -X main.Build=$(BUILD) -s -w"

.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

.PHONY: build
build: ## Build
go build $(LDFLAGS) -o $(BINARY)

.PHONY: compressed
compressed: fullbuild
upx --brute $(BINARY)

.PHONY: docker
docker: ## Build the docker image
docker build -t $(BINARY):latest -t $(BINARY):$(BUILD) \
--build-arg build=$(BUILD) --build-arg version=$(VERSION) \
-f Dockerfile .

.PHONY: lint
lint: ## Runs the linter
$(GOPATH)/bin/golangci-lint run --exclude-use-default=false

.PHONY: test
test: ## Run the test suite
CGO_ENABLED=1 go test -race -coverprofile="coverage.txt" ./...

.PHONY: clean
clean: ## Remove the binary
if [ -f $(BINARY) ] ; then rm $(BINARY) ; fi
if [ -f coverage.txt ] ; then rm coverage.txt ; fi
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
# fox
Discord bot playing your Soundcloud musics and playlists in a voice channel

[![Build Status](https://drone.depa.do/api/badges/Depado/fox/status.svg)](https://drone.depa.do/Depado/fox)


## Note

This project is still in an early phase and helps with the amelioration of the
[github.com/Depado/soundcloud](https://github.com/Depado/soundcloud) lib.
87 changes: 87 additions & 0 deletions bot/bot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package bot

import (
"context"
"fmt"
"math/rand"
"time"

"github.com/Depado/fox/cmd"
"github.com/Depado/soundcloud"
"github.com/bwmarrin/discordgo"
"github.com/rs/zerolog"
"go.uber.org/fx"
)

type BotInstance struct {
conf *cmd.Conf
log *zerolog.Logger
Session *discordgo.Session
Soundcloud *soundcloud.Client
Voice *discordgo.VoiceConnection
Player *Player
}

func NewBotInstance(lc fx.Lifecycle, c *cmd.Conf, log *zerolog.Logger, sc *soundcloud.Client) *BotInstance {
rand.Seed(time.Now().UnixNano())
dg, err := discordgo.New("Bot " + c.Bot.Token)
if err != nil {
log.Fatal().Err(err).Msg("unable to init")
}
dg.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged)
if err := dg.Open(); err != nil {
log.Fatal().Err(err).Msg("unable to open")
}

voice, err := dg.ChannelVoiceJoin(c.Bot.Guild, c.Bot.Channels.Voice, false, true)
if err != nil {
log.Fatal().Err(err).Msg("unable to initiate voice connection")
}

b := &BotInstance{
conf: c,
log: log,
Soundcloud: sc,
Session: dg,
Voice: voice,
Player: &Player{tracks: soundcloud.Tracks{}},
}
b.Session.AddHandler(b.MessageCreated)

lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
b.log.Debug().Msg("cleanup")
if b.Player.playing {
b.Player.stop = true
b.Player.session.Stop() // nolint:errcheck
b.Player.session.Cleanup()
}
b.Voice.Close()
b.Session.Close()
return nil
},
})

return b
}

// Start is a simple function invoked by fx to bootstrap the dependecy chain
func Start(l *zerolog.Logger, b *BotInstance) {
l.Info().Msg("Bot is now running")
}

// SalvageVoice will attempt to disconnect and reconnect to the vocal channel
func (b *BotInstance) SalvageVoice() error {
if b.Voice != nil {
if err := b.Voice.Disconnect(); err != nil {
return fmt.Errorf("unable to disonncet vocal channel: %w", err)
}
}

voice, err := b.Session.ChannelVoiceJoin(b.conf.Bot.Guild, b.conf.Bot.Channels.Voice, false, true)
if err != nil {
return fmt.Errorf("unable to establish connection to vocal channel: %w", err)
}
b.Voice = voice
return nil
}
113 changes: 113 additions & 0 deletions bot/display.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package bot

import (
"fmt"
"strconv"
"time"

"github.com/Depado/soundcloud"
"github.com/bwmarrin/discordgo"
"github.com/hako/durafmt"
"github.com/rs/zerolog/log"
)

func (b *BotInstance) SendNowPlaying(t soundcloud.Track) {
e := &discordgo.MessageEmbed{
Title: t.Title,
URL: t.PermalinkURL,
Author: &discordgo.MessageEmbedAuthor{
IconURL: t.User.AvatarURL,
Name: t.User.Username,
URL: t.User.PermalinkURL,
},
Thumbnail: &discordgo.MessageEmbedThumbnail{URL: t.ArtworkURL},
Description: "**Now Playing**",
Color: 0xff5500,
Fields: []*discordgo.MessageEmbedField{
{Name: "Plays", Value: strconv.Itoa(t.PlaybackCount), Inline: true},
{Name: "Likes", Value: strconv.Itoa(t.LikesCount), Inline: true},
{Name: "Reposts", Value: strconv.Itoa(t.RepostsCount), Inline: true},
{Name: "Duration", Value: durafmt.Parse(time.Duration(t.Duration) * time.Millisecond).LimitFirstN(2).String(), Inline: true},
},
}

_, err := b.Session.ChannelMessageSendEmbed(b.conf.Bot.Channels.Public, e)
if err != nil {
log.Err(err).Msg("unable to send embed")
}
}

func (b *BotInstance) DisplayQueue(m *discordgo.MessageCreate) {
b.Player.tracksM.Lock()
defer b.Player.tracksM.Unlock()

var body string
var tot int
if len(b.Player.tracks) > 0 {
for i, t := range b.Player.tracks {
if i <= 10 {
body += fmt.Sprintf("[%s - %s](%s)\n", t.Title, t.User.Username, t.PermalinkURL)
}
tot += t.Duration
}
if len(b.Player.tracks) > 10 {
body += fmt.Sprintf("\nAnd **%d** other tracks", len(b.Player.tracks)-10)
}
} else {
body = "There is currently no track in queue"
}

e := &discordgo.MessageEmbed{
Title: "Current Queue",
Description: body,
Color: 0xff5500,
Fields: []*discordgo.MessageEmbedField{
{Name: "Tracks", Value: strconv.Itoa(len(b.Player.tracks)), Inline: true},
{Name: "Duration", Value: durafmt.Parse(time.Duration(tot) * time.Millisecond).LimitFirstN(2).String(), Inline: true},
},
Footer: &discordgo.MessageEmbedFooter{
Text: fmt.Sprintf("Tip: Add new tracks using '%s add' or '%s a'", b.conf.Bot.Prefix, b.conf.Bot.Prefix),
},
}
_, err := b.Session.ChannelMessageSendEmbed(m.ChannelID, e)
if err != nil {
log.Err(err).Msg("unable to send embed")
}
}

func (b *BotInstance) SendPublicMessage(title, body string) {
b.SendNotice(title, body, "", b.conf.Bot.Channels.Public)
}

func (b *BotInstance) SendControlMessage(title, body string) {
b.SendNotice(title, body, "", b.conf.Bot.Channels.Control)
}

func (b *BotInstance) SendNotice(title, body, footer string, channel string) {
e := &discordgo.MessageEmbed{
Title: title,
Description: body,
Footer: &discordgo.MessageEmbedFooter{Text: footer},
Color: 0xff5500,
}
_, err := b.Session.ChannelMessageSendEmbed(channel, e)
if err != nil {
log.Err(err).Msg("unable to send embed")
}
}

func (b *BotInstance) SendNamedNotice(m *discordgo.MessageCreate, prefix, title, body, footer string) {
e := &discordgo.MessageEmbed{
Title: title,
Description: body,
Fields: []*discordgo.MessageEmbedField{
{Name: prefix, Value: fmt.Sprintf("<@%s>", m.Author.ID)},
},
Footer: &discordgo.MessageEmbedFooter{Text: footer},
Color: 0xff5500,
}
_, err := b.Session.ChannelMessageSendEmbed(m.ChannelID, e)
if err != nil {
log.Err(err).Msg("unable to send embed")
}
}
8 changes: 8 additions & 0 deletions bot/documentation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package bot

var QueueDoc = `!fox <q|queue> : Displays the current queue (todo)
!fox <q|queue> <soundcloud track or playlist URL> : Adds to queue`

// var QueueDoc = &discordgo.MessageEmbed{

// }
Loading

0 comments on commit d3b73ce

Please sign in to comment.