2021-04-21 20:43:33 +00:00
package main
import (
2023-05-21 13:24:43 +00:00
"context"
2021-04-21 20:43:33 +00:00
"strings"
2021-10-13 12:30:45 +00:00
"time"
2021-08-19 14:40:34 +00:00
2022-12-26 20:19:48 +00:00
"github.com/pkg/errors"
2023-05-21 13:24:43 +00:00
"github.com/Luzifer/twitch-bot/v3/internal/service/access"
2022-12-26 21:38:14 +00:00
"github.com/Luzifer/twitch-bot/v3/pkg/twitch"
2022-11-02 21:38:14 +00:00
"github.com/Luzifer/twitch-bot/v3/plugins"
2021-04-21 20:43:33 +00:00
)
func init ( ) {
2023-08-25 21:37:37 +00:00
tplFuncs . Register ( "displayName" , plugins . GenericTemplateFunctionGetter ( tplTwitchDisplayName ) , plugins . TemplateFuncDocumentation {
Description : "Returns the display name the specified user set for themselves" ,
Syntax : "displayName <username> [fallback]" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ displayName "luziferus" }} - {{ displayName "notexistinguser" "foobar" }} ` ,
FakedOutput : "Luziferus - foobar" ,
} ,
} )
tplFuncs . Register ( "doesFollowLongerThan" , plugins . GenericTemplateFunctionGetter ( tplTwitchDoesFollowLongerThan ) , plugins . TemplateFuncDocumentation {
Description : "Returns whether `from` follows `to` for more than `duration`" ,
Syntax : "doesFollowLongerThan <from> <to> <duration>" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ doesFollowLongerThan "tezrian" "luziferus" "168h" }} ` ,
FakedOutput : "true" ,
} ,
} )
tplFuncs . Register ( "doesFollow" , plugins . GenericTemplateFunctionGetter ( tplTwitchDoesFollow ) , plugins . TemplateFuncDocumentation {
Description : "Returns whether `from` follows `to`" ,
Syntax : "doesFollow <from> <to>" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ doesFollow "tezrian" "luziferus" }} ` ,
FakedOutput : "true" ,
} ,
} )
tplFuncs . Register ( "followAge" , plugins . GenericTemplateFunctionGetter ( tplTwitchFollowAge ) , plugins . TemplateFuncDocumentation {
Description : "Looks up when `from` followed `to` and returns the duration between then and now" ,
Syntax : "followAge <from> <to>" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ followAge "tezrian" "luziferus" }} ` ,
FakedOutput : "15004h14m59.116620989s" ,
} ,
} )
tplFuncs . Register ( "followDate" , plugins . GenericTemplateFunctionGetter ( tplTwitchFollowDate ) , plugins . TemplateFuncDocumentation {
Description : "Looks up when `from` followed `to`" ,
Syntax : "followDate <from> <to>" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ followDate "tezrian" "luziferus" }} ` ,
FakedOutput : "2021-04-10 16:07:07 +0000 UTC" ,
} ,
} )
tplFuncs . Register ( "lastPoll" , plugins . GenericTemplateFunctionGetter ( tplTwitchLastPoll ) , plugins . TemplateFuncDocumentation {
Description : "Gets the last (currently running or archived) poll for the given channel (the channel must have given extended permission for poll access!)" ,
Syntax : "lastPoll <channel>" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` Last Poll: {{ ( lastPoll .channel ) .Title }} ` ,
FakedOutput : "Last Poll: Und wie siehts im Template aus?" ,
} ,
Remarks : "See schema of returned object in [`pkg/twitch/polls.go#L13`](https://github.com/Luzifer/twitch-bot/blob/master/pkg/twitch/polls.go#L13)" ,
} )
tplFuncs . Register ( "profileImage" , plugins . GenericTemplateFunctionGetter ( tplTwitchProfileImage ) , plugins . TemplateFuncDocumentation {
Description : "Gets the URL of the given users profile image" ,
Syntax : "profileImage <username>" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ profileImage .username }} ` ,
FakedOutput : "https://static-cdn.jtvnw.net/jtv_user_pictures/[...].png" ,
} ,
} )
tplFuncs . Register ( "recentGame" , plugins . GenericTemplateFunctionGetter ( tplTwitchRecentGame ) , plugins . TemplateFuncDocumentation {
Description : "Returns the last played game name of the specified user (see shoutout example) or the `fallback` if the game could not be fetched. If no fallback was supplied the message will fail and not be sent." ,
Syntax : "recentGame <username> [fallback]" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ recentGame "luziferus" "none" }} - {{ recentGame "thisuserdoesnotexist123" "none" }} ` ,
FakedOutput : "Metro Exodus - none" ,
} ,
} )
tplFuncs . Register ( "recentTitle" , plugins . GenericTemplateFunctionGetter ( tplTwitchRecentTitle ) , plugins . TemplateFuncDocumentation {
Description : "Returns the last stream title of the specified user or the `fallback` if the title could not be fetched. If no fallback was supplied the message will fail and not be sent." ,
Syntax : "recentTitle <username> [fallback]" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ recentGame "luziferus" "none" }} - {{ recentGame "thisuserdoesnotexist123" "none" }} ` ,
FakedOutput : "Die Oper haben wir überlebt, mal sehen was uns sonst noch alles töten möchte… - none" ,
} ,
} )
tplFuncs . Register ( "streamUptime" , plugins . GenericTemplateFunctionGetter ( tplTwitchStreamUptime ) , plugins . TemplateFuncDocumentation {
Description : "Returns the duration the stream is online (causes an error if no current stream is found)" ,
Syntax : "streamUptime <username>" ,
Example : & plugins . TemplateFuncDocumentationExample {
Template : ` {{ formatDuration ( streamUptime "luziferus" ) "hours" "minutes" "" }} ` ,
FakedOutput : "3 hours, 56 minutes" ,
} ,
} )
2023-01-28 23:41:36 +00:00
}
2021-05-24 19:42:55 +00:00
2023-01-28 23:41:36 +00:00
func tplTwitchDisplayName ( username string , v ... string ) ( string , error ) {
displayName , err := twitchClient . GetDisplayNameForUser ( strings . TrimLeft ( username , "#" ) )
if len ( v ) > 0 && ( err != nil || displayName == "" ) {
2023-03-24 21:32:00 +00:00
return v [ 0 ] , nil //nolint:nilerr // Default value, no need to return error
2023-01-28 23:41:36 +00:00
}
2021-05-24 19:42:55 +00:00
2023-01-28 23:41:36 +00:00
return displayName , err
}
2022-12-26 21:38:14 +00:00
2023-01-28 23:41:36 +00:00
func tplTwitchDoesFollow ( from , to string ) ( bool , error ) {
_ , err := twitchClient . GetFollowDate ( from , to )
switch {
case err == nil :
return true , nil
2022-12-26 21:38:14 +00:00
2023-01-28 23:41:36 +00:00
case errors . Is ( err , twitch . ErrUserDoesNotFollow ) :
return false , nil
2022-12-26 21:38:14 +00:00
2023-01-28 23:41:36 +00:00
default :
return false , errors . Wrap ( err , "getting follow date" )
}
}
2022-12-26 21:38:14 +00:00
2023-01-28 23:41:36 +00:00
func tplTwitchFollowAge ( from , to string ) ( time . Duration , error ) {
since , err := twitchClient . GetFollowDate ( from , to )
return time . Since ( since ) , errors . Wrap ( err , "getting follow date" )
}
2022-12-26 21:38:14 +00:00
2023-01-28 23:41:36 +00:00
func tplTwitchFollowDate ( from , to string ) ( time . Time , error ) {
return twitchClient . GetFollowDate ( from , to )
}
2022-12-26 21:38:14 +00:00
2023-01-28 23:41:36 +00:00
func tplTwitchDoesFollowLongerThan ( from , to string , t any ) ( bool , error ) {
var (
age time . Duration
err error
)
2021-04-21 20:43:33 +00:00
2023-01-28 23:41:36 +00:00
switch v := t . ( type ) {
case int64 :
age = time . Duration ( v ) * time . Second
2021-10-13 12:30:45 +00:00
2023-01-28 23:41:36 +00:00
case string :
if age , err = time . ParseDuration ( v ) ; err != nil {
return false , errors . Wrap ( err , "parsing duration" )
2021-10-13 12:30:45 +00:00
}
2023-01-28 23:41:36 +00:00
default :
return false , errors . Errorf ( "unexpected input for duration %t" , t )
}
fd , err := twitchClient . GetFollowDate ( from , to )
switch {
case err == nil :
return time . Since ( fd ) > age , nil
case errors . Is ( err , twitch . ErrUserDoesNotFollow ) :
return false , nil
default :
return false , errors . Wrap ( err , "getting follow date" )
}
}
2023-05-21 13:24:43 +00:00
func tplTwitchLastPoll ( username string ) ( * twitch . PollInfo , error ) {
hasPollAccess , err := accessService . HasAnyPermissionForChannel ( username , twitch . ScopeChannelReadPolls , twitch . ScopeChannelManagePolls )
if err != nil {
return nil , errors . Wrap ( err , "checking read-poll-permission" )
}
if ! hasPollAccess {
return nil , errors . Errorf ( "not authorized to read polls for channel %s" , username )
}
tc , err := accessService . GetTwitchClientForChannel ( strings . TrimLeft ( username , "#" ) , access . ClientConfig {
TwitchClient : cfg . TwitchClient ,
TwitchClientSecret : cfg . TwitchClientSecret ,
} )
if err != nil {
return nil , errors . Wrap ( err , "getting twitch client for user" )
}
poll , err := tc . GetLatestPoll ( context . Background ( ) , strings . TrimLeft ( username , "#" ) )
return poll , errors . Wrap ( err , "getting last poll" )
}
2023-08-18 15:48:20 +00:00
func tplTwitchProfileImage ( username string ) ( string , error ) {
user , err := twitchClient . GetUserInformation ( strings . TrimLeft ( username , "#@" ) )
if err != nil {
return "" , errors . Wrap ( err , "getting user info" )
}
return user . ProfileImageURL , nil
}
2023-01-28 23:41:36 +00:00
func tplTwitchRecentGame ( username string , v ... string ) ( string , error ) {
game , _ , err := twitchClient . GetRecentStreamInfo ( strings . TrimLeft ( username , "#" ) )
if len ( v ) > 0 && ( err != nil || game == "" ) {
return v [ 0 ] , nil
}
return game , err
}
func tplTwitchRecentTitle ( username string , v ... string ) ( string , error ) {
_ , title , err := twitchClient . GetRecentStreamInfo ( strings . TrimLeft ( username , "#" ) )
if len ( v ) > 0 && ( err != nil || title == "" ) {
return v [ 0 ] , nil
}
return title , err
}
func tplTwitchStreamUptime ( username string ) ( time . Duration , error ) {
si , err := twitchClient . GetCurrentStreamInfo ( strings . TrimLeft ( username , "#" ) )
if err != nil {
return 0 , err
}
return time . Since ( si . StartedAt ) , nil
2021-04-21 20:43:33 +00:00
}