2015-10-27 14:25:13 -04:00
var FFZ = window . FrankerFaceZ ,
2016-09-30 13:09:03 -04:00
constants = require ( './constants' ) ,
utils = require ( './utils' ) ,
2016-12-10 22:54:53 -05:00
KNOWN _COMMANDS = [ 'ffz' , 'unban' , 'ban' , 'timeout' , 'r9kbeta' , 'r9kbetaoff' , 'slow' , 'slowoff' , 'subscribers' , 'subscribersoff' , 'mod' , 'unmod' , 'me' , 'emoteonly' , 'emoteonlyoff' , 'host' , 'unhost' , 'commercial' ] ,
2016-09-30 13:09:03 -04:00
STATUS _CODES = {
400 : "Bad Request" ,
401 : "Unauthorized" ,
403 : "Forbidden" ,
404 : "Not Found" ,
500 : "Internal Server Error"
} ,
format _result = function ( response ) {
if ( typeof response === "string" )
return response ;
else if ( Array . isArray ( response ) )
return _ . map ( response , format _result ) . join ( ", " ) ;
return JSON . stringify ( response ) ;
} ,
ObjectPath = require ( './ObjectPath' ) ;
FFZ . ObjectPath = ObjectPath ;
// -----------------
// Settings
// -----------------
FFZ . settings _info . command _aliases = {
type : "button" ,
value : [ ] ,
category : "Chat Moderation" ,
no _bttv : true ,
name : "Command Aliases" ,
help : "Define custom commands for chat that are shortcuts for other commands or messages to send in chat." ,
on _update : function ( ) {
this . cache _command _aliases ( ) ;
} ,
method : function ( ) {
var f = this ,
old _val = [ ] ,
input = utils . createElement ( 'textarea' ) ;
input . style . marginBottom = '20px' ;
for ( var i = 0 ; i < this . settings . command _aliases . length ; i ++ ) {
var pair = this . settings . command _aliases [ i ] ,
name = pair [ 0 ] ,
2016-10-12 16:42:43 -04:00
command = pair [ 1 ] ,
label = pair [ 2 ] ;
2016-09-30 13:09:03 -04:00
2016-10-12 16:42:43 -04:00
old _val . push ( name + ( label ? ' ' + label : '' ) + '=' + command ) ;
2016-09-30 13:09:03 -04:00
}
utils . prompt (
"Command Aliases" ,
"Please enter a list of custom commands that you would like to use in Twitch chat. " +
"One item per line. To send multiple commands, separate them with <code><LINE></code>. " +
"Variables, such as arguments you provide running the custom command, can be inserted into the output.<hr>" +
"All custom commands require names. Names go at the start of each line, and are separated from " +
2016-10-12 16:42:43 -04:00
"the actual command by an equals sign. Do not include the leading slash or dot. Those are automatically included. " +
"You can also include a description of the arguments after the name but before the equals-sign " +
"to include a helpful reminder when using tab-completion with the command.<br>" +
"<strong>Example:</strong> <code>boop <user>=/timeout {0} 15 Boop!</code><hr>" +
2016-09-30 13:09:03 -04:00
"<code>{0}</code>, <code>{1}</code>, <code>{2}</code>, etc. will be replaced with any arguments you've supplied. " +
"Follow an argument index with a <code>$</code> to also include all remaining arguments.<br>" +
"<strong>Example:</strong> <code>boop=/timeout {0} 15 {1$}</code><hr>" +
"<strong>Allowed Variables</strong><br><table><tbody>" +
"<tr><td><code>{room}</code></td><td>chat room's name</td>" +
"<td><code>{room_name}</code></td><td>chat room's name</td></tr>" +
"<tr><td><code>{room_display_name}</code></td><td>chat room's display name</td>" +
"<td><code>{room_id}</code></td><td>chat room's numeric ID</td></tr>" +
"</tbody></table>" ,
old _val . join ( "\n" ) ,
function ( new _val ) {
if ( new _val === null || new _val === undefined )
return ;
var vals = new _val . trim ( ) . split ( /\s*\n\s*/g ) ,
output = [ ] ;
for ( var i = 0 ; i < vals . length ; i ++ ) {
2016-10-12 16:42:43 -04:00
var cmd = vals [ i ] ;
if ( cmd . charAt ( 0 ) === '.' || cmd . charAt ( 0 ) === '/' )
cmd = cmd . substr ( 1 ) ;
var name ,
label ,
2016-09-30 13:09:03 -04:00
name _match = /^([^=]+)=/ . exec ( cmd ) ;
if ( ! cmd || ! cmd . length )
continue ;
if ( name _match ) {
2016-10-12 16:42:43 -04:00
var ind = name _match [ 1 ] . indexOf ( ' ' ) ;
if ( ind === - 1 ) {
name = name _match [ 1 ] . toLowerCase ( ) ;
label = null ;
} else {
name = name _match [ 1 ] . substr ( 0 , ind ) . toLowerCase ( ) ;
label = name _match [ 1 ] . substr ( ind ) . trim ( ) ;
}
2016-09-30 13:09:03 -04:00
cmd = cmd . substr ( name _match [ 0 ] . length ) ;
}
2016-10-12 16:42:43 -04:00
output . push ( [ name , cmd , label ] ) ;
2016-09-30 13:09:03 -04:00
}
f . settings . set ( "command_aliases" , output ) ;
2016-10-12 16:42:43 -04:00
2016-09-30 13:09:03 -04:00
} , 600 , input ) ;
}
} ;
FFZ . prototype . _command _aliases = { } ;
FFZ . prototype . cache _command _aliases = function ( ) {
var aliases = this . _command _aliases = { } ;
for ( var i = 0 ; i < this . settings . command _aliases . length ; i ++ ) {
var pair = this . settings . command _aliases [ i ] ,
name = pair [ 0 ] ,
2016-10-12 16:42:43 -04:00
command = pair [ 1 ] ,
label = pair [ 2 ] ;
2016-09-30 13:09:03 -04:00
// Skip taken/invalid names.
if ( ! name || ! name . length || aliases [ name ] || KNOWN _COMMANDS . indexOf ( name ) !== - 1 )
continue ;
2016-10-12 16:42:43 -04:00
aliases [ name ] = [ command , label ] ;
2016-09-30 13:09:03 -04:00
}
2016-10-12 16:42:43 -04:00
if ( this . _inputv )
Ember . propertyDidChange ( this . _inputv , 'ffz_commands' ) ;
2016-09-30 13:09:03 -04:00
}
2015-01-16 17:45:37 -05:00
2015-01-19 15:27:10 -05:00
2015-02-10 01:34:23 -05:00
// -----------------
// Log Export
// -----------------
FFZ . ffz _commands . log = function ( room , args ) {
2016-10-13 23:05:54 -04:00
var f = this ;
this . get _debugging _info ( ) . then ( function ( result ) {
f . _pastebin ( result ) . then ( function ( url ) {
f . room _message ( room , "Your FrankerFaceZ logs have been pasted to: " + url ) ;
} ) . catch ( function ( ) {
f . room _message ( room , "An error occured uploading the logs to a pastebin." ) ;
} ) ;
2015-02-10 01:34:23 -05:00
} ) ;
} ;
2015-10-27 14:25:13 -04:00
// -----------------
// Data Reload
// -----------------
FFZ . ffz _commands . reload = function ( room , args ) {
var f = this ,
promises = [ ] ;
2016-10-27 12:49:31 -04:00
// Feature Friday. There's no feedback possible so don't use a promise.
this . check _ff ( ) ;
2015-10-27 14:25:13 -04:00
// Badge Information
promises . push ( new Promise ( function ( done , fail ) {
2016-05-20 17:30:34 -04:00
f . load _badges ( function ( success , badge _count , badge _total , badge _data ) {
done ( success ? [ badge _count , badge _total , badge _data ] : [ 0 , 0 , { } ] ) ;
2015-10-27 14:25:13 -04:00
} ) ;
} ) ) ;
// Emote Sets
for ( var set _id in this . emote _sets ) {
var es = this . emote _sets [ set _id ] ;
2016-04-11 18:57:25 -04:00
if ( ! es || es . hasOwnProperty ( 'source_ext' ) )
2015-10-27 14:25:13 -04:00
continue ;
promises . push ( new Promise ( function ( done , fail ) {
f . load _set ( set _id , done ) ;
} ) ) ;
}
// Do it!
Promise . all ( promises ) . then ( function ( results ) {
2016-05-20 17:30:34 -04:00
try {
var success = 0 ,
badge _count = results [ 0 ] [ 0 ] ,
badge _total = results [ 0 ] [ 1 ] ,
badges = results [ 0 ] [ 2 ] ,
total = results . length - 1 ,
badge _string = [ ] ;
if ( results . length > 1 ) {
for ( var i = 1 ; i < results . length ; i ++ ) {
if ( results [ i ] )
success ++ ;
}
2015-10-27 14:25:13 -04:00
}
2016-05-20 17:30:34 -04:00
for ( var key in badges ) {
if ( badges . hasOwnProperty ( key ) )
badge _string . push ( key + ': ' + badges [ key ] )
}
f . room _message ( room , "Loaded " + utils . number _commas ( badge _count ) + " badge" + utils . pluralize ( badge _count ) + " across " + utils . number _commas ( badge _total ) + " badge type" + utils . pluralize ( badge _total ) + ( badge _string . length ? " (" + badge _string . join ( ", " ) + ")" : "" ) + ". Successfully reloaded " + utils . number _commas ( success ) + " of " + utils . number _commas ( total ) + " emoticon set" + utils . pluralize ( total ) + "." ) ;
} catch ( err ) {
f . room _message ( room , "An error occured running the command." ) ;
f . error ( "Error Running FFZ Reload" , err ) ;
}
2015-10-27 14:25:13 -04:00
} )
}
2015-12-12 13:28:35 -05:00
// -----------------
// Moderation Cards
// -----------------
FFZ . chat _commands . card = function ( room , args ) {
if ( ! args || ! args . length || args . length > 1 )
return "Usage: /card <username>" ;
if ( ! this . _roomv )
return "An error occured. (We don't have the Room View.)" ;
// Get the position of the input box.
var el = this . _roomv . get ( 'element' ) ,
ta = el && el . querySelector ( 'textarea' ) ,
bounds = ta && ta . getBoundingClientRect ( ) ,
x = 0 , y = 0 , bottom , right ;
if ( ! bounds )
bounds = el && el . getBoundingClientRect ( ) || document . body . getBoundingClientRect ( ) ;
if ( bounds ) {
if ( bounds . left > 400 ) {
right = bounds . left - 40 ;
bottom = bounds . top + bounds . height ;
} else {
x = bounds . left - 20 ;
bottom = bounds . top - 20 ;
}
}
2016-12-10 22:24:31 -05:00
this . _roomv . actions . showModOverlay . call ( this . _roomv , {
2015-12-12 13:28:35 -05:00
top : y ,
left : x ,
bottom : bottom ,
right : right ,
sender : args [ 0 ]
} ) ;
}
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . card . label = '/card <user>' ;
FFZ . chat _commands . card . info = 'Open Moderation Card' ;
2015-12-12 13:28:35 -05:00
2016-09-30 13:09:03 -04:00
FFZ . chat _commands . rules = function ( room , args ) {
var f = this ,
r = room . room ;
r . waitForRoomProperties ( ) . then ( function ( ) {
var rules = r . get ( "roomProperties.chat_rules" ) ;
if ( ! rules || ! rules . length )
return f . room _message ( room , "This chat room does not have rules set." ) ;
r . set ( "chatRules" , rules ) ;
r . set ( "shouldDisplayChatRules" , true ) ;
} ) ;
}
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . rules . info = 'Show Chat Room Rules' ;
2016-09-30 13:09:03 -04:00
FFZ . chat _commands . open _link = function ( room , args ) {
if ( ! args || ! args . length )
return "Usage: /open_link <url>" ;
var wnd = window . open ( args . join ( " " ) , "_blank" ) ;
wnd . opener = null ;
}
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . open _link . label = '/open_link <url>' ;
FFZ . chat _commands . open _link . info = 'Open URL in Tab' ;
2016-09-30 13:09:03 -04:00
FFZ . chat _commands . fetch _link = function ( room , args ) {
if ( ! args || ! args . length )
return "Usage: /fetch_link <url> [template]\nTemplates use http://objectpath.org/ to format data. Default Template is \"Response: #$#\"" ;
var f = this ,
url = args . shift ( ) ,
headers = { } ;
if ( /https?:\/\/[^.]+\.twitch\.tv\// . test ( url ) )
headers [ 'Client-ID' ] = constants . CLIENT _ID ;
jQuery . ajax ( {
url : url ,
headers : headers ,
success : function ( data ) {
f . log ( "Response Received" , data ) ;
args = ( args && args . length ) ? args . join ( " " ) . split ( /#/g ) : [ "Response: " , "$" ] ;
if ( typeof data === "string" )
data = [ data ] ;
var is _special = true ,
output = [ ] ,
op = new ObjectPath ( data ) ;
for ( var i = 0 ; i < args . length ; i ++ ) {
var segment = args [ i ] ;
is _special = ! is _special ;
if ( ! is _special )
output . push ( segment ) ;
else
try {
output . push ( format _result ( op . execute ( segment ) ) ) ;
} catch ( err ) {
f . log ( "Error" , err ) ;
output . push ( "[Error: " + ( err . message || err ) + "]" ) ;
}
}
f . room _message ( room , output . join ( '' ) ) ;
} ,
error : function ( xhr ) {
f . log ( "Request Error" , xhr ) ;
f . room _message ( room , "Request Failed: " + ( xhr . status === 0 ? 'Unknown Error. ' + ( url . indexOf ( 'https' ) === - 1 ? 'Please make sure you\'re making HTTPS requests.' : 'Likely a CORS problem. Check your browser\'s Networking console for more.' ) : xhr . status + ' ' + ( STATUS _CODES [ xhr . status ] || '' ) ) ) ;
}
} ) ;
}
2016-10-12 16:42:43 -04:00
FFZ . chat _commands . fetch _link . label = '/fetch_link <url> <i>[template]</i>' ;
FFZ . chat _commands . fetch _link . info = 'Fetch URL and Display in Chat' ;
2016-09-30 13:09:03 -04:00
2016-12-10 22:54:53 -05:00
// ---------------------
// Group Chat Renaming
// ---------------------
FFZ . chat _commands . renamegroup = function ( room , args ) {
var f = this ,
new _name = args . join ( ' ' ) ;
if ( ! new _name . length )
return "Usage: /renamegroup <name>\nGroup owner only. Rename a group chat." ;
// Check that the length of the arguments is less than 120 bytes
else if ( utils . utf8 _encode ( new _name ) . length > 120 )
return "You entered a room name that is too long." ;
// Set the group name
room . room . tmiRoom . session . _depotApi . put ( "/rooms/" + room . id , {
display _name : new _name
} ) . then ( function ( result ) {
if ( result && result . room && result . room . display _name === new _name )
f . room _message ( room , 'The room was renamed to: ' + new _name ) ;
else
f . room _message ( room , 'The room name was not changed successfully.' )
} ) ;
}
FFZ . chat _commands . renamegroup . label = '/renamegroup <name>' ;
FFZ . chat _commands . renamegroup . info = 'Rename a group chat. Group owner only.'
FFZ . chat _commands . renamegroup . enabled = function ( room ) {
// Are we in a group chat and are we the owner?
return room && room . room && room . room . get ( 'isGroupRoom' ) && room . room . get ( 'isOwner' ) ;
}
2015-01-16 17:45:37 -05:00
// -----------------
2015-01-19 15:27:10 -05:00
// Mass Moderation
2015-01-16 17:45:37 -05:00
// -----------------
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . massunmod = function ( room , args ) {
2015-01-16 17:45:37 -05:00
args = args . join ( " " ) . trim ( ) ;
if ( ! args . length )
return "You must provide a list of users to unmod." ;
args = args . split ( /\W*,\W*/ ) ;
var user = this . get _user ( ) ;
if ( ! user || ! user . login == room . id )
return "You must be the broadcaster to use massunmod." ;
if ( args . length > 50 )
return "Each user you unmod counts as a single message. To avoid being globally banned, please limit yourself to 50 at a time and wait between uses." ;
var count = args . length ;
while ( args . length ) {
var name = args . shift ( ) ;
room . room . tmiRoom . sendMessage ( "/unmod " + name ) ;
}
return "Sent unmod command for " + count + " users." ;
}
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . massunmod . help = "Usage: /ffz massunmod <list, of, users>\nBroadcaster only. Unmod all the users in the provided list." ;
2015-01-16 17:45:37 -05:00
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . massmod = function ( room , args ) {
2015-01-16 17:45:37 -05:00
args = args . join ( " " ) . trim ( ) ;
if ( ! args . length )
return "You must provide a list of users to mod." ;
args = args . split ( /\W*,\W*/ ) ;
var user = this . get _user ( ) ;
if ( ! user || ! user . login == room . id )
return "You must be the broadcaster to use massmod." ;
if ( args . length > 50 )
return "Each user you mod counts as a single message. To avoid being globally banned, please limit yourself to 50 at a time and wait between uses." ;
var count = args . length ;
while ( args . length ) {
var name = args . shift ( ) ;
room . room . tmiRoom . sendMessage ( "/mod " + name ) ;
}
return "Sent mod command for " + count + " users." ;
}
2015-07-13 21:52:44 -04:00
FFZ . ffz _commands . massmod . help = "Usage: /ffz massmod <list, of, users>\nBroadcaster only. Mod all the users in the provided list." ;
2016-09-09 17:34:20 -04:00
// -----------------
// Mass Unbanning
// -----------------
FFZ . prototype . get _banned _users = function ( ) {
var f = this ;
return new Promise ( function ( succeed , fail ) {
var user = f . get _user ( ) ;
if ( ! user )
return fail ( ) ;
jQuery . get ( "/settings/channel" ) . done ( function ( data ) {
try {
var dom = new DOMParser ( ) . parseFromString ( data , 'text/html' ) ,
users = _ . pluck ( dom . querySelectorAll ( '.ban .obj' ) , 'textContent' ) ;
succeed ( _ . map ( users , function ( x ) { return x . trim ( ) } ) ) ;
} catch ( err ) {
f . error ( "Failed to parse banned users" , err ) ;
fail ( ) ;
}
} ) . fail ( function ( err ) {
f . error ( "Failed to load banned users" , err ) ;
fail ( ) ;
} )
} ) ;
}
/ * F F Z . f f z _ c o m m a n d s . m a s s u n b a n = f u n c t i o n ( r o o m , a r g s ) {
var user = this . get _user ( ) ;
if ( ! user || ( user . login !== room . id && ! user . is _admin && ! user . is _staff ) )
return "You must be the broadcaster to use massunban." ;
} * /
2015-07-13 21:52:44 -04:00
/ * F F Z . f f z _ c o m m a n d s . m a s s u n b a n = f u n c t i o n ( r o o m , a r g s ) {
args = args . join ( " " ) . trim ( ) ;
2015-10-27 14:25:13 -04:00
2016-12-09 05:33:38 +00:00
} * /