--- a/.hgignore Sat Apr 06 02:34:52 2019 -0500
+++ b/.hgignore Sat Apr 06 00:39:10 2019 -0700
@@ -1,34 +1,34 @@
-nowdead # ignore the binary
-# This line is a comment, and will be skipped.
-# Empty lines are skipped too.
-# Backup files left behind by the Emacs editor.
-# Lock files used by the Emacs editor.
-# Notice that the "#" character is quoted with a backslash.
-# This prevents it from being interpreted as starting a comment.
-# Temporary files used by the vim editor.
-# A hidden file created by the Mac OS X Finder.
+nowdead # ignore the binary +# This line is a comment, and will be skipped. +# Empty lines are skipped too. +# Backup files left behind by the Emacs editor. +# Lock files used by the Emacs editor. +# Notice that the "#" character is quoted with a backslash. +# This prevents it from being interpreted as starting a comment. +# Temporary files used by the vim editor. +# A hidden file created by the Mac OS X Finder. --- a/database/bitcask.go Sat Apr 06 02:34:52 2019 -0500
+++ b/database/bitcask.go Sat Apr 06 00:39:10 2019 -0700
@@ -1,35 +1,35 @@
- "github.com/prologic/bitcask"
-type db_bitcask struct {
- database *bitcask.Bitcask
- database, _ := bitcask.Open("./db")
- Register("bitcask", db_bitcask{
-func (db db_bitcask) GetChannel(guildid string) string {
- data, err := db.database.Get(guildid)
-func (db db_bitcask) SetChannel(guildid, channel string) {
- db.database.Put(guildid, []byte(channel))
-func (db db_bitcask) Close() {
+ "github.com/prologic/bitcask" +type db_bitcask struct { + database *bitcask.Bitcask + database, _ := bitcask.Open("./db") + Register("bitcask", db_bitcask{ +func (db db_bitcask) GetChannel(guildid string) string { + data, err := db.database.Get(guildid) +func (db db_bitcask) SetChannel(guildid, channel string) { + db.database.Put(guildid, []byte(channel)) +func (db db_bitcask) Close() { --- a/database/database.go Sat Apr 06 02:34:52 2019 -0500
+++ b/database/database.go Sat Apr 06 00:39:10 2019 -0700
@@ -1,21 +1,21 @@
-type Database interface {
- SetChannel(guildid, channel string)
- GetChannel(guildid string) string
-var supported map[string]Database
-func Register(name string, db Database) {
- if len(supported) <= 0 {
- supported = make(map[string]Database)
-func Get(name string) Database {
+type Database interface { + SetChannel(guildid, channel string) + GetChannel(guildid string) string +var supported map[string]Database +func Register(name string, db Database) { + if len(supported) <= 0 { + supported = make(map[string]Database) +func Get(name string) Database { --- a/database/redis.go Sat Apr 06 02:34:52 2019 -0500
+++ b/database/redis.go Sat Apr 06 00:39:10 2019 -0700
@@ -1,39 +1,39 @@
- "github.com/go-redis/redis"
- database := redis.NewClient(&redis.Options{
- Addr: "localhost:6379",
- Password: "", // no password set
- DB: 0, // use default DB
- Register("redis", db_redis{
-func (db db_redis) GetChannel(guildid string) string {
- data, err := db.database.Get(guildid).Result()
-func (db db_redis) SetChannel(guildid, channel string) {
- db.database.Set(guildid, []byte(channel), 0)
-func (db db_redis) Close() {
+ "github.com/go-redis/redis" + database := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + Register("redis", db_redis{ +func (db db_redis) GetChannel(guildid string) string { + data, err := db.database.Get(guildid).Result() +func (db db_redis) SetChannel(guildid, channel string) { + db.database.Set(guildid, []byte(channel), 0) +func (db db_redis) Close() { --- a/main.go Sat Apr 06 02:34:52 2019 -0500
+++ b/main.go Sat Apr 06 00:39:10 2019 -0700
@@ -1,250 +1,250 @@
- "bitbucket.org/TheToyz/nowdead/database"
- "github.com/dustin/go-humanize"
- "github.com/bwmarrin/discordgo"
- "github.com/nicklaw5/helix"
- TwitchClient *helix.Client
- logger = log.New(os.Stderr, " ", log.Ldate|log.Ltime)
- flag.StringVar(&DiscordToken, "t", "", "Discord Bot Token")
- flag.StringVar(&TwitchToken, "tcid", "", "Twitch Client ID")
- flag.StringVar(&DatabaseType, "db_type", "bitcask", "Database type to use (default: bitcask)")
-func logDebug(v ...interface{}) {
- logger.SetPrefix("DEBUG ")
-func logInfo(v ...interface{}) {
- logger.SetPrefix("INFO ")
-func logError(v ...interface{}) {
- logger.SetPrefix("ERROR ")
-func logPanic(v ...interface{}) {
- logger.SetPrefix("PANIC ")
- db = database.Get(DatabaseType)
- if len(DiscordToken) <= 0 || len(TwitchToken) <= 0 {
- logPanic("Discord Token and Twitch Client ID must be set")
- logDebug("Discord Token:", DiscordToken)
- logDebug("Twitch Token:", TwitchToken)
- client, err := helix.NewClient(&helix.Options{
- dg, err := discordgo.New("Bot " + strings.TrimSpace(DiscordToken))
- fmt.Println("error creating Discord session,", err)
- dg.AddHandler(presenceUpdate)
- dg.AddHandler(messageCreate)
- fmt.Println("Bot 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)
-func presenceUpdate(sess *discordgo.Session, evt *discordgo.PresenceUpdate) {
- logDebug("PRESENSE UPDATE user-ID:", evt.User.ID)
- logDebug(fmt.Sprintf("PRESENSE UPDATE game: %#v", evt.Game))
- if evt.Game.Type == discordgo.GameTypeStreaming {
- pURL, _ := url.Parse(evt.Game.URL)
- channel := db.GetChannel(evt.GuildID)
- err := processPresenceUpdate(sess, channel, strings.TrimLeft(pURL.Path, "/"), evt.Game.URL)
-func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
- if m.Author.ID == s.State.User.ID {
- commandItems := strings.Split(m.Content, " ")
- if strings.EqualFold(commandItems[0], "!uptime") {
- duration := time.Now().Sub(startTime)
- channel := db.GetChannel(m.GuildID)
- if channel != m.ChannelID {
- err := sendMessage(s, channel, fmt.Sprintf(
- "Uptime is: **%02d:%02d:%02d** (since **%s**)",
- int(duration.Minutes())%60,
- int(duration.Seconds())%60,
- startTime.Format(time.Stamp)))
- if strings.EqualFold(commandItems[0], "!isLive") {
- if len(commandItems) == 1 {
- channel := db.GetChannel(m.GuildID)
- if channel != m.ChannelID {
- err := processPresenceUpdate(s, channel, commandItems[1], "https://twitch.com/"+commandItems[1])
- sendMessage(s, channel, fmt.Sprintf("_%s_ does not seem to be live right now!", commandItems[1]))
- if strings.EqualFold(commandItems[0], "!init") {
- sendMessage(s, m.ChannelID, "All set up to talk in this channel!")
- db.SetChannel(m.GuildID, m.ChannelID)
-func sendMessage(sess *discordgo.Session, channelid, message string) error {
- logInfo("SENDING MESSAGE:", channelid, message)
- _, err := sess.ChannelMessageSend(channelid, message)
-func processPresenceUpdate(s *discordgo.Session, channelid, twitchName, url string) error {
- resp, err := TwitchClient.GetUsers(&helix.UsersParams{
- Logins: []string{twitchName},
- data, _ := TwitchClient.GetStreams(&helix.StreamsParams{
- UserLogins: []string{twitchName},
- if len(resp.Data.Users) <= 0 || len(data.Data.Streams) <= 0 {
- return errors.New("user was not found")
- user := resp.Data.Users[0]
- stream := data.Data.Streams[0]
- f := make([]*discordgo.MessageEmbedField, 0)
- f = append(f, &discordgo.MessageEmbedField{
- f = append(f, &discordgo.MessageEmbedField{
- Value: humanize.Comma(int64(stream.ViewerCount)),
- f = append(f, &discordgo.MessageEmbedField{
- Value: stream.Language,
- image := strings.Replace(stream.ThumbnailURL, "{width}", "400", -1)
- image = strings.Replace(image, "{height}", "225", -1)
- _, err = s.ChannelMessageSendEmbed(channelid, &discordgo.MessageEmbed{
- Image: &discordgo.MessageEmbedImage{
- Author: &discordgo.MessageEmbedAuthor{
- Name: user.DisplayName + " is now live",
- Thumbnail: &discordgo.MessageEmbedThumbnail{
- URL: user.ProfileImageURL,
-func fileExist(name string) bool {
- if _, err := os.Stat(name); err != nil {
- if os.IsNotExist(err) {
+ "bitbucket.org/TheToyz/nowdead/database" + "github.com/dustin/go-humanize" + "github.com/bwmarrin/discordgo" + "github.com/nicklaw5/helix" + TwitchClient *helix.Client + logger = log.New(os.Stderr, " ", log.Ldate|log.Ltime) + flag.StringVar(&DiscordToken, "t", "", "Discord Bot Token") + flag.StringVar(&TwitchToken, "tcid", "", "Twitch Client ID") + flag.StringVar(&DatabaseType, "db_type", "bitcask", "Database type to use (default: bitcask)") +func logDebug(v ...interface{}) { + logger.SetPrefix("DEBUG ") +func logInfo(v ...interface{}) { + logger.SetPrefix("INFO ") +func logError(v ...interface{}) { + logger.SetPrefix("ERROR ") +func logPanic(v ...interface{}) { + logger.SetPrefix("PANIC ") + db = database.Get(DatabaseType) + if len(DiscordToken) <= 0 || len(TwitchToken) <= 0 { + logPanic("Discord Token and Twitch Client ID must be set") + logDebug("Discord Token:", DiscordToken) + logDebug("Twitch Token:", TwitchToken) + client, err := helix.NewClient(&helix.Options{ + dg, err := discordgo.New("Bot " + strings.TrimSpace(DiscordToken)) + fmt.Println("error creating Discord session,", err) + dg.AddHandler(presenceUpdate) + dg.AddHandler(messageCreate) + fmt.Println("Bot 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) +func presenceUpdate(sess *discordgo.Session, evt *discordgo.PresenceUpdate) { + logDebug("PRESENSE UPDATE user-ID:", evt.User.ID) + logDebug(fmt.Sprintf("PRESENSE UPDATE game: %#v", evt.Game)) + if evt.Game.Type == discordgo.GameTypeStreaming { + pURL, _ := url.Parse(evt.Game.URL) + channel := db.GetChannel(evt.GuildID) + err := processPresenceUpdate(sess, channel, strings.TrimLeft(pURL.Path, "/"), evt.Game.URL) +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + if m.Author.ID == s.State.User.ID { + commandItems := strings.Split(m.Content, " ") + if strings.EqualFold(commandItems[0], "!uptime") { + duration := time.Now().Sub(startTime) + channel := db.GetChannel(m.GuildID) + if channel != m.ChannelID { + err := sendMessage(s, channel, fmt.Sprintf( + "Uptime is: **%02d:%02d:%02d** (since **%s**)", + int(duration.Minutes())%60, + int(duration.Seconds())%60, + startTime.Format(time.Stamp))) + if strings.EqualFold(commandItems[0], "!isLive") { + if len(commandItems) == 1 { + channel := db.GetChannel(m.GuildID) + if channel != m.ChannelID { + err := processPresenceUpdate(s, channel, commandItems[1], "https://twitch.com/"+commandItems[1]) + sendMessage(s, channel, fmt.Sprintf("_%s_ does not seem to be live right now!", commandItems[1])) + if strings.EqualFold(commandItems[0], "!init") { + sendMessage(s, m.ChannelID, "All set up to talk in this channel!") + db.SetChannel(m.GuildID, m.ChannelID) +func sendMessage(sess *discordgo.Session, channelid, message string) error { + logInfo("SENDING MESSAGE:", channelid, message) + _, err := sess.ChannelMessageSend(channelid, message) +func processPresenceUpdate(s *discordgo.Session, channelid, twitchName, url string) error { + resp, err := TwitchClient.GetUsers(&helix.UsersParams{ + Logins: []string{twitchName}, + data, _ := TwitchClient.GetStreams(&helix.StreamsParams{ + UserLogins: []string{twitchName}, + if len(resp.Data.Users) <= 0 || len(data.Data.Streams) <= 0 { + return errors.New("user was not found") + user := resp.Data.Users[0] + stream := data.Data.Streams[0] + f := make([]*discordgo.MessageEmbedField, 0) + f = append(f, &discordgo.MessageEmbedField{ + f = append(f, &discordgo.MessageEmbedField{ + Value: humanize.Comma(int64(stream.ViewerCount)), + f = append(f, &discordgo.MessageEmbedField{ + Value: stream.Language, + image := strings.Replace(stream.ThumbnailURL, "{width}", "400", -1) + image = strings.Replace(image, "{height}", "225", -1) + _, err = s.ChannelMessageSendEmbed(channelid, &discordgo.MessageEmbed{ + Image: &discordgo.MessageEmbedImage{ + Author: &discordgo.MessageEmbedAuthor{ + Name: user.DisplayName + " is now live", + Thumbnail: &discordgo.MessageEmbedThumbnail{ + URL: user.ProfileImageURL, +func fileExist(name string) bool { + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) {