Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
TheTipo01 committed Aug 3, 2020
1 parent 88a4779 commit e725149
Show file tree
Hide file tree
Showing 15 changed files with 13,965 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@

# Dependency directories (remove the comment below to include it)
# vendor/

.idea/
temp/
dist/
!balcon.exe
!libsamplerate.dll
42 changes: 42 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
before:
hooks:
- go mod download
- go generate ./...
builds:
- env:
- CGO_ENABLED=0
goos:
- windows
- linux
- darwin
goarch:
- amd64
- 386
- arm
- arm64
archives:
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
files:
- README.md
- LICENSE
- example_config.yml
- gen.bat
- gen.sh
- balcon.exe
- libsamplerate.dll
- parole.txt
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
# roberto
DIscord TTS bot

Discord TTS bot

Dependencies: [DCA](https://github.com/bwmarrin/dca/tree/master/cmd/dca), [youtube-dl](https://youtube-dl.org/) and [ffmpeg](https://ffmpeg.org/download.html) and Roberto SAPI voice

For download, see releases.
Binary file added balcon.exe
Binary file not shown.
5 changes: 5 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#Example configuration file, remember to remove "example_" from the file after adding your tokens!
#For getting a discord token: https://discord.com/developers/applications

#Bot token
token: NTg3NzYxOTE4ODY1ODM0MDIz.XQGVbg.LG-nX9WuQiFKwN5M1CUQ_Uo7oG8
5 changes: 5 additions & 0 deletions example_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#Example configuration file, remember to remove "example_"!
#For getting a discord token: https://discord.com/developers/applications

#Bot token, get one from https://discord.com/developers/applications
token:
2 changes: 2 additions & 0 deletions gen.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
balcon -w temp\%1.wav -n Roberto -t %2
ffmpeg -i ./temp/%1.wav -f s16le -ar 48000 -ac 2 pipe:1 | dca > ./temp/%1.dca
4 changes: 4 additions & 0 deletions gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
cd temp
WINEPREFIX=~/.PlayOnLinux/wineprefix/tts wine c:/balcon/balcon -w $1.wav -n Roberto -t "$2"
ffmpeg -i $1.wav -f s16le -ar 48000 -ac 2 pipe:1 | dca > $1.dca
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module ttsBestemmie

go 1.14

require (
github.com/bwmarrin/discordgo v0.22.0
github.com/spf13/viper v1.7.1
)
298 changes: 298 additions & 0 deletions go.sum

Large diffs are not rendered by default.

Binary file added libsamplerate.dll
Binary file not shown.
270 changes: 270 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package main

import (
"crypto/sha1"
"encoding/base32"
"encoding/binary"
"fmt"
"github.com/bwmarrin/discordgo"
"github.com/spf13/viper"
"io"
"io/ioutil"
"math/rand"
"os"
"os/exec"
"os/signal"
"runtime"
"strings"
"sync"
"syscall"
"time"
)

var (
token string
server = make(map[string]*sync.Mutex)
stop = make(map[string]bool)
b []string
a = [3]string{"Dio", "Gesu", "Madonna"}
)

func bestemmia() string {

s1 := a[rand.Intn(len(a))]

s := s1 + " " + b[rand.Intn(len(b))]

if s1 == "Madonna" {
s = s[:len(s)-2] + "a"
}

return s
}

func gen(bestemmia string, uuid string) {
_, err := os.Stat("./temp/" + uuid + ".dca")

if err != nil {
switch runtime.GOOS {
case "linux":
cmd := exec.Command("/bin/bash", "gen.sh", uuid, bestemmia)
_ = cmd.Run()
case "windows":
cmd := exec.Command("gen.bat", uuid, bestemmia)
_ = cmd.Run()
}

err = os.Remove("./temp/" + uuid + ".wav")
if err != nil {
fmt.Println("Can't delete file", err)
}

}

}

func init() {

viper.SetConfigName("config")
viper.SetConfigType("yml")
viper.AddConfigPath(".")

viper.SetDefault("prefix", "!")

if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// Config file not found
fmt.Println("Config file not found! See example_config.yml")
return
}
} else {
//Config file found
token = viper.GetString("token")
}

//Read adjective
foo, _ := ioutil.ReadFile("parole.txt")
b = strings.Split(string(foo), "\n")

//Initialize rand
rand.Seed(time.Now().Unix())
}

func main() {

if token == "" {
fmt.Println("No token provided. Please run: ttsBestemmie -token <bot token> or modify config.yml")
return
}

// Create a new Discord session using the provided bot token.
dg, err := discordgo.New("Bot " + token)
if err != nil {
fmt.Println("Error creating Discord session: ", err)
return
}

dg.AddHandler(messageCreate)
dg.AddHandler(guildCreate)

//We set the intents that we use
dg.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuildMessages | discordgo.IntentsGuilds | discordgo.IntentsGuildVoiceStates)

// Open the websocket and begin listening.
err = dg.Open()
if err != nil {
fmt.Println("Error opening Discord session: ", err)
}

_ = dg.UpdateStatus(0, "!bestemmia")

// Wait here until CTRL-C or other term signal is received.
fmt.Println("ttsBestemmie is now running. Press CTRL-C to exit.")
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc

// Cleanly close down the Discord session.
_ = dg.Close()
}

func guildCreate(_ *discordgo.Session, event *discordgo.GuildCreate) {
server[event.ID] = &sync.Mutex{}
stop[event.ID] = true
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
//Makes the message all uppercase and replaces endlines with blank spaces
message := strings.ReplaceAll(strings.ToLower(m.Content), "\n", " ")

// check if the message is "!bestemmia"
if strings.HasPrefix(message, "!bestemmia") {
go deleteMessage(s, m)

vs := findUserVoiceState(s, m.Author.ID)
if vs != nil {
playSound(s, vs.GuildID, vs.ChannelID, genAudio(strings.ToUpper(bestemmia())))
}

return
}

if strings.HasPrefix(message, "!say") {
go deleteMessage(s, m)

vs := findUserVoiceState(s, m.Author.ID)
if vs != nil {
playSound(s, vs.GuildID, vs.ChannelID, genAudio(strings.TrimPrefix(message, "!say ")))
}

return
}

if strings.HasPrefix(message, "!stop") {
stop[m.GuildID] = false
go deleteMessage(s, m)
return
}

if strings.HasPrefix(message, "!treno") {
go deleteMessage(s, m)

vs := findUserVoiceState(s, m.Author.ID)
if vs != nil {
playSound(s, vs.GuildID, vs.ChannelID, genAudio(ricercaAndGetTreno(strings.TrimPrefix(message, "!treno "))))
}

return
}

}

// genAudio generates a dca file from a string
func genAudio(text string) string {
h := sha1.New()
h.Write([]byte(text))
uuid := strings.ToUpper(base32.HexEncoding.EncodeToString(h.Sum(nil)))

gen(text, uuid)

return uuid + ".dca"
}

// playSound plays a file to the provided channel.
func playSound(s *discordgo.Session, guildID, channelID, fileName string) {
var opuslen int16

file, err := os.Open("./temp/" + fileName)
if err != nil {
fmt.Println("Error opening dca file :", err)
return
}

//Locks the mutex for the current server
server[guildID].Lock()

// Join the provided voice channel.
vc, err := s.ChannelVoiceJoin(guildID, channelID, false, false)
if err != nil {
return
}

// Start speaking.
_ = vc.Speaking(true)
stop[guildID] = true

for {
// Read opus frame length from dca file.
err = binary.Read(file, binary.LittleEndian, &opuslen)

// If this is the end of the file, just return.
if err == io.EOF || err == io.ErrUnexpectedEOF {
err := file.Close()
if err != nil {
break
}
break
}

if err != nil {
fmt.Println("Error reading from dca file :", err)
break
}

// Read encoded pcm from dca file.
InBuf := make([]byte, opuslen)
err = binary.Read(file, binary.LittleEndian, &InBuf)

// Should not be any end of file errors
if err != nil {
fmt.Println("Error reading from dca file :", err)
break
}

// Stream data to discord
if stop[guildID] {
vc.OpusSend <- InBuf
} else {
break
}
}

//Resets the stop boolean
stop[guildID] = true

// Stop speaking
_ = vc.Speaking(false)

// Disconnect from the provided voice channel.
err = vc.Disconnect()
if err != nil {
fmt.Println("Can't disconnect from voice channel,", err)
return
}

// Releases the mutex lock for the server
server[guildID].Unlock()

}
Loading

0 comments on commit e725149

Please sign in to comment.