2021-07-29 21:26:30 +00:00
package main
import (
"context"
"net/url"
"strings"
"github.com/Luzifer/go_helpers/v2/str"
"github.com/bwmarrin/discordgo"
"github.com/pkg/errors"
"github.com/robfig/cron/v3"
log "github.com/sirupsen/logrus"
)
/ *
* @ module liverole
* @ module_desc Adds live - role to certain group of users if they are streaming on Twitch
* /
func init ( ) {
RegisterModule ( "liverole" , func ( ) module { return & modLiveRole { } } )
}
type modLiveRole struct {
attrs moduleAttributeStore
discord * discordgo . Session
2021-08-07 14:16:14 +00:00
id string
2021-07-29 21:26:30 +00:00
}
2021-08-07 14:16:14 +00:00
func ( m modLiveRole ) ID ( ) string { return m . id }
func ( m * modLiveRole ) Initialize ( id string , crontab * cron . Cron , discord * discordgo . Session , attrs moduleAttributeStore ) error {
2021-07-29 21:26:30 +00:00
m . attrs = attrs
m . discord = discord
2021-08-07 14:16:14 +00:00
m . id = id
2021-07-29 21:26:30 +00:00
if err := attrs . Expect (
"role_streamers_live" ,
"twitch_client_id" ,
"twitch_client_secret" ,
) ; err != nil {
return errors . Wrap ( err , "validating attributes" )
}
discord . AddHandler ( m . handlePresenceUpdate )
return nil
}
2021-08-07 13:24:11 +00:00
func ( m modLiveRole ) Setup ( ) error { return nil }
2021-07-29 21:26:30 +00:00
func ( m modLiveRole ) addLiveStreamerRole ( guildID , userID string , presentRoles [ ] string ) error {
2021-08-24 16:02:06 +00:00
// @attr role_streamers_live required string "" Role ID to assign to live streamers (make sure the bot [can assign](https://support.discord.com/hc/en-us/articles/214836687-Role-Management-101) this role)
2021-07-29 21:26:30 +00:00
roleID := m . attrs . MustString ( "role_streamers_live" , nil )
2021-07-30 00:08:39 +00:00
if roleID == "" {
return errors . New ( "empty live-role-id" )
}
2021-07-29 21:26:30 +00:00
if str . StringInSlice ( roleID , presentRoles ) {
// Already there fine!
return nil
}
return errors . Wrap (
m . discord . GuildMemberRoleAdd ( guildID , userID , roleID ) ,
"adding role" ,
)
}
func ( m modLiveRole ) handlePresenceUpdate ( d * discordgo . Session , p * discordgo . PresenceUpdate ) {
if p . User == nil {
// The frick? Non-user presence?
return
}
2021-08-04 15:22:18 +00:00
if p . GuildID != config . GuildID {
// Bot is in multiple guilds, we don't have a config for this one
return
}
2021-08-30 20:28:12 +00:00
logger := log . WithField ( "user" , p . User . ID )
2021-07-29 21:26:30 +00:00
member , err := d . GuildMember ( p . GuildID , p . User . ID )
if err != nil {
logger . WithError ( err ) . Error ( "Unable to fetch member status for user" )
return
}
2021-08-30 20:28:12 +00:00
logger = logger . WithField ( "name" , member . User . String ( ) )
2021-07-31 21:22:10 +00:00
// @attr role_streamers optional string "" Only take members with this role ID into account
2021-07-29 21:26:30 +00:00
roleStreamer := m . attrs . MustString ( "role_streamers" , ptrStringEmpty )
if roleStreamer != "" && ! str . StringInSlice ( roleStreamer , member . Roles ) {
// User is not part of the streamer role
return
}
2021-07-31 18:58:11 +00:00
var exitFunc func ( string , string , [ ] string ) error
2021-07-29 21:26:30 +00:00
defer func ( ) {
if exitFunc != nil {
if err := exitFunc ( p . GuildID , p . User . ID , member . Roles ) ; err != nil {
logger . WithError ( err ) . Error ( "Unable to update live-streamer-role" )
}
2021-08-25 22:55:26 +00:00
logger . Debug ( "Updated live-streamer-role" )
2021-07-29 21:26:30 +00:00
}
} ( )
var activity * discordgo . Activity
for _ , a := range p . Activities {
if a . Type == discordgo . ActivityTypeStreaming {
activity = a
break
}
}
if activity == nil {
// No streaming activity: Remove role
exitFunc = m . removeLiveStreamerRole
2021-08-25 22:55:26 +00:00
logger = logger . WithFields ( log . Fields { "action" : "remove" , "reason" : "no activity" } )
2021-07-29 21:26:30 +00:00
return
}
u , err := url . Parse ( activity . URL )
if err != nil {
logger . WithError ( err ) . WithField ( "url" , activity . URL ) . Warning ( "Unable to parse activity URL" )
exitFunc = m . removeLiveStreamerRole
2021-08-25 22:55:26 +00:00
logger = logger . WithFields ( log . Fields { "action" : "remove" , "reason" : "broken activity URL" } )
2021-07-29 21:26:30 +00:00
return
}
2021-07-29 21:55:16 +00:00
if u . Host != "www.twitch.tv" {
2021-07-29 21:26:30 +00:00
logger . WithError ( err ) . WithField ( "url" , activity . URL ) . Warning ( "Activity is not on Twitch" )
exitFunc = m . removeLiveStreamerRole
2021-08-25 22:55:26 +00:00
logger = logger . WithFields ( log . Fields { "action" : "remove" , "reason" : "activity not on twitch" } )
2021-07-29 21:26:30 +00:00
return
}
twitch := newTwitchAdapter (
// @attr twitch_client_id required string "" Twitch client ID the token was issued for
m . attrs . MustString ( "twitch_client_id" , nil ) ,
// @attr twitch_client_secret required string "" Secret for the Twitch app identified with twitch_client_id
m . attrs . MustString ( "twitch_client_secret" , nil ) ,
"" , // No User-Token used
)
streams , err := twitch . GetStreamsForUser ( context . Background ( ) , strings . TrimLeft ( u . Path , "/" ) )
if err != nil {
logger . WithError ( err ) . WithField ( "user" , strings . TrimLeft ( u . Path , "/" ) ) . Warning ( "Unable to fetch streams for user" )
exitFunc = m . removeLiveStreamerRole
2021-08-25 22:55:26 +00:00
logger = logger . WithFields ( log . Fields { "action" : "remove" , "reason" : "error in getting streams" } )
2021-07-29 21:26:30 +00:00
return
}
if len ( streams . Data ) > 0 {
exitFunc = m . addLiveStreamerRole
2021-08-25 22:55:26 +00:00
logger = logger . WithFields ( log . Fields { "action" : "add" , "reason" : "stream found" } )
2021-07-29 21:26:30 +00:00
}
}
func ( m modLiveRole ) removeLiveStreamerRole ( guildID , userID string , presentRoles [ ] string ) error {
roleID := m . attrs . MustString ( "role_streamers_live" , nil )
2021-07-30 00:08:39 +00:00
if roleID == "" {
return errors . New ( "empty live-role-id" )
}
2021-07-29 21:26:30 +00:00
if ! str . StringInSlice ( roleID , presentRoles ) {
// Not there: fine!
return nil
}
return errors . Wrap (
m . discord . GuildMemberRoleRemove ( guildID , userID , roleID ) ,
"adding role" ,
)
}