initial start of youtube support
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/consts/consts.go Tue Jan 21 21:10:24 2020 -0600
@@ -0,0 +1,6 @@
+ WasDeadURL = "https://bitbucket.org/rw_grim/wasdead/" --- a/discord/cmdtwitch.go Sun Nov 03 03:29:23 2019 -0600
+++ b/discord/cmdtwitch.go Tue Jan 21 21:10:24 2020 -0600
@@ -1,6 +1,8 @@
"bitbucket.org/rw_grim/wasdead/presence"
@@ -9,9 +11,12 @@
func (c *TwitchCmd) Run(g *Globals) error {
- uri := "https://twitch.tv/" + c.Target
+ provider := presence.GetProvider("twitch") + return fmt.Errorf("the twitch provider is not configured") - presence, err := presence.GetPresence(uri)
+ presence, err := provider.GetPresenceFromUsername(c.Target) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/discord/cmdyoutube.go Tue Jan 21 21:10:24 2020 -0600
@@ -0,0 +1,25 @@
+ "bitbucket.org/rw_grim/wasdead/presence" +type YoutubeCmd struct { + Target string `kong:"arg"` +func (c *YoutubeCmd) Run(g *Globals) error { + provider := presence.GetProvider("youtube") + return fmt.Errorf("the youtube provider is not configured") + presence, err := provider.GetPresenceFromUsername(c.Target) + return g.client.sendEmbedChannel(g.msg.ChannelID, presenceEmbed(presence)) --- a/discord/commands.go Sun Nov 03 03:29:23 2019 -0600
+++ b/discord/commands.go Tue Jan 21 21:10:24 2020 -0600
@@ -24,6 +24,7 @@
Status StatusCmd `kong:"cmd,help='get the streaming status of someone'"`
Twitch TwitchCmd `kong:"cmd,help='check if someone is streaming on twitch'"`
Uptime UptimeCmd `kong:"cmd,help='display how long the bot has been running for'"`
+ Youtube YoutubeCmd `kong:"cmd,help='check if someone is streaming on youtube'"` --- a/discord/presence.go Sun Nov 03 03:29:23 2019 -0600
+++ b/discord/presence.go Tue Jan 21 21:10:24 2020 -0600
@@ -7,6 +7,7 @@
"github.com/dustin/go-humanize"
log "github.com/sirupsen/logrus"
+ "bitbucket.org/rw_grim/wasdead/consts" "bitbucket.org/rw_grim/wasdead/database"
"bitbucket.org/rw_grim/wasdead/presence"
@@ -52,22 +53,32 @@
func presenceEmbed(presence presence.Presence) *discordgo.MessageEmbed {
- return &discordgo.MessageEmbed{
- Image: &discordgo.MessageEmbedImage{
- URL: presence.ProfileImageURL,
+ embed := &discordgo.MessageEmbed{ + Author: &discordgo.MessageEmbedAuthor{ + URL: consts.WasDeadURL, - Author: &discordgo.MessageEmbedAuthor{
- Name: fmt.Sprintf("%s is now live", presence.Username),
+ Description: "description", Thumbnail: &discordgo.MessageEmbedThumbnail{
URL: presence.ThumbnailURL,
- Fields: []*discordgo.MessageEmbedField{
- &discordgo.MessageEmbedField{
+ if presence.Color != 0 { + embed.Color = presence.Color + embed.Title = fmt.Sprintf("%s is live", presence.Username) + embed.Image = &discordgo.MessageEmbedImage{ + URL: presence.ProfileImageURL, &discordgo.MessageEmbedField{
Value: humanize.Comma(presence.Viewers),
@@ -78,6 +89,8 @@
Value: presence.Language,
--- a/presence/presence.go Sun Nov 03 03:29:23 2019 -0600
+++ b/presence/presence.go Tue Jan 21 21:10:24 2020 -0600
@@ -1,6 +1,13 @@
@@ -12,20 +19,13 @@
-type Provider interface {
- GetPresence(url string) (Presence, error)
+// GetPresence returns the presence for the given url +func GetPresence(url string) (Presence, error) { + for _, provider := range providers { + if provider.HandleURL(url) { + return provider.GetPresenceFromURL(url) -var providers map[string]Provider
- providers = map[string]Provider{}
+ return Presence{}, fmt.Errorf("no provider for %q", url)
-func AddProvider(hostname string, provider Provider) {
- providers[hostname] = provider
-func GetPresence(url string) (Presence, error) {
- return providers["twitch.tv"].GetPresence(url)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/presence/provider.go Tue Jan 21 21:10:24 2020 -0600
@@ -0,0 +1,35 @@
+type Provider interface { + HandleURL(url string) bool + GetPresenceFromURL(url string) (Presence, error) + GetPresenceFromUsername(username string) (Presence, error) +var providers map[string]Provider + providers = map[string]Provider{} +// AddProvider adds the given provider with the given name +func AddProvider(name string, provider Provider) { + providers[name] = provider +// NumProviders returns how many presence providers have been configured +func NumProviders() int { +// ProviderAvailable returns whether or not a +func ProviderAvailable(name string) bool { + _, found := providers[name] +// GetProvider returns the provider with the given name or nil +func GetProvider(name string) Provider { --- a/run/run.go Sun Nov 03 03:29:23 2019 -0600
+++ b/run/run.go Tue Jan 21 21:10:24 2020 -0600
@@ -13,11 +13,13 @@
"bitbucket.org/rw_grim/wasdead/globals"
"bitbucket.org/rw_grim/wasdead/presence"
"bitbucket.org/rw_grim/wasdead/twitch"
+ "bitbucket.org/rw_grim/wasdead/youtube" DiscordToken string `kong:"flag,name='discord-token',env='DISCORD_TOKEN',help='The bot token for discord',required"`
- TwitchClientID string `kong:"flag,name='twitch-client-id',env='TWITCH_CLIENT_ID',help='The Twitch client ID',required"`
+ TwitchClientID string `kong:"flag,name='twitch-client-id',env='TWITCH_CLIENT_ID',help='The Twitch client ID',optional"` + YouTubeApiKey string `kong:"flag,name='youtube-api-key',env='YOUTUBE_API_KEY',help='The Youtube api key',optional"` @@ -41,10 +43,24 @@
- presence.AddProvider("twitch.tv", provider)
+ presence.AddProvider("twitch", provider) log.Info("Added twitch provider")
+ if len(r.YouTubeApiKey) > 0 { + provider, err := youtube.New(r.YouTubeApiKey) + presence.AddProvider("youtube", provider) + log.Info("Added youtube provider") + if presence.NumProviders() == 0 { + log.Panic("no presence providers configured!") // create our error channel that's used by the various clients
errChan := make(chan error, 1)
--- a/twitch/twitch.go Sun Nov 03 03:29:23 2019 -0600
+++ b/twitch/twitch.go Tue Jan 21 21:10:24 2020 -0600
@@ -25,10 +25,26 @@
return &Twitch{client: client}, nil
-func (t *Twitch) GetPresence(uri string) (presence.Presence, error) {
+func (t *Twitch) HandleURL(uri string) bool { + parsedURL, _ := url.Parse(uri) + for _, host := range []string{"twitch.tv", "www.twitch.tv"} { + if parsedURL.Host == host { +func (t *Twitch) GetPresenceFromURL(uri string) (presence.Presence, error) { parsedURL, _ := url.Parse(uri)
username := strings.TrimLeft(parsedURL.Path, "/")
+ return t.GetPresenceFromUsername(username) +func (t *Twitch) GetPresenceFromUsername(username string) (presence.Presence, error) { users, err := t.client.GetUsers(&helix.UsersParams{
Logins: []string{username},
@@ -62,7 +78,7 @@
Language: stream.Language,
ProfileImageURL: profile_url,
ThumbnailURL: user.ProfileImageURL,
+ URL: "https://twitch.tv/" + username, --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/youtube/youtube.go Tue Jan 21 21:10:24 2020 -0600
@@ -0,0 +1,109 @@
+ "google.golang.org/api/option" + "google.golang.org/api/youtube/v3" + "bitbucket.org/rw_grim/wasdead/presence" + service *youtube.Service +func New(apiKey string) (presence.Provider, error) { + ctx := context.Background() + service, err := youtube.NewService(ctx, option.WithAPIKey(apiKey)) + return &YouTube{service: service}, nil +func (yt *YouTube) HandleURL(uri string) bool { + parsed, _ := url.Parse(uri) + for _, host := range []string{"youtube.com", "youtu.be", "www.youtube.com"} { + if parsed.Host == host { +func (yt *YouTube) getChannel(username string) (*youtube.SearchResultSnippet, error) { + call := yt.service.Search.List("id,snippet"). + if len(resp.Items) == 0 { + return nil, fmt.Errorf("no channels found matching %q", username) + return resp.Items[0].Snippet, nil +func (yt *YouTube) getLiveStreamID(channelID string) (string, error) { + call := yt.service.Search.List("id,snippet"). + if len(resp.Items) == 0 { + return resp.Items[0].Id.VideoId, nil +func (yt *YouTube) GetPresenceFromUsername(username string) (presence.Presence, error) { + p := presence.Presence{} + chanSnippet, err := yt.getChannel(username) + liveStreamID, err := yt.getLiveStreamID(chanSnippet.ChannelId) + // fill out the presence structure + p.UserID = chanSnippet.ChannelId + p.Title = chanSnippet.Title + " : " + liveStreamID + p.ThumbnailURL = chanSnippet.Thumbnails.Default.Url + p.ProfileImageURL = chanSnippet.Thumbnails.High.Url +func (yt *YouTube) GetPresenceFromURL(uri string) (presence.Presence, error) { + parsedURL, _ := url.Parse(uri) + username := strings.TrimLeft(parsedURL.Path, "/") + return yt.GetPresenceFromUsername(username)