2021-12-06 16:25:19 +00:00
< template >
< div >
< b-row >
< b-col >
2021-12-31 12:42:37 +00:00
< b-card no -body >
< b-card-header >
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'hashtag']"
/ >
Channels
< / b-card-header >
< b-list-group flush >
< b-list-group-item
v - for = "channel in sortedChannels"
: key = "channel"
class = "d-flex align-items-center align-middle"
>
< span class = "mr-auto" >
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'hashtag']"
/ >
{ { channel } }
< font-awesome-icon
v - if = "!generalConfig.channel_has_scopes[channel]"
: id = "`channelPublicWarn${channel}`"
fixed - width
class = "ml-1 text-warning"
: icon = "['fas', 'exclamation-triangle']"
/ >
< b-tooltip
: target = "`channelPublicWarn${channel}`"
triggers = "hover"
>
Channel cannot use features like channel - point redemptions .
See "Channel Permissions" for more info how to authorize .
< / b-tooltip >
< / span >
< b-button
size = "sm"
variant = "danger"
@ click = "removeChannel(channel)"
>
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'minus']"
/ >
< / b-button >
< / b-list-group-item >
< b-list-group-item >
< b-input-group >
< b-form-input
v - model = "models.addChannel"
2021-12-31 16:12:07 +00:00
: state = "!!validateUserName(models.addChannel)"
2021-12-31 12:42:37 +00:00
@ keyup . enter = "addChannel"
/ >
< b-input-group-append >
< b-button
variant = "success"
2021-12-31 16:12:07 +00:00
: disabled = "!validateUserName(models.addChannel)"
2021-12-31 12:42:37 +00:00
@ click = "addChannel"
>
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'plus']"
/ >
Add
< / b-button >
< / b-input-group-append >
< / b-input-group >
< / b-list-group-item >
< / b-list-group >
< / b-card >
< / b-col >
< b-col >
< b-card
no - body
class = "mb-3"
>
< b-card-header >
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'users']"
/ >
Bot - Editors
< / b-card-header >
< b-list-group flush >
< b-list-group-item
v - for = "editor in sortedEditors"
: key = "editor"
class = "d-flex align-items-center align-middle"
>
< b-avatar
class = "mr-3"
: src = "userProfiles[editor] ? userProfiles[editor].profile_image_url : ''"
2021-12-06 16:25:19 +00:00
/ >
2021-12-31 12:42:37 +00:00
< span class = "mr-auto" > { { userProfiles [ editor ] ? userProfiles [ editor ] . display _name : editor } } < / span >
< b-button
size = "sm"
variant = "danger"
@ click = "removeEditor(editor)"
2021-12-06 16:25:19 +00:00
>
2021-12-31 12:42:37 +00:00
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'minus']"
/ >
< / b-button >
< / b-list-group-item >
< b-list-group-item >
< b-input-group >
< b-form-input
v - model = "models.addEditor"
2021-12-31 16:12:07 +00:00
: state = "!!validateUserName(models.addEditor)"
2021-12-31 12:42:37 +00:00
@ keyup . enter = "addEditor"
/ >
< b-input-group-append >
< b-button
variant = "success"
2021-12-31 16:12:07 +00:00
: disabled = "!validateUserName(models.addEditor)"
2021-12-31 12:42:37 +00:00
@ click = "addEditor"
>
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'plus']"
/ >
Add
< / b-button >
< / b-input-group-append >
< / b-input-group >
< / b-list-group-item >
< / b-list-group >
< / b-card >
< b-card
no - body
>
< b-card-header
class = "d-flex align-items-center align-middle"
>
< span class = "mr-auto" >
2021-12-06 16:25:19 +00:00
< font-awesome-icon
fixed - width
class = "mr-1"
2021-12-31 12:42:37 +00:00
: icon = "['fas', 'ticket-alt']"
2021-12-06 16:25:19 +00:00
/ >
2021-12-31 12:42:37 +00:00
Auth - Tokens
< / span >
< b-button-group size = "sm" >
< b-button
variant = "success"
@ click = "newAPIToken"
2021-12-06 16:25:19 +00:00
>
2021-12-31 12:42:37 +00:00
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'plus']"
2021-12-06 16:25:19 +00:00
/ >
2021-12-31 12:42:37 +00:00
< / b-button >
< / b-button-group >
< / b-card-header >
< b-list-group flush >
< b-list-group-item
v - if = "createdAPIToken"
variant = "success"
>
Token was created , copy it within 30 s as you will not see it again : < br >
< code > { { createdAPIToken . token } } < / code >
< / b-list-group-item >
2021-12-06 16:25:19 +00:00
2021-12-31 12:42:37 +00:00
< b-list-group-item
v - for = "(token, uuid) in apiTokens"
: key = "uuid"
2021-12-06 16:25:19 +00:00
class = "d-flex align-items-center align-middle"
>
< span class = "mr-auto" >
2021-12-31 12:42:37 +00:00
{ { token . name } } < br >
< b-badge
v - for = "module in token.modules"
: key = "module"
> { { module === '*' ? 'ANY' : module } } < / b-badge >
< / span >
< b-button
size = "sm"
variant = "danger"
@ click = "removeAPIToken(uuid)"
>
2021-12-06 16:25:19 +00:00
< font-awesome-icon
fixed - width
class = "mr-1"
2021-12-31 12:42:37 +00:00
: icon = "['fas', 'minus']"
2021-12-06 16:25:19 +00:00
/ >
2021-12-31 12:42:37 +00:00
< / b-button >
< / b-list-group-item >
< / b-list-group >
< / b-card >
< / b-col >
< b-col >
< b-card
no - body
class = "mb-3"
: border - variant = "botConnectionCardVariant"
>
< b-card-header
class = "d-flex align-items-center align-middle"
: header - bg - variant = "botConnectionCardVariant"
>
< span class = "mr-auto" >
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'sign-in-alt']"
/ >
Bot Connection
< / span >
2021-12-31 18:09:29 +00:00
< template v-if = "generalConfig.bot_name" >
< code
id = "botUserName"
>
{ { generalConfig . bot _name } }
< b-tooltip
target = "botUserName"
triggers = "hover"
>
Twitch Login - Name of the bot user currently authorized
< / b-tooltip >
< / code >
< / template >
< template v-else >
< font-awesome-icon
id = "botUserNameDC"
fixed - width
class = "mr-1 text-danger"
: icon = "['fas', 'unlink']"
/ >
< b-tooltip
target = "botUserNameDC"
triggers = "hover"
>
Bot is not currently authorized !
< / b-tooltip >
< / template >
2021-12-31 12:42:37 +00:00
< / b-card-header >
< b-card-body >
< p >
Here you can manage your bots auth - token : it 's required to communicate with Twitch Chat and APIs. This will override the token you might have provided when starting the bot and will be automatically renewed as long as you don' t change your password or revoke the apps permission on your bot account .
< / p >
< ul >
< li > Copy the URL provided below < / li >
< li > Open an inkognito tab or different browser you are not logged into Twitch or are logged in with your bot account < / li >
< li > Open the copied URL , sign in with the bot account and accept the permissions < / li >
< li > The bot will display a message containing the authorized account . If this account is wrong , just start over , the token will be overwritten . < / li >
< / ul >
< b-input-group >
< b-form-input
placeholder = "Loading..."
readonly
: value = "authURLs.update_bot_token"
@ focus = "$event.target.select()"
/ >
< b-input-group-append >
2021-12-06 16:25:19 +00:00
< b-button
2021-12-31 12:42:37 +00:00
: variant = "copyButtonVariant.botConnection"
@ click = "copyAuthURL('botConnection')"
2021-12-06 16:25:19 +00:00
>
< font-awesome-icon
fixed - width
class = "mr-1"
2021-12-31 12:42:37 +00:00
: icon = "['fas', 'clipboard']"
2021-12-06 16:25:19 +00:00
/ >
2021-12-31 12:42:37 +00:00
Copy
2021-12-06 16:25:19 +00:00
< / b-button >
2021-12-31 12:42:37 +00:00
< / b-input-group-append >
< / b-input-group >
< / b-card-body >
< / b-card >
< b-card
no - body
class = "mb-3"
>
< b-card-header >
< font-awesome-icon
fixed - width
class = "mr-1"
: icon = "['fas', 'sign-in-alt']"
/ >
Channel Permissions
< / b-card-header >
< b-card-body >
< p >
In order to access non - public information as channel - point redemptions the bot needs additional permissions . The < strong > owner < / strong > of the channel needs to grant those !
< / p >
< ul >
< li > Copy the URL provided below < / li >
< li > Pass the URL to the channel owner and tell them to open it with their personal account logged in < / li >
< li > The bot will display a message containing the updated account < / li >
< / ul >
< b-input-group >
< b-form-input
placeholder = "Loading..."
readonly
: value = "authURLs.update_channel_scopes"
@ focus = "$event.target.select()"
/ >
< b-input-group-append >
2021-12-06 16:25:19 +00:00
< b-button
2021-12-31 12:42:37 +00:00
: variant = "copyButtonVariant.channelPermission"
@ click = "copyAuthURL('channelPermission')"
2021-12-06 16:25:19 +00:00
>
< font-awesome-icon
fixed - width
class = "mr-1"
2021-12-31 12:42:37 +00:00
: icon = "['fas', 'clipboard']"
2021-12-06 16:25:19 +00:00
/ >
2021-12-31 12:42:37 +00:00
Copy
2021-12-06 16:25:19 +00:00
< / b-button >
2021-12-31 12:42:37 +00:00
< / b-input-group-append >
< / b-input-group >
< / b-card-body >
< / b-card >
2021-12-06 16:25:19 +00:00
< / b-col >
< / b-row >
<!-- API - Token Editor -- >
< b-modal
v - if = "showAPITokenEditModal"
hide - header - close
: ok - disabled = "!validateAPIToken"
ok - title = "Save"
size = "md"
: visible = "showAPITokenEditModal"
title = "New API-Token"
@ hidden = "showAPITokenEditModal=false"
@ ok = "saveAPIToken"
>
< b-form-group
label = "Name"
label - for = "formAPITokenName"
>
< b-form-input
id = "formAPITokenName"
v - model = "models.apiToken.name"
: state = "Boolean(models.apiToken.name)"
type = "text"
/ >
< / b-form-group >
< b-form-group
label = "Enabled for Modules"
>
< b-form-checkbox-group
v - model = "models.apiToken.modules"
class = "mb-3"
: options = "availableModules"
text - field = "text"
value - field = "value"
/ >
< / b-form-group >
< / b-modal >
< / div >
< / template >
< script >
import * as constants from './const.js'
import axios from 'axios'
import Vue from 'vue'
export default {
computed : {
availableModules ( ) {
return [
{ text : 'ANY' , value : '*' } ,
... [ ... this . modules || [ ] ] . sort ( )
. filter ( m => m !== 'config-editor' )
. map ( m => ( { text : m , value : m } ) ) ,
]
} ,
2021-12-31 12:42:37 +00:00
botConnectionCardVariant ( ) {
if ( this . $parent . status . overall _status _success ) {
return 'secondary'
}
return 'warning'
} ,
2021-12-06 16:25:19 +00:00
sortedChannels ( ) {
return [ ... this . generalConfig ? . channels || [ ] ] . sort ( ( a , b ) => a . toLocaleLowerCase ( ) . localeCompare ( b . toLocaleLowerCase ( ) ) )
} ,
sortedEditors ( ) {
return [ ... this . generalConfig ? . bot _editors || [ ] ] . sort ( ( a , b ) => {
const an = this . userProfiles [ a ] ? . login || a
const bn = this . userProfiles [ b ] ? . login || b
return an . localeCompare ( bn )
} )
} ,
validateAPIToken ( ) {
return this . models . apiToken . modules . length > 0 && Boolean ( this . models . apiToken . name )
} ,
} ,
data ( ) {
return {
apiTokens : { } ,
2021-12-31 12:42:37 +00:00
authURLs : { } ,
copyButtonVariant : {
botConnection : 'primary' ,
channelPermission : 'primary' ,
} ,
2021-12-06 16:25:19 +00:00
createdAPIToken : null ,
generalConfig : { } ,
models : {
addChannel : '' ,
addEditor : '' ,
apiToken : { } ,
} ,
modules : [ ] ,
showAPITokenEditModal : false ,
userProfiles : { } ,
}
} ,
methods : {
addChannel ( ) {
2021-12-31 16:12:07 +00:00
if ( ! this . validateUserName ( this . models . addChannel ) ) {
return
}
2021-12-06 16:25:19 +00:00
this . generalConfig . channels . push ( this . models . addChannel . replace ( /^#*/ , '' ) )
this . models . addChannel = ''
this . updateGeneralConfig ( )
} ,
addEditor ( ) {
2021-12-31 16:12:07 +00:00
if ( ! this . validateUserName ( this . models . addEditor ) ) {
return
}
2021-12-06 16:25:19 +00:00
this . fetchProfile ( this . models . addEditor )
this . generalConfig . bot _editors . push ( this . models . addEditor )
this . models . addEditor = ''
this . updateGeneralConfig ( )
} ,
2021-12-31 12:42:37 +00:00
copyAuthURL ( type ) {
let prom = null
let btnField = null
switch ( type ) {
case 'botConnection' :
prom = navigator . clipboard . writeText ( this . authURLs . update _bot _token )
btnField = 'botConnection'
break
case 'channelPermission' :
prom = navigator . clipboard . writeText ( this . authURLs . update _channel _scopes )
btnField = 'channelPermission'
break
}
return prom
. then ( ( ) => {
this . copyButtonVariant [ btnField ] = 'success'
} )
. catch ( ( ) => {
this . copyButtonVariant [ btnField ] = 'danger'
} )
. finally ( ( ) => {
window . setTimeout ( ( ) => {
this . copyButtonVariant [ btnField ] = 'primary'
} , 2000 )
} )
} ,
2021-12-06 16:25:19 +00:00
fetchAPITokens ( ) {
this . $bus . $emit ( constants . NOTIFY _LOADING _DATA , true )
return axios . get ( 'config-editor/auth-tokens' , this . $root . axiosOptions )
. then ( resp => {
this . apiTokens = resp . data
} )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
2021-12-31 12:42:37 +00:00
fetchAuthURLs ( ) {
this . $bus . $emit ( constants . NOTIFY _LOADING _DATA , true )
return axios . get ( 'config-editor/auth-urls' , this . $root . axiosOptions )
. then ( resp => {
this . authURLs = resp . data
} )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
2021-12-06 16:25:19 +00:00
fetchGeneralConfig ( ) {
this . $bus . $emit ( constants . NOTIFY _LOADING _DATA , true )
return axios . get ( 'config-editor/general' , this . $root . axiosOptions )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
. then ( resp => {
this . generalConfig = resp . data
const promises = [ ]
for ( const editor of this . generalConfig . bot _editors ) {
promises . push ( this . fetchProfile ( editor ) )
}
return Promise . all ( promises )
} )
} ,
fetchModules ( ) {
this . $bus . $emit ( constants . NOTIFY _LOADING _DATA , true )
return axios . get ( 'config-editor/modules' )
. then ( resp => {
this . modules = resp . data
} )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
fetchProfile ( user ) {
this . $bus . $emit ( constants . NOTIFY _LOADING _DATA , true )
return axios . get ( ` config-editor/user?user= ${ user } ` , this . $root . axiosOptions )
. then ( resp => {
Vue . set ( this . userProfiles , user , resp . data )
this . $bus . $emit ( constants . NOTIFY _LOADING _DATA , false )
} )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
newAPIToken ( ) {
Vue . set ( this . models , 'apiToken' , {
modules : [ ] ,
name : '' ,
} )
this . showAPITokenEditModal = true
} ,
removeAPIToken ( uuid ) {
axios . delete ( ` config-editor/auth-tokens/ ${ uuid } ` , this . $root . axiosOptions )
. then ( ( ) => {
this . $bus . $emit ( constants . NOTIFY _CHANGE _PENDING , true )
} )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
removeChannel ( channel ) {
this . generalConfig . channels = this . generalConfig . channels
. filter ( ch => ch !== channel )
this . updateGeneralConfig ( )
} ,
removeEditor ( editor ) {
this . generalConfig . bot _editors = this . generalConfig . bot _editors
. filter ( ed => ed !== editor )
this . updateGeneralConfig ( )
} ,
saveAPIToken ( evt ) {
if ( ! this . validateAPIToken ) {
evt . preventDefault ( )
return
}
axios . post ( ` config-editor/auth-tokens ` , this . models . apiToken , this . $root . axiosOptions )
. then ( resp => {
this . createdAPIToken = resp . data
this . $bus . $emit ( constants . NOTIFY _CHANGE _PENDING , true )
window . setTimeout ( ( ) => {
this . createdAPIToken = null
} , 30000 )
} )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
updateGeneralConfig ( ) {
axios . put ( 'config-editor/general' , this . generalConfig , this . $root . axiosOptions )
. then ( ( ) => {
this . $bus . $emit ( constants . NOTIFY _CHANGE _PENDING , true )
} )
. catch ( err => this . $bus . $emit ( constants . NOTIFY _FETCH _ERROR , err ) )
} ,
2021-12-31 16:12:07 +00:00
validateUserName ( user ) {
return user . match ( constants . REGEXP _USER )
} ,
2021-12-06 16:25:19 +00:00
} ,
mounted ( ) {
this . $bus . $on ( constants . NOTIFY _CONFIG _RELOAD , ( ) => {
Promise . all ( [
this . fetchGeneralConfig ( ) ,
this . fetchAPITokens ( ) ,
2021-12-31 12:42:37 +00:00
this . fetchAuthURLs ( ) ,
2021-12-06 16:25:19 +00:00
] ) . then ( ( ) => {
this . $bus . $emit ( constants . NOTIFY _CHANGE _PENDING , false )
this . $bus . $emit ( constants . NOTIFY _LOADING _DATA , false )
} )
} )
Promise . all ( [
this . fetchGeneralConfig ( ) ,
this . fetchAPITokens ( ) ,
2021-12-31 12:42:37 +00:00
this . fetchAuthURLs ( ) ,
2021-12-06 16:25:19 +00:00
this . fetchModules ( ) ,
] ) . then ( ( ) => this . $bus . $emit ( constants . NOTIFY _LOADING _DATA , false ) )
} ,
name : 'TwitchBotEditorAppGeneralConfig' ,
}
< / script >