2015-01-20 01:53:18 -05:00
var FFZ = window . FrankerFaceZ ,
2015-07-29 01:03:10 -04:00
HOSTED _SUB = / subscribed to / ,
2015-01-20 01:53:18 -05:00
constants = require ( '../constants' ) ,
utils = require ( '../utils' ) ,
2015-11-07 22:56:15 -05:00
helpers ,
2015-01-20 01:53:18 -05:00
2016-05-06 02:23:12 -04:00
STATUS _BADGES = [
[ "r9k" , "r9k" , "This room is in R9K-mode." ] ,
2016-05-20 17:30:34 -04:00
[ "emote" , "emoteOnly" , "This room is in Twitch emoticons only mode. Emoticons added by extensions are not available in this mode." ] ,
2016-05-06 02:23:12 -04:00
[ "sub" , "subsOnly" , "This room is in subscribers-only mode." ] ,
[ "slow" , "slow" , function ( room ) { return "This room is in slow mode. You may send messages every " + utils . number _commas ( room && room . get ( 'slow' ) || 120 ) + " seconds." } ] ,
[ "ban" , "ffz_banned" , "You have been banned from talking in this room." ] ,
[ "delay" , function ( ) { return this . settings . chat _delay !== 0 } , function ( ) { return "You have enabled artificial chat delay. Messages are displayed after " + ( this . settings . chat _delay / 1000 ) + " seconds." } ] ,
[ "batch" , function ( ) { return this . settings . chat _batching !== 0 } , function ( ) { return "You have enabled chat message batching. Messages are displayed in " + ( this . settings . chat _batching / 1000 ) + " second increments." } ]
] ,
2015-07-18 21:10:27 -04:00
// StrimBagZ Support
is _android = navigator . userAgent . indexOf ( 'Android' ) !== - 1 ,
2015-01-20 01:53:18 -05:00
moderator _css = function ( room ) {
if ( ! room . moderator _badge )
return "" ;
2016-03-23 19:28:22 -04:00
return '.chat-line[data-room="' + room . id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-repeat: no-repeat; background-size: initial; background-position: center; background-image:url("' + room . moderator _badge + '") !important; }' ;
2015-11-07 22:56:15 -05:00
} ;
try {
helpers = window . require && window . require ( "ember-twitch-chat/helpers/chat-line-helpers" ) ;
} catch ( err ) { }
2015-01-20 01:53:18 -05:00
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _room = function ( ) {
this . rooms = { } ;
this . log ( "Creating room style element." ) ;
var s = this . _room _style = document . createElement ( "style" ) ;
s . id = "ffz-room-css" ;
document . head . appendChild ( s ) ;
2015-07-04 17:06:36 -04:00
this . log ( "Hooking the Ember Room controller." ) ;
2015-01-20 01:53:18 -05:00
2015-05-17 19:02:57 -04:00
// Responsive ban button.
2015-07-04 17:06:36 -04:00
var f = this ,
2016-03-23 20:23:04 -04:00
RC = utils . ember _lookup ( 'controller:room' ) ;
2016-03-23 19:28:22 -04:00
2015-05-17 19:02:57 -04:00
if ( RC ) {
2015-07-04 17:06:36 -04:00
var orig _ban = RC . _actions . banUser ,
orig _to = RC . _actions . timeoutUser ;
2015-05-17 19:02:57 -04:00
RC . _actions . banUser = function ( e ) {
2016-03-23 19:28:22 -04:00
orig _ban . call ( this , e ) ;
2016-05-13 23:56:59 -04:00
this . get ( "model" ) . clearMessages ( e . user , null , true ) ;
2015-07-04 17:06:36 -04:00
}
RC . _actions . timeoutUser = function ( e ) {
2016-03-23 19:28:22 -04:00
orig _to . call ( this , e ) ;
2016-05-13 23:56:59 -04:00
this . get ( "model" ) . clearMessages ( e . user , null , true ) ;
2015-05-17 19:02:57 -04:00
}
2015-12-12 13:28:35 -05:00
RC . _actions . showModOverlay = function ( e ) {
2016-03-23 20:23:04 -04:00
var Channel = utils . ember _resolve ( 'model:channel' ) ;
2015-12-12 13:28:35 -05:00
if ( ! Channel )
return ;
var chan = Channel . find ( { id : e . sender } ) ;
// Don't try loading the channel if it's already loaded. Don't make mod cards
// refresh the channel page when you click the broadcaster, basically.
if ( ! chan . get ( 'isLoaded' ) )
chan . load ( ) ;
this . set ( "showModerationCard" , true ) ;
// We pass in renderBottom and renderRight, which we use to reposition the window
// after we know how big it actually is.
this . set ( "moderationCardInfo" , {
user : chan ,
renderTop : e . top ,
renderLeft : e . left ,
renderBottom : e . bottom ,
renderRight : e . right ,
isIgnored : this . get ( "tmiSession" ) . isIgnored ( e . sender ) ,
2016-03-23 19:28:22 -04:00
isChannelOwner : this . get ( "login.userData.login" ) === e . sender ,
2015-12-12 13:28:35 -05:00
profileHref : Twitch . uri . profile ( e . sender ) ,
isModeratorOrHigher : this . get ( "model.isModeratorOrHigher" )
} ) ;
}
2015-05-17 19:02:57 -04:00
}
2015-07-04 17:06:36 -04:00
this . log ( "Hooking the Ember Room model." ) ;
2016-03-23 20:23:04 -04:00
var Room = utils . ember _resolve ( 'model:room' ) ;
2015-01-20 01:53:18 -05:00
this . _modify _room ( Room ) ;
// Modify all current instances of Room, as the changes to the base
// class won't be inherited automatically.
var instances = Room . instances ;
for ( var key in instances ) {
if ( ! instances . hasOwnProperty ( key ) )
continue ;
var inst = instances [ key ] ;
this . add _room ( inst . id , inst ) ;
this . _modify _room ( inst ) ;
2015-06-05 03:59:28 -04:00
inst . ffzPatchTMI ( ) ;
2015-01-20 01:53:18 -05:00
}
2015-07-04 17:06:36 -04:00
this . log ( "Hooking the Ember Room view." ) ;
2016-03-23 20:23:04 -04:00
var RoomView = utils . ember _resolve ( 'view:room' ) ;
2015-07-04 17:06:36 -04:00
this . _modify _rview ( RoomView ) ;
// For some reason, this doesn't work unless we create an instance of the
// room view and then destroy it immediately.
try {
RoomView . create ( ) . destroy ( ) ;
} catch ( err ) { }
// Modify all existing Room views.
2016-03-23 20:23:04 -04:00
var views = utils . ember _views ( ) ;
2015-12-12 13:28:35 -05:00
for ( var key in views ) {
if ( ! views . hasOwnProperty ( key ) )
2015-07-04 17:06:36 -04:00
continue ;
2015-12-12 13:28:35 -05:00
var view = views [ key ] ;
2015-07-04 17:06:36 -04:00
if ( ! ( view instanceof RoomView ) )
continue ;
this . log ( "Manually updating existing Room view." , view ) ;
try {
view . ffzInit ( ) ;
} catch ( err ) {
this . error ( "RoomView setup ffzInit: " + err ) ;
}
}
}
// --------------------
// View Customization
// --------------------
FFZ . prototype . _modify _rview = function ( view ) {
var f = this ;
view . reopen ( {
didInsertElement : function ( ) {
this . _super ( ) ;
try {
this . ffzInit ( ) ;
} catch ( err ) {
f . error ( "RoomView didInsertElement: " + err ) ;
}
} ,
willClearRender : function ( ) {
try {
this . ffzTeardown ( ) ;
} catch ( err ) {
f . error ( "RoomView willClearRender: " + err ) ;
}
this . _super ( ) ;
} ,
ffzInit : function ( ) {
f . _roomv = this ;
this . ffz _frozen = false ;
2015-07-18 21:10:27 -04:00
// Fix scrolling.
this . _ffz _mouse _down = this . ffzMouseDown . bind ( this ) ;
if ( is _android )
// We don't unbind scroll because that messes with the scrollbar. ;_;
this . _$chatMessagesScroller . bind ( 'scroll' , this . _ffz _mouse _down ) ;
this . _$chatMessagesScroller . unbind ( 'mousedown' ) ;
this . _$chatMessagesScroller . bind ( 'mousedown' , this . _ffz _mouse _down ) ;
2015-07-04 17:06:36 -04:00
if ( f . settings . chat _hover _pause )
this . ffzEnableFreeze ( ) ;
if ( f . settings . room _status )
this . ffzUpdateStatus ( ) ;
var controller = this . get ( 'controller' ) ;
if ( controller ) {
controller . reopen ( {
submitButtonText : function ( ) {
if ( this . get ( "model.isWhisperMessage" ) && this . get ( "model.isWhispersEnabled" ) )
return i18n ( "Whisper" ) ;
var wait = this . get ( "model.slowWait" ) ,
msg = this . get ( "model.messageToSend" ) || "" ;
if ( ( msg . charAt ( 0 ) === "/" && msg . substr ( 0 , 4 ) !== "/me " ) || ! wait || ! f . settings . room _status )
return i18n ( "Chat" ) ;
return utils . time _to _string ( wait , false , false , true ) ;
} . property ( "model.isWhisperMessage" , "model.isWhispersEnabled" , "model.slowWait" )
} ) ;
Ember . propertyDidChange ( controller , 'submitButtonText' ) ;
}
} ,
ffzTeardown : function ( ) {
if ( f . _roomv === this )
f . _roomv = undefined ;
2015-11-14 23:52:49 -05:00
if ( this . _ffz _chat _display )
this . _ffz _chat _display = undefined ;
2015-07-04 17:06:36 -04:00
this . ffzDisableFreeze ( ) ;
} ,
ffzUpdateStatus : function ( ) {
var room = this . get ( 'controller.model' ) ,
el = this . get ( 'element' ) ,
cont = el && el . querySelector ( '.chat-buttons-container' ) ;
if ( ! cont )
2015-07-13 21:52:44 -04:00
return ;
2015-07-04 17:06:36 -04:00
2016-05-06 02:23:12 -04:00
var btn = cont . querySelector ( 'button' ) ;
2015-07-04 17:06:36 -04:00
if ( f . has _bttv || ! f . settings . room _status ) {
2016-05-06 02:23:12 -04:00
jQuery ( ".ffz.room-state" , cont ) . remove ( ) ;
2015-07-04 17:06:36 -04:00
if ( btn )
btn . classList . remove ( 'ffz-waiting' ) ;
return ;
2016-05-06 02:23:12 -04:00
} else if ( btn ) {
btn . classList . toggle ( 'ffz-waiting' , ( room && room . get ( 'slowWait' ) || 0 ) ) ;
2016-05-12 00:11:50 -04:00
btn . classList . toggle ( 'ffz-banned' , ( room && room . get ( 'ffz_banned' ) || false ) ) ;
2015-07-04 17:06:36 -04:00
}
2016-05-06 02:23:12 -04:00
var badge , id , info , vis _count = 0 ;
for ( var i = 0 ; i < STATUS _BADGES . length ; i ++ ) {
info = STATUS _BADGES [ i ] ;
id = 'ffz-stat-' + info [ 0 ] ;
badge = cont . querySelector ( '#' + id ) ;
visible = typeof info [ 1 ] === "function" ? info [ 1 ] . call ( f , room ) : room && room . get ( info [ 1 ] ) ;
2016-05-20 17:30:34 -04:00
if ( typeof visible === "string" )
visible = visible === "1" ;
2016-05-06 02:23:12 -04:00
if ( ! badge ) {
badge = utils . createElement ( 'span' , 'ffz room-state stat float-right' , info [ 0 ] . charAt ( 0 ) . toUpperCase ( ) + '<span>' + info [ 0 ] . substr ( 1 ) . toUpperCase ( ) + '</span>' ) ;
badge . id = id ;
jQuery ( badge ) . tipsy ( { gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'se' ) } ) ;
cont . appendChild ( badge ) ;
}
2015-08-09 23:32:29 -07:00
2016-05-06 02:23:12 -04:00
badge . title = typeof info [ 2 ] === "function" ? info [ 2 ] . call ( f , room ) : info [ 2 ] ;
badge . classList . toggle ( 'hidden' , ! visible ) ;
if ( visible )
vis _count ++ ;
2015-11-01 17:28:19 -05:00
}
2015-08-14 13:21:57 -04:00
2016-05-06 02:23:12 -04:00
jQuery ( ".ffz.room-state" , cont ) . toggleClass ( "truncated" , vis _count > 3 ) ;
2015-07-04 17:06:36 -04:00
} . observes ( 'controller.model' ) ,
ffzEnableFreeze : function ( ) {
var el = this . get ( 'element' ) ,
messages = el . querySelector ( '.chat-messages' ) ;
if ( ! messages )
return ;
this . _ffz _interval = setInterval ( this . ffzPulse . bind ( this ) , 200 ) ;
this . _ffz _messages = messages ;
2015-08-14 13:21:57 -04:00
2015-07-04 17:06:36 -04:00
this . _ffz _mouse _move = this . ffzMouseMove . bind ( this ) ;
this . _ffz _mouse _out = this . ffzMouseOut . bind ( this ) ;
messages . addEventListener ( 'mousemove' , this . _ffz _mouse _move ) ;
2015-07-18 21:10:27 -04:00
messages . addEventListener ( 'touchmove' , this . _ffz _mouse _move ) ;
2015-07-04 17:06:36 -04:00
messages . addEventListener ( 'mouseout' , this . _ffz _mouse _out ) ;
} ,
ffzDisableFreeze : function ( ) {
if ( this . _ffz _interval ) {
clearInterval ( this . _ffz _interval ) ;
this . _ffz _interval = undefined ;
}
this . ffzUnfreeze ( ) ;
var messages = this . _ffz _messages ;
if ( ! messages )
return ;
this . _ffz _messages = undefined ;
if ( this . _ffz _mouse _move ) {
messages . removeEventListener ( 'mousemove' , this . _ffz _mouse _move ) ;
2016-03-23 19:28:22 -04:00
messages . removeEventListener ( 'touchmove' , this . _ffz _mouse _move ) ;
2015-07-04 17:06:36 -04:00
this . _ffz _mouse _move = undefined ;
}
if ( this . _ffz _mouse _out ) {
messages . removeEventListener ( 'mouseout' , this . _ffz _mouse _out ) ;
this . _ffz _mouse _out = undefined ;
}
} ,
ffzPulse : function ( ) {
if ( this . ffz _frozen ) {
var elapsed = Date . now ( ) - this . _ffz _last _move ;
if ( elapsed > 750 )
this . ffzUnfreeze ( ) ;
}
} ,
2015-11-14 23:52:49 -05:00
ffzUnfreeze : function ( from _stuck ) {
2015-07-04 17:06:36 -04:00
this . ffz _frozen = false ;
this . _ffz _last _move = 0 ;
this . ffzUnwarnPaused ( ) ;
2015-11-14 23:52:49 -05:00
if ( ! from _stuck && this . get ( 'stuckToBottom' ) )
2015-07-04 17:06:36 -04:00
this . _scrollToBottom ( ) ;
} ,
2015-07-13 21:52:44 -04:00
ffzMouseDown : function ( event ) {
var t = this . _$chatMessagesScroller ;
2015-07-29 01:03:10 -04:00
if ( t && t [ 0 ] && ( ( ! this . ffz _frozen && "mousedown" === event . type ) || "mousewheel" === event . type || ( is _android && "scroll" === event . type ) ) ) {
if ( event . type === "mousedown" )
f . log ( "Freezing from mouse down!" , event ) ;
2015-07-13 21:52:44 -04:00
var r = t [ 0 ] . scrollHeight - t [ 0 ] . scrollTop - t [ 0 ] . offsetHeight ;
this . _setStuckToBottom ( 10 >= r ) ;
}
} ,
2015-07-04 17:06:36 -04:00
ffzMouseOut : function ( event ) {
this . _ffz _outside = true ;
var e = this ;
2015-07-06 00:09:21 -04:00
setTimeout ( function ( ) {
2015-07-04 17:06:36 -04:00
if ( e . _ffz _outside )
e . ffzUnfreeze ( ) ;
2015-07-06 00:09:21 -04:00
} , 25 ) ;
2015-07-04 17:06:36 -04:00
} ,
ffzMouseMove : function ( event ) {
this . _ffz _last _move = Date . now ( ) ;
this . _ffz _outside = false ;
if ( event . screenX === this . _ffz _last _screenx && event . screenY === this . _ffz _last _screeny )
return ;
this . _ffz _last _screenx = event . screenX ;
this . _ffz _last _screeny = event . screenY ;
if ( this . ffz _frozen )
return ;
this . ffz _frozen = true ;
if ( this . get ( 'stuckToBottom' ) ) {
this . set ( 'controller.model.messageBufferSize' , f . settings . scrollback _length + 150 ) ;
this . ffzWarnPaused ( ) ;
}
} ,
_scrollToBottom : _ . throttle ( function ( ) {
var e = this ,
s = this . _$chatMessagesScroller ;
Ember . run . next ( function ( ) {
2015-07-29 01:03:10 -04:00
setTimeout ( function ( ) {
if ( e . ffz _frozen || ! s || ! s . length )
return ;
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
s [ 0 ] . scrollTop = s [ 0 ] . scrollHeight ;
e . _setStuckToBottom ( true ) ;
2015-07-04 17:06:36 -04:00
} )
} )
} , 200 ) ,
_setStuckToBottom : function ( val ) {
this . set ( "stuckToBottom" , val ) ;
this . get ( "controller.model" ) && this . set ( "controller.model.messageBufferSize" , f . settings . scrollback _length + ( val ? 0 : 150 ) ) ;
2015-07-13 21:52:44 -04:00
if ( ! val )
2015-11-14 23:52:49 -05:00
this . ffzUnfreeze ( true ) ;
2015-07-04 17:06:36 -04:00
} ,
// Warnings~!
ffzWarnPaused : function ( ) {
var el = this . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( ! el )
return ;
if ( ! warning ) {
warning = document . createElement ( 'div' ) ;
warning . className = 'more-messages-indicator ffz-freeze-indicator' ;
warning . innerHTML = '(Chat Paused Due to Mouse Movement)' ;
var cont = el . querySelector ( '.chat-interface' ) ;
if ( ! cont )
return ;
cont . insertBefore ( warning , cont . childNodes [ 0 ] )
}
warning . classList . remove ( 'hidden' ) ;
} ,
ffzUnwarnPaused : function ( ) {
var el = this . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( warning )
warning . classList . add ( 'hidden' ) ;
}
} ) ;
2015-01-20 01:53:18 -05:00
}
// --------------------
// Command System
// --------------------
FFZ . chat _commands = { } ;
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands = { } ;
2015-01-20 01:53:18 -05:00
FFZ . prototype . room _message = function ( room , text ) {
var lines = text . split ( "\n" ) ;
if ( this . has _bttv ) {
for ( var i = 0 ; i < lines . length ; i ++ )
BetterTTV . chat . handlers . onPrivmsg ( room . id , { style : 'admin' , date : new Date ( ) , from : 'jtv' , message : lines [ i ] } ) ;
} else {
for ( var i = 0 ; i < lines . length ; i ++ )
room . room . addMessage ( { style : 'ffz admin' , date : new Date ( ) , from : 'FFZ' , message : lines [ i ] } ) ;
}
}
FFZ . prototype . run _command = function ( text , room _id ) {
2015-02-10 01:34:23 -05:00
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return false ;
if ( ! text )
return ;
var args = text . split ( " " ) ,
cmd = args . shift ( ) . substr ( 1 ) . toLowerCase ( ) ,
command = FFZ . chat _commands [ cmd ] ,
output ;
if ( ! command )
return false ;
if ( command . hasOwnProperty ( 'enabled' ) ) {
var val = command . enabled ;
if ( typeof val == "function" ) {
try {
2016-03-23 19:28:22 -04:00
val = command . enabled . call ( this , room , args ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
this . error ( 'command "' + cmd + '" enabled: ' + err ) ;
val = false ;
}
}
if ( ! val )
return false ;
}
this . log ( "Received Command: " + cmd , args , true ) ;
try {
2016-03-23 19:28:22 -04:00
output = command . call ( this , room , args ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
this . error ( 'command "' + cmd + '" runner: ' + err ) ;
output = "There was an error running the command." ;
}
if ( output )
this . room _message ( room , output ) ;
return true ;
}
FFZ . prototype . run _ffz _command = function ( text , room _id ) {
2015-01-20 01:53:18 -05:00
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return ;
if ( ! text ) {
// Try to pop-up the menu.
var link = document . querySelector ( 'a.ffz-ui-toggle' ) ;
if ( link )
return link . click ( ) ;
text = "help" ;
}
var args = text . split ( " " ) ,
cmd = args . shift ( ) . toLowerCase ( ) ;
this . log ( "Received Command: " + cmd , args , true ) ;
2015-02-10 01:34:23 -05:00
var command = FFZ . ffz _commands [ cmd ] , output ;
2015-01-20 01:53:18 -05:00
if ( command ) {
try {
2016-03-23 19:28:22 -04:00
output = command . call ( this , room , args ) ;
2015-01-20 01:53:18 -05:00
} catch ( err ) {
this . log ( "Error Running Command - " + cmd + ": " + err , room ) ;
output = "There was an error running the command." ;
}
} else
output = 'There is no "' + cmd + '" command.' ;
if ( output )
this . room _message ( room , output ) ;
}
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . help = function ( room , args ) {
2015-01-20 01:53:18 -05:00
if ( args && args . length ) {
2015-02-10 01:34:23 -05:00
var command = FFZ . ffz _commands [ args [ 0 ] . toLowerCase ( ) ] ;
2015-01-20 01:53:18 -05:00
if ( ! command )
return 'There is no "' + args [ 0 ] + '" command.' ;
else if ( ! command . help )
return 'No help is available for the command "' + args [ 0 ] + '".' ;
else
return command . help ;
}
var cmds = [ ] ;
2015-02-10 01:34:23 -05:00
for ( var c in FFZ . ffz _commands )
FFZ . ffz _commands . hasOwnProperty ( c ) && cmds . push ( c ) ;
2015-01-20 01:53:18 -05:00
return "The available commands are: " + cmds . join ( ", " ) ;
}
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . help . help = "Usage: /ffz help [command]\nList available commands, or show help for a specific command." ;
2015-01-20 01:53:18 -05:00
// --------------------
// Room Management
// --------------------
FFZ . prototype . add _room = function ( id , room ) {
if ( this . rooms [ id ] )
return this . log ( "Tried to add existing room: " + id ) ;
this . log ( "Adding Room: " + id ) ;
// Create a basic data table for this room.
2015-10-24 22:44:00 -04:00
var data = this . rooms [ id ] = { id : id , room : room , sets : [ ] , ext _sets : [ ] , css : null , needs _history : false } ;
2015-01-20 01:53:18 -05:00
2015-07-04 17:06:36 -04:00
if ( this . follow _sets && this . follow _sets [ id ] ) {
data . extra _sets = this . follow _sets [ id ] ;
delete this . follow _sets [ id ] ;
for ( var i = 0 ; i < data . extra _sets . length ; i ++ ) {
var sid = data . extra _sets [ i ] ,
set = this . emote _sets && this . emote _sets [ sid ] ;
if ( set ) {
if ( set . users . indexOf ( id ) === - 1 )
set . users . push ( id ) ;
continue ;
}
this . load _set ( sid , function ( success , data ) {
if ( success )
data . users . push ( id ) ;
} ) ;
}
}
2015-01-20 01:53:18 -05:00
// Let the server know where we are.
2016-05-06 02:23:12 -04:00
room && room . ffzSubscribe && room . ffzSubscribe ( ) ;
//this.ws_send("sub", "room." + id);
2015-01-20 01:53:18 -05:00
2015-06-10 18:46:04 -04:00
// See if we need history?
if ( ! this . has _bttv && this . settings . chat _history && room && ( room . get ( 'messages.length' ) || 0 ) < 10 ) {
if ( ! this . ws _send ( "chat_history" , [ id , 25 ] , this . _load _history . bind ( this , id ) ) )
data . needs _history = true ;
}
2015-07-04 17:06:36 -04:00
// Why don't we set the scrollback length, too?
room . set ( 'messageBufferSize' , this . settings . scrollback _length + ( ( this . _roomv && ! this . _roomv . get ( 'stuckToBottom' ) && this . _roomv . get ( 'controller.model.id' ) === id ) ? 150 : 0 ) ) ;
2015-10-24 22:44:00 -04:00
// Load the room's data from the API.
2015-06-05 03:59:28 -04:00
this . load _room ( id ) ;
2015-10-24 22:44:00 -04:00
// Announce this room to any extension callback functions.
for ( var api _id in this . _apis ) {
var api = this . _apis [ api _id ] ;
api . _room _callbacks ( id , data ) ;
}
2015-01-20 01:53:18 -05:00
}
FFZ . prototype . remove _room = function ( id ) {
var room = this . rooms [ id ] ;
if ( ! room )
return ;
this . log ( "Removing Room: " + id ) ;
// Remove the CSS
if ( room . css || room . moderator _badge )
utils . update _css ( this . _room _style , id , null ) ;
// Let the server know we're gone and delete our data for this room.
2015-10-26 12:13:28 -07:00
this . ws _send ( "unsub" , "room." + id ) ;
2015-01-20 01:53:18 -05:00
delete this . rooms [ id ] ;
// Clean up sets we aren't using any longer.
2015-06-05 03:59:28 -04:00
if ( id . charAt ( 0 ) === "_" )
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
var set = this . emote _sets [ room . set ] ;
if ( set ) {
2015-01-20 01:53:18 -05:00
set . users . removeObject ( id ) ;
2015-06-05 03:59:28 -04:00
if ( ! this . global _sets . contains ( room . set ) && ! set . users . length )
this . unload _set ( room . set ) ;
2015-01-20 01:53:18 -05:00
}
}
2015-06-10 18:46:04 -04:00
// --------------------
// Chat History
// --------------------
FFZ . prototype . _load _history = function ( room _id , success , data ) {
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return ;
if ( success )
this . log ( "Received " + data . length + " old messages for: " + room _id ) ;
else
return this . log ( "Error retrieving chat history for: " + room _id ) ;
if ( ! data . length )
return ;
2015-11-19 02:45:56 -05:00
return this . _insert _history ( room _id , data , true ) ;
2015-06-10 18:46:04 -04:00
}
FFZ . prototype . _show _deleted = function ( room _id ) {
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return ;
var old _messages = room . room . get ( 'messages.0.ffz_old_messages' ) ;
if ( ! old _messages || ! old _messages . length )
return ;
room . room . set ( 'messages.0.ffz_old_messages' , undefined ) ;
this . _insert _history ( room _id , old _messages ) ;
}
2015-11-19 02:45:56 -05:00
FFZ . prototype . _insert _history = function ( room _id , data , from _server ) {
var room = this . rooms [ room _id ] , f = this ;
2015-06-10 18:46:04 -04:00
if ( ! room || ! room . room )
return ;
2016-05-13 23:56:59 -04:00
var current _user = this . get _user ( ) ,
r = room . room ,
2015-06-10 18:46:04 -04:00
messages = r . get ( 'messages' ) ,
2015-11-19 02:45:56 -05:00
buffer _size = r . get ( 'messageBufferSize' ) ,
2015-06-10 18:46:04 -04:00
tmiSession = r . tmiSession || ( TMI . _sessions && TMI . _sessions [ 0 ] ) ,
2015-11-19 02:45:56 -05:00
delete _links = r . get ( 'roomProperties.hide_chat_links' ) ,
2015-06-10 18:46:04 -04:00
2015-11-14 23:52:49 -05:00
removed = 0 ,
2015-06-10 18:46:04 -04:00
inserted = 0 ,
2015-11-19 02:45:56 -05:00
first _inserted ,
first _existing ,
before ;
2015-06-10 18:46:04 -04:00
2015-11-19 02:45:56 -05:00
first _existing = messages . length ? messages [ 0 ] : null ;
if ( first _existing && first _existing . from === 'jtv' && first _existing . message === 'Welcome to the chat room!' )
first _existing = messages . length > 1 ? messages [ 1 ] : null ;
2015-11-07 22:56:15 -05:00
2015-11-19 02:45:56 -05:00
if ( first _existing )
before = first _existing . date && first _existing . date . getTime ( ) ;
2015-06-10 18:46:04 -04:00
2015-11-19 02:45:56 -05:00
this . parse _history ( data , null , room _id , delete _links , tmiSession , function ( msg ) {
if ( from _server )
msg . from _server = true ;
// Skip messages that are from the future.
if ( ! msg . date || ( before && ( before - ( msg . from _server && ! first _existing . from _server ? f . _ws _server _offset || 0 : 0 ) ) < msg . date . getTime ( ) ) )
return true ;
if ( f . settings . remove _deleted && msg . deleted )
return true ;
2016-05-13 23:56:59 -04:00
if ( msg . tags . target && msg . tags . target !== '@@' ) {
var is _mine = current _user && current _user . login === msg . tags . target ;
if ( ! is _mine && ! r . ffzShouldDisplayNotice ( ) )
return true ;
// Display the Ban Reason if we're a moderator or that user.
if ( msg . tags [ 'ban-reason' ] && is _mine || r . get ( 'isModeratorOrHigher' ) ) {
msg . message = msg . message . substr ( 0 , msg . message . length - 1 ) + ' with reason: ' + msg . tags [ 'ban-reason' ] ;
msg . cachedTokens = [ utils . sanitize ( msg . message ) ] ;
}
}
2015-11-19 02:45:56 -05:00
if ( r . shouldShowMessage ( msg ) && r . ffzShouldShowMessage ( msg ) ) {
if ( messages . length < buffer _size ) {
2015-06-10 18:46:04 -04:00
if ( msg . ffz _old _messages ) {
2015-11-19 02:45:56 -05:00
var max _messages = buffer _size - ( messages . length + 1 ) ;
if ( max _messages <= 0 )
msg . ffz _old _messages = null ;
else if ( msg . ffz _old _messages . length > max _messages )
msg . ffz _old _messages = msg . ffz _old _messages . slice ( msg . ffz _old _messages . length - max _messages ) ;
2015-06-10 18:46:04 -04:00
}
2015-11-19 02:45:56 -05:00
if ( ! first _inserted )
first _inserted = msg ;
2015-06-10 18:46:04 -04:00
messages . unshiftObject ( msg ) ;
inserted += 1 ;
2015-11-19 02:45:56 -05:00
2015-06-10 18:46:04 -04:00
} else
2015-11-19 02:45:56 -05:00
return false ;
2015-06-10 18:46:04 -04:00
}
2015-11-07 22:56:15 -05:00
2015-11-19 02:45:56 -05:00
// If there's a CLEARCHAT, stop processing.
2015-11-07 22:56:15 -05:00
if ( msg . tags && msg . tags . target === '@@' )
2015-11-19 02:45:56 -05:00
return false ;
2015-11-07 22:56:15 -05:00
2015-11-19 02:45:56 -05:00
return true ;
} ) ;
2015-06-10 18:46:04 -04:00
2015-11-19 02:45:56 -05:00
if ( ! first _inserted )
return ;
var now = Date . now ( ) - ( first _inserted . from _server ? this . _ws _server _offset || 0 : 0 ) ,
age = now - first _inserted . date . getTime ( ) ;
if ( age > 300000 ) {
2015-06-10 18:46:04 -04:00
var msg = {
color : "#755000" ,
2015-11-19 02:45:56 -05:00
date : first _inserted . date ,
2015-06-10 18:46:04 -04:00
from : "frankerfacez_admin" ,
style : "admin" ,
2015-11-19 02:45:56 -05:00
message : "(Last message is " + utils . human _time ( age / 1000 ) + " old.)" ,
room : room _id ,
from _server : from _server
2015-06-10 18:46:04 -04:00
} ;
2015-11-19 02:45:56 -05:00
this . tokenize _chat _line ( msg , false , delete _links ) ;
2015-06-10 18:46:04 -04:00
if ( r . shouldShowMessage ( msg ) ) {
messages . insertAt ( inserted , msg ) ;
2015-11-19 02:45:56 -05:00
while ( messages . length > buffer _size ) {
2015-06-10 18:46:04 -04:00
messages . removeAt ( 0 ) ;
2015-11-14 23:52:49 -05:00
removed ++ ;
}
2015-06-10 18:46:04 -04:00
}
}
}
2015-01-20 01:53:18 -05:00
// --------------------
// Receiving Set Info
// --------------------
2015-06-05 03:59:28 -04:00
FFZ . prototype . load _room = function ( room _id , callback , tries ) {
var f = this ;
2015-12-12 13:28:35 -05:00
jQuery . getJSON ( constants . API _SERVER + "v1/room/" + room _id )
2015-06-05 03:59:28 -04:00
. done ( function ( data ) {
if ( data . sets ) {
for ( var key in data . sets )
data . sets . hasOwnProperty ( key ) && f . _load _set _json ( key , undefined , data . sets [ key ] ) ;
}
f . _load _room _json ( room _id , callback , data ) ;
} ) . fail ( function ( data ) {
if ( data . status == 404 )
return typeof callback == "function" && callback ( false ) ;
tries = ( tries || 0 ) + 1 ;
if ( tries < 10 )
return f . load _room ( room _id , callback , tries ) ;
return typeof callback == "function" && callback ( false ) ;
} ) ;
2015-01-20 01:53:18 -05:00
}
FFZ . prototype . _load _room _json = function ( room _id , callback , data ) {
2015-06-05 03:59:28 -04:00
if ( ! data || ! data . room )
return typeof callback == "function" && callback ( false ) ;
data = data . room ;
2015-01-20 01:53:18 -05:00
// Preserve the pointer to the Room instance.
if ( this . rooms [ room _id ] )
data . room = this . rooms [ room _id ] . room ;
2015-07-04 17:06:36 -04:00
// Preserve everything else.
for ( var key in this . rooms [ room _id ] ) {
if ( key !== 'room' && this . rooms [ room _id ] . hasOwnProperty ( key ) && ! data . hasOwnProperty ( key ) )
data [ key ] = this . rooms [ room _id ] [ key ] ;
}
2015-06-10 18:46:04 -04:00
data . needs _history = this . rooms [ room _id ] && this . rooms [ room _id ] . needs _history || false ;
2015-01-20 01:53:18 -05:00
this . rooms [ room _id ] = data ;
if ( data . css || data . moderator _badge )
utils . update _css ( this . _room _style , room _id , moderator _css ( data ) + ( data . css || "" ) ) ;
2015-06-05 03:59:28 -04:00
if ( ! this . emote _sets . hasOwnProperty ( data . set ) )
2015-07-04 17:06:36 -04:00
this . load _set ( data . set , function ( success , set ) {
if ( set . users . indexOf ( room _id ) === - 1 )
set . users . push ( room _id ) ;
} ) ;
else if ( this . emote _sets [ data . set ] . users . indexOf ( room _id ) === - 1 )
this . emote _sets [ data . set ] . users . push ( room _id ) ;
2015-01-20 01:53:18 -05:00
this . update _ui _link ( ) ;
2016-04-11 18:57:25 -04:00
if ( data . set )
this . rerender _feed _cards ( data . set ) ;
2015-01-20 01:53:18 -05:00
if ( callback )
callback ( true , data ) ;
}
// --------------------
// Ember Modifications
// --------------------
FFZ . prototype . _modify _room = function ( room ) {
var f = this ;
room . reopen ( {
2015-07-04 17:06:36 -04:00
slowWaiting : false ,
2015-07-29 01:03:10 -04:00
slow : 0 ,
2015-07-04 17:06:36 -04:00
2016-05-12 00:11:50 -04:00
ffz _banned : false ,
2015-07-06 00:09:21 -04:00
mru _list : [ ] ,
2016-05-13 23:56:59 -04:00
updateWait : function ( value , was _banned , update ) {
2015-07-04 17:06:36 -04:00
var wait = this . get ( 'slowWait' ) || 0 ;
this . set ( 'slowWait' , value ) ;
if ( wait < 1 && value > 0 ) {
2015-07-13 21:52:44 -04:00
if ( this . _ffz _wait _timer )
clearTimeout ( this . _ffz _wait _timer ) ;
this . _ffz _wait _timer = setTimeout ( this . ffzUpdateWait . bind ( this ) , 1000 ) ;
2016-05-13 23:56:59 -04:00
! update && f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
2015-07-04 17:06:36 -04:00
} else if ( ( wait > 0 && value < 1 ) || was _banned ) {
this . set ( 'ffz_banned' , false ) ;
2016-05-13 23:56:59 -04:00
! update && f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
2015-07-04 17:06:36 -04:00
}
} ,
ffzUpdateWait : function ( ) {
2015-07-13 21:52:44 -04:00
this . _ffz _wait _timer = undefined ;
2015-07-04 17:06:36 -04:00
var wait = this . get ( 'slowWait' ) || 0 ;
if ( wait < 1 )
return ;
this . set ( 'slowWait' , -- wait ) ;
if ( wait > 0 )
2015-07-13 21:52:44 -04:00
this . _ffz _wait _timer = setTimeout ( this . ffzUpdateWait . bind ( this ) , 1000 ) ;
2015-07-04 17:06:36 -04:00
else {
this . set ( 'ffz_banned' , false ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
} ,
2015-12-12 13:28:35 -05:00
ffzScheduleDestroy : function ( ) {
if ( this . _ffz _destroy _timer )
return ;
var t = this ;
this . _ffz _destroy _timer = setTimeout ( function ( ) {
t . _ffz _destroy _timer = null ;
t . ffzCheckDestroy ( ) ;
} , 5000 ) ;
} ,
ffzCheckDestroy : function ( ) {
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ,
2015-12-12 13:28:35 -05:00
user = f . get _user ( ) ,
room _id = this . get ( 'id' ) ;
if ( ( Chat && Chat . get ( 'currentChannelRoom' ) === this ) || ( user && user . login === room _id ) || ( f . _chatv && f . _chatv . _ffz _host === room _id ) || ( f . settings . pinned _rooms && f . settings . pinned _rooms . indexOf ( room _id ) !== - 1 ) )
2016-05-06 02:23:12 -04:00
return this . ffzUnsubscribe ( true ) ;
2015-12-12 13:28:35 -05:00
this . destroy ( ) ;
} ,
2015-07-04 17:06:36 -04:00
ffzUpdateStatus : function ( ) {
if ( f . _roomv )
f . _roomv . ffzUpdateStatus ( ) ;
2016-03-23 19:28:22 -04:00
} . observes ( 'r9k' , 'subsOnly' , 'emoteOnly' , 'slow' , 'ffz_banned' ) ,
2015-07-29 01:03:10 -04:00
2016-05-06 02:23:12 -04:00
ffzShouldSubscribe : function ( ) {
var Chat = utils . ember _lookup ( 'controller:chat' ) ,
room _id = this . get ( 'id' ) ;
return ( Chat && Chat . get ( 'currentChannelRoom' ) === this ) || ( f . settings . pinned _rooms && f . settings . pinned _rooms . indexOf ( room _id ) !== - 1 ) ;
} ,
ffzSubscribe : function ( ) {
if ( this . ffzShouldSubscribe ( ) )
f . ws _send ( "sub" , "room." + this . get ( 'id' ) ) ;
} ,
ffzUnsubscribe : function ( not _always ) {
if ( ! not _always || ! this . ffzShouldSubscribe ( ) )
f . ws _send ( "unsub" , "room." + this . get ( 'id' ) ) ;
} ,
2015-07-29 01:03:10 -04:00
// User Level
ffzUserLevel : function ( ) {
if ( this . get ( 'isStaff' ) )
return 5 ;
else if ( this . get ( 'isAdmin' ) )
return 4 ;
else if ( this . get ( 'isBroadcaster' ) )
return 3 ;
else if ( this . get ( 'isGlobalModerator' ) )
return 2 ;
else if ( this . get ( 'isModerator' ) )
return 1 ;
return 0 ;
} . property ( 'id' , 'chatLabels.[]' ) ,
2015-07-04 17:06:36 -04:00
2015-01-20 20:25:26 -05:00
// Track which rooms the user is currently in.
2015-01-20 01:53:18 -05:00
init : function ( ) {
this . _super ( ) ;
2015-08-09 23:32:29 -07:00
2015-02-10 01:34:23 -05:00
try {
f . add _room ( this . id , this ) ;
2015-06-05 03:59:28 -04:00
this . set ( "ffz_chatters" , { } ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
f . error ( "add_room: " + err ) ;
}
2015-01-20 01:53:18 -05:00
} ,
willDestroy : function ( ) {
this . _super ( ) ;
2015-08-09 23:32:29 -07:00
2015-02-10 01:34:23 -05:00
try {
f . remove _room ( this . id ) ;
} catch ( err ) {
f . error ( "remove_room: " + err ) ;
}
2015-01-20 01:53:18 -05:00
} ,
2016-05-13 23:56:59 -04:00
clearMessages : function ( user , tags , disable _log ) {
2015-06-10 18:46:04 -04:00
var t = this ;
2016-05-13 23:56:59 -04:00
2015-06-10 18:46:04 -04:00
if ( user ) {
2016-05-20 17:30:34 -04:00
var duration = Infinity ,
2016-05-13 23:56:59 -04:00
reason = undefined ,
current _user = f . get _user ( ) ,
is _me = current _user && current _user . login === user ;
// Read the ban duration and reason from the message tags.
if ( tags && tags [ 'ban-duration' ] )
duration = parseInt ( tags [ 'ban-duration' ] ) ;
2016-05-20 17:30:34 -04:00
if ( isNaN ( duration ) )
duration = Infinity ;
2016-05-13 23:56:59 -04:00
if ( tags && tags [ 'ban-reason' ] && ( is _me || t . get ( 'isModeratorOrHigher' ) ) )
reason = tags [ 'ban-reason' ] ;
// If we were banned, set the state and update the UI.
if ( is _me ) {
t . set ( 'ffz_banned' , true ) ;
if ( typeof duration === "number" && duration )
t . updateWait ( duration )
else if ( duration ) {
t . set ( 'slowWait' , 0 ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
}
// Mark the user as recently banned.
if ( ! t . ffzRecentlyBanned )
t . ffzRecentlyBanned = [ ] ;
t . ffzRecentlyBanned . push ( user ) ;
while ( t . ffzRecentlyBanned . length > 100 )
t . ffzRecentlyBanned . shift ( ) ;
2015-08-09 23:32:29 -07:00
2016-05-13 23:56:59 -04:00
// Delete Visible Messages
2015-07-29 01:03:10 -04:00
var msgs = t . get ( 'messages' ) ,
total = msgs . get ( 'length' ) ,
2015-11-14 23:52:49 -05:00
i = total ,
removed = 0 ;
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
while ( i -- ) {
var msg = msgs . get ( i ) ;
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
if ( msg . from === user ) {
if ( f . settings . remove _deleted ) {
msgs . removeAt ( i ) ;
2015-11-14 23:52:49 -05:00
removed ++ ;
2015-07-29 01:03:10 -04:00
continue ;
}
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
t . set ( 'messages.' + i + '.ffz_deleted' , true ) ;
2015-06-10 18:46:04 -04:00
if ( ! f . settings . prevent _clear )
2015-07-29 01:03:10 -04:00
t . set ( 'messages.' + i + '.deleted' , true ) ;
2015-06-10 18:46:04 -04:00
}
2015-07-29 01:03:10 -04:00
}
2015-07-04 17:06:36 -04:00
2016-05-13 23:56:59 -04:00
// Delete Panding Messages
if ( t . ffzPending ) {
2015-08-09 23:32:29 -07:00
msgs = t . ffzPending ;
i = msgs . length ;
while ( i -- ) {
var msg = msgs . get ( i ) ;
if ( msg . from !== user ) continue ;
msg . ffz _deleted = true ;
msg . deleted = ! f . settings . prevent _clear ;
msg . removed = f . settings . remove _deleted ;
}
}
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
// Now we need to see about displaying a ban notice.
if ( ! disable _log ) {
// Look up the user's last ban.
var show _notice = is _me || this . ffzShouldDisplayNotice ( ) ,
show _reason = is _me || this . get ( 'isModeratorOrHigher' ) ,
room = f . rooms && f . rooms [ t . get ( 'id' ) ] ,
now = new Date ,
end _time = now + ( duration * 1000 ) ,
ban _history , last _ban ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
if ( room ) {
ban _history = room . ban _history = room . ban _history || { } ;
last _ban = ban _history [ user ] ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
// Only overwrite a ban in the last 15 seconds.
if ( ! last _ban || Math . abs ( now - last _ban . date ) > 15000 )
last _ban = null ;
}
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
// Display a notice in chat.
var message = ( is _me ? "You have" : FFZ . get _capitalization ( user ) + " has" ) + " been " + ( isFinite ( duration ) ? "timed out for " + utils . duration _string ( duration , true ) : "banned" ) ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
if ( show _notice ) {
if ( ! last _ban ) {
var msg = {
style : "admin" ,
date : now ,
ffz _ban _target : user ,
reasons : reason ? [ reason ] : [ ] ,
durations : [ duration ] ,
end _time : end _time ,
timeouts : 1 ,
message : message + ( show _reason && reason ? ' with reason: ' + reason : '.' )
} ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
if ( ban _history )
ban _history [ user ] = msg ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
this . addMessage ( msg ) ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
} else {
if ( reason && last _ban . reasons . indexOf ( reason ) === - 1 )
last _ban . reasons . push ( reason ) ;
if ( last _ban . durations . indexOf ( duration ) === - 1 )
last _ban . durations . push ( duration ) ;
last _ban . end _time = end _time ;
last _ban . timeouts ++ ;
last _ban . message = message + ' ' + utils . number _commas ( last _ban . timeouts ) + ' times' + ( ! show _reason || last _ban . reasons . length === 0 ? '.' : ' with reason' + utils . pluralize ( last _ban . reasons . length ) + ': ' + last _ban . reasons . join ( ', ' ) ) ;
last _ban . cachedTokens = [ { type : "text" , text : last _ban . message } ] ;
// Now that we've reset the tokens, if there's a line for this,
if ( last _ban . _line )
Ember . propertyDidChange ( last _ban . _line , 'tokenizedMessage' ) ;
}
}
// Mod Card History
if ( room && f . settings . mod _card _history ) {
var chat _history = room . user _history = room . user _history || { } ,
user _history = room . user _history [ user ] = room . user _history [ user ] || [ ] ,
last _ban = user _history . length > 0 ? user _history [ user _history . length - 1 ] : null ;
if ( ! last _ban || ! last _ban . is _delete || Math . abs ( now - last _ban . date ) > 15000 )
last _ban = null ;
if ( last _ban )
last _ban . cachedTokens = [ message + ' ' + utils . number _commas ( last _ban . timeouts ) + ' times' + ( last _ban . reasons . length === 0 ? '.' : ' with reason' + utils . pluralize ( last _ban . reasons . length ) + ': ' + last _ban . reasons . join ( ', ' ) ) ] ;
else {
user _history . push ( {
from : 'jtv' ,
is _delete : true ,
style : 'admin' ,
date : now ,
ffz _ban _target : user ,
reasons : reason ? [ reason ] : [ ] ,
durations : [ duration ] ,
end _time : end _time ,
timeouts : 1 ,
cachedTokens : message + ( reason ? ' with reason: ' + reason : '.' )
} )
while ( user _history . length > 20 )
user _history . shift ( ) ;
}
2015-07-04 17:06:36 -04:00
}
}
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
2015-06-10 18:46:04 -04:00
} else {
if ( f . settings . prevent _clear )
this . addTmiMessage ( "A moderator's attempt to clear chat was ignored." ) ;
else {
var msgs = t . get ( "messages" ) ;
t . set ( "messages" , [ ] ) ;
t . addMessage ( {
style : 'admin' ,
message : i18n ( "Chat was cleared by a moderator" ) ,
ffz _old _messages : msgs
} ) ;
}
}
} ,
2015-07-29 01:03:10 -04:00
trimMessages : function ( ) {
var messages = this . get ( "messages" ) ,
len = messages . get ( "length" ) ,
limit = this . get ( "messageBufferSize" ) ;
2015-08-14 13:21:57 -04:00
2016-03-23 19:28:22 -04:00
if ( len > limit )
2015-07-29 01:03:10 -04:00
messages . removeAt ( 0 , len - limit ) ;
} ,
2015-08-09 23:32:29 -07:00
// Artificial chat delay
2015-06-10 18:46:04 -04:00
pushMessage : function ( msg ) {
2015-11-01 17:28:19 -05:00
if ( f . settings . chat _batching !== 0 || f . settings . chat _delay !== 0 || ( this . ffzPending && this . ffzPending . length ) ) {
2015-08-14 13:21:57 -04:00
if ( ! this . ffzPending )
2015-08-09 23:32:29 -07:00
this . ffzPending = [ ] ;
2015-11-19 02:45:56 -05:00
var now = msg . time = Date . now ( ) ;
2015-08-09 23:32:29 -07:00
this . ffzPending . push ( msg ) ;
2015-08-14 13:21:57 -04:00
this . ffzSchedulePendingFlush ( now ) ;
2015-08-09 23:32:29 -07:00
} else {
this . ffzActualPushMessage ( msg ) ;
}
} ,
ffzActualPushMessage : function ( msg ) {
if ( this . shouldShowMessage ( msg ) && this . ffzShouldShowMessage ( msg ) ) {
2015-07-29 01:03:10 -04:00
this . get ( "messages" ) . pushObject ( msg ) ;
this . trimMessages ( ) ;
2015-06-10 18:46:04 -04:00
2015-12-12 13:28:35 -05:00
if ( msg . style !== "admin" && msg . style !== "whisper" ) {
if ( msg . ffz _has _mention ) {
this . ffz _last _mention = Date . now ( ) ;
}
this . ffz _last _activity = Date . now ( ) ;
this . incrementProperty ( "unreadCount" , 1 ) ;
}
2015-06-10 18:46:04 -04:00
}
} ,
2015-08-14 13:21:57 -04:00
ffzSchedulePendingFlush : function ( now ) {
// Instead of just blindly looping every x seconds, we want to calculate the time until
// the next message should be displayed, and then set the timeout for that. We'll
// end up looping a bit more frequently, but it'll make chat feel more responsive.
2015-11-01 17:28:19 -05:00
// If we have a pending flush, don't reschedule. It wouldn't change.
2015-08-14 13:21:57 -04:00
if ( this . _ffz _pending _flush )
2015-11-01 17:28:19 -05:00
return ;
2015-08-14 13:21:57 -04:00
if ( this . ffzPending && this . ffzPending . length ) {
2015-11-01 17:28:19 -05:00
// We need either the amount of chat delay past the first message, if chat_delay is on, or the
// amount of time from the last batch.
2015-11-19 02:45:56 -05:00
now = now || Date . now ( ) ;
2015-11-01 17:28:19 -05:00
var delay = Math . max (
2015-11-19 02:45:56 -05:00
( f . settings . chat _delay !== 0 ? 50 + Math . max ( 0 , ( f . settings . chat _delay + ( this . ffzPending [ 0 ] . time || 0 ) ) - now ) : 0 ) ,
( f . settings . chat _batching !== 0 ? Math . max ( 0 , f . settings . chat _batching - ( now - ( this . _ffz _last _batch || 0 ) ) ) : 0 ) ) ;
2015-11-01 17:28:19 -05:00
2015-08-14 13:21:57 -04:00
this . _ffz _pending _flush = setTimeout ( this . ffzPendingFlush . bind ( this ) , delay ) ;
}
} ,
2015-08-09 23:32:29 -07:00
ffzPendingFlush : function ( ) {
2015-08-14 13:21:57 -04:00
this . _ffz _pending _flush = null ;
2015-11-01 17:28:19 -05:00
var now = this . _ffz _last _batch = Date . now ( ) ;
2015-08-09 23:32:29 -07:00
for ( var i = 0 , l = this . ffzPending . length ; i < l ; i ++ ) {
var msg = this . ffzPending [ i ] ;
2015-08-14 13:21:57 -04:00
if ( msg . removed )
continue ;
2015-11-01 17:28:19 -05:00
if ( f . settings . chat _delay !== 0 && ( f . settings . chat _delay + msg . time > now ) )
2015-08-14 13:21:57 -04:00
break ;
2015-08-09 23:32:29 -07:00
this . ffzActualPushMessage ( msg ) ;
}
2015-08-14 13:21:57 -04:00
this . ffzPending = this . ffzPending . slice ( i ) ;
this . ffzSchedulePendingFlush ( now ) ;
2015-08-09 23:32:29 -07:00
} ,
ffzShouldShowMessage : function ( msg ) {
2015-11-19 02:45:56 -05:00
if ( ! f . settings . hosted _sub _notices && msg . style === 'notification' && HOSTED _SUB . test ( msg . message ) )
return false ;
2015-08-09 23:32:29 -07:00
if ( f . settings . remove _bot _ban _notices && this . ffzRecentlyBanned ) {
var banned = '(' + this . ffzRecentlyBanned . join ( '|' ) + ')' ;
var bots = {
'nightbot' : '^' + banned ,
'moobot' : '\\(' + banned + '\\)' ,
'xanbot' : '^' + banned ,
} ;
if ( msg . from in bots && ( new RegExp ( bots [ msg . from ] ) ) . test ( msg . message ) ) {
return false ;
}
}
return true ;
} ,
2016-05-13 23:56:59 -04:00
ffzShouldDisplayNotice : function ( ) {
return f . settings . timeout _notices === 2 || ( f . settings . timeout _notices === 1 && this . get ( 'isModeratorOrHigher' ) ) ;
} ,
addNotification : function ( msg ) {
if ( msg ) {
// We don't want to display these notices because we're injecting our own messages.
if ( ( msg . msgId === 'timeout_success' || msg . msgId === 'ban_success' ) && this . ffzShouldDisplayNotice ( ) )
return ;
return this . _super ( msg ) ;
}
} ,
2015-06-05 03:59:28 -04:00
addMessage : function ( msg ) {
2015-07-29 01:03:10 -04:00
if ( msg ) {
var is _whisper = msg . style === 'whisper' ;
2015-11-10 21:37:30 -05:00
// Ignore whispers if conversations are enabled.
2016-03-23 20:23:04 -04:00
if ( is _whisper && utils . ember _lookup ( 'controller:application' ) . get ( 'isConversationsEnabled' ) )
2015-11-10 21:37:30 -05:00
return ;
2015-06-10 18:46:04 -04:00
2015-07-29 01:03:10 -04:00
if ( ! is _whisper )
msg . room = this . get ( 'id' ) ;
2015-08-21 19:00:48 -04:00
// Look up color and labels.
if ( this . tmiRoom && msg . from ) {
if ( ! msg . color )
msg . color = msg . tags && msg . tags . color ? msg . tags . color : this . tmiSession . getColor ( msg . from . toLowerCase ( ) ) ;
if ( ! msg . labels )
msg . labels = this . tmiRoom . getLabels ( msg . from ) ;
}
2015-07-29 01:03:10 -04:00
// Tokenization
f . tokenize _chat _line ( msg , false , this . get ( 'roomProperties.hide_chat_links' ) ) ;
2016-03-23 19:28:22 -04:00
// If it's from Twitch notify, and it's directly related to
if ( msg . from === 'twitchnotify' && msg . message . indexOf ( 'subscribed to' ) === - 1 && msg . message . indexOf ( 'subscribed' ) !== - 1 ) {
if ( ! msg . tags )
msg . tags = { } ;
msg . tags . subscriber = true ;
if ( msg . labels && msg . labels . indexOf ( "subscriber" ) === - 1 )
msg . labels . push ( "subscriber" ) ;
}
2015-07-29 01:03:10 -04:00
// Keep the history.
if ( ! is _whisper && msg . from && msg . from !== 'jtv' && msg . from !== 'twitchnotify' && f . settings . mod _card _history ) {
var room = f . rooms && f . rooms [ msg . room ] ;
if ( room ) {
var chat _history = room . user _history = room . user _history || { } ,
user _history = room . user _history [ msg . from ] = room . user _history [ msg . from ] || [ ] ;
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
user _history . push ( {
2015-11-19 02:45:56 -05:00
from : msg . from ,
tags : { 'display-name' : msg . tags && msg . tags [ 'display-name' ] } ,
message : msg . message ,
2015-07-29 01:03:10 -04:00
cachedTokens : msg . cachedTokens ,
style : msg . style ,
date : msg . date
} ) ;
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
if ( user _history . length > 20 )
user _history . shift ( ) ;
2015-11-07 22:56:15 -05:00
if ( f . _mod _card && f . _mod _card . ffz _room _id === msg . room && f . _mod _card . get ( 'cardInfo.user.id' ) === msg . from ) {
var el = f . _mod _card . get ( 'element' ) ,
2015-11-19 02:45:56 -05:00
history = el && el . querySelector ( '.chat-history:not(.adjacent-history)' ) ,
2015-11-07 22:56:15 -05:00
was _at _top = history && history . scrollTop >= ( history . scrollHeight - history . clientHeight ) ;
if ( history ) {
2015-11-19 02:45:56 -05:00
history . appendChild ( f . _build _mod _card _history ( msg , f . _mod _card ) ) ;
2015-11-07 22:56:15 -05:00
if ( was _at _top )
setTimeout ( function ( ) { history . scrollTop = history . scrollHeight ; } )
// Don't do infinite scrollback.
2015-11-19 02:45:56 -05:00
if ( history . childElementCount > 100 )
2015-11-07 22:56:15 -05:00
history . removeChild ( history . firstElementChild ) ;
}
}
2015-07-04 17:06:36 -04:00
}
2015-07-29 01:03:10 -04:00
}
2015-07-04 17:06:36 -04:00
2016-05-13 23:56:59 -04:00
// Clear the last ban for that user.
var f _room = f . rooms && f . rooms [ msg . room ] ,
ban _history = f _room && f _room . ban _history ;
if ( ban _history && msg . from )
ban _history [ msg . from ] = false ;
2015-07-29 01:03:10 -04:00
// Check for message from us.
if ( ! is _whisper ) {
var user = f . get _user ( ) ;
if ( user && user . login === msg . from ) {
var was _banned = this . get ( 'ffz_banned' ) ;
this . set ( 'ffz_banned' , false ) ;
// Update the wait time.
2015-07-31 17:44:20 -04:00
if ( this . get ( 'isSubscriber' ) || this . get ( 'isModeratorOrHigher' ) || ! this . get ( 'slowMode' ) )
2015-07-29 01:03:10 -04:00
this . updateWait ( 0 , was _banned )
else if ( this . get ( 'slowMode' ) )
this . updateWait ( this . get ( 'slow' ) ) ;
2015-07-04 17:06:36 -04:00
}
2015-06-05 03:59:28 -04:00
}
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
// Also update chatters.
if ( ! is _whisper && this . chatters && ! this . chatters [ msg . from ] && msg . from !== 'twitchnotify' && msg . from !== 'jtv' )
this . ffzUpdateChatters ( msg . from ) ;
}
2015-07-18 21:10:27 -04:00
2015-07-29 01:03:10 -04:00
// Color processing.
if ( msg . color )
f . _handle _color ( msg . color ) ;
2016-05-06 02:23:12 -04:00
// Message Filtering
var i = f . _chat _filters . length ;
while ( i -- )
if ( f . _chat _filters [ i ] ( msg ) === false )
return ;
2016-03-23 19:28:22 -04:00
// Report this message to the dashboard.
if ( window !== window . parent && parent . postMessage && msg . from && msg . from !== "jtv" && msg . from !== "twitchnotify" )
2016-05-20 17:30:34 -04:00
parent . postMessage ( { from _ffz : true , command : 'chat_message' , data : { from : msg . from , room : msg . room } } , "*" ) ; //location.protocol + "//www.twitch.tv/");
2016-03-23 19:28:22 -04:00
2015-08-21 19:00:48 -04:00
// Add the message.
return this . _super ( msg ) ;
2015-06-05 03:59:28 -04:00
} ,
2016-05-06 02:23:12 -04:00
ffzChatFilters : function ( msg ) {
var i = f . _chat _filters . length ;
} ,
2015-06-05 03:59:28 -04:00
setHostMode : function ( e ) {
2015-07-13 21:52:44 -04:00
this . set ( 'ffz_host_target' , e && e . hostTarget || null ) ;
var user = f . get _user ( ) ;
if ( user && f . _cindex && this . get ( 'id' ) === user . login )
f . _cindex . ffzUpdateHostButton ( ) ;
2015-08-14 13:21:57 -04:00
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ;
2015-06-05 03:59:28 -04:00
if ( ! Chat || Chat . get ( 'currentChannelRoom' ) !== this )
return ;
return this . _super ( e ) ;
2015-01-20 20:25:26 -05:00
} ,
2015-10-17 18:05:44 -04:00
send : function ( text , ignore _history ) {
2015-02-10 01:34:23 -05:00
try {
2015-12-12 13:28:35 -05:00
this . ffz _last _input = Date . now ( ) ;
2015-10-17 18:05:44 -04:00
if ( text && ! ignore _history ) {
2015-07-06 00:09:21 -04:00
// Command History
var mru = this . get ( 'mru_list' ) ,
ind = mru . indexOf ( text ) ;
2015-08-14 13:21:57 -04:00
2015-07-06 00:09:21 -04:00
if ( ind !== - 1 )
mru . splice ( ind , 1 )
else if ( mru . length > 20 )
mru . pop ( ) ;
2015-08-14 13:21:57 -04:00
2015-07-06 00:09:21 -04:00
mru . unshift ( text ) ;
}
2015-08-14 13:21:57 -04:00
2015-02-10 01:34:23 -05:00
var cmd = text . split ( ' ' , 1 ) [ 0 ] . toLowerCase ( ) ;
if ( cmd === "/ffz" ) {
this . set ( "messageToSend" , "" ) ;
f . run _ffz _command ( text . substr ( 5 ) , this . get ( 'id' ) ) ;
return ;
} else if ( cmd . charAt ( 0 ) === "/" && f . run _command ( text , this . get ( 'id' ) ) ) {
this . set ( "messageToSend" , "" ) ;
return ;
}
} catch ( err ) {
f . error ( "send: " + err ) ;
}
return this . _super ( text ) ;
2015-06-05 03:59:28 -04:00
} ,
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
ffzUpdateUnread : function ( ) {
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ;
2015-12-12 13:28:35 -05:00
if ( Chat && Chat . get ( 'currentRoom' ) === this )
this . resetUnreadCount ( ) ;
else if ( f . _chatv )
f . _chatv . ffzUpdateUnread ( this . get ( 'id' ) ) ;
2015-06-05 03:59:28 -04:00
} . observes ( 'unreadCount' ) ,
ffzInitChatterCount : function ( ) {
if ( ! this . tmiRoom )
return ;
2015-07-29 01:03:10 -04:00
if ( this . _ffz _chatter _timer ) {
clearTimeout ( this . _ffz _chatter _timer ) ;
this . _ffz _chatter _timer = undefined ;
}
2015-06-05 03:59:28 -04:00
var room = this ;
this . tmiRoom . list ( ) . done ( function ( data ) {
var chatters = { } ;
data = data . data . chatters ;
2015-08-28 22:54:12 -04:00
if ( data && data . admins )
for ( var i = 0 ; i < data . admins . length ; i ++ )
chatters [ data . admins [ i ] ] = true ;
if ( data && data . global _mods )
for ( var i = 0 ; i < data . global _mods . length ; i ++ )
chatters [ data . global _mods [ i ] ] = true ;
if ( data && data . moderators )
for ( var i = 0 ; i < data . moderators . length ; i ++ )
chatters [ data . moderators [ i ] ] = true ;
if ( data && data . staff )
for ( var i = 0 ; i < data . staff . length ; i ++ )
chatters [ data . staff [ i ] ] = true ;
if ( data && data . viewers )
for ( var i = 0 ; i < data . viewers . length ; i ++ )
chatters [ data . viewers [ i ] ] = true ;
2015-06-05 03:59:28 -04:00
room . set ( "ffz_chatters" , chatters ) ;
room . ffzUpdateChatters ( ) ;
2015-07-29 01:03:10 -04:00
} ) . always ( function ( ) {
room . _ffz _chatter _timer = setTimeout ( room . ffzInitChatterCount . bind ( room ) , 300000 ) ;
2015-06-05 03:59:28 -04:00
} ) ;
} ,
2015-01-20 01:53:18 -05:00
2015-07-04 17:06:36 -04:00
2015-06-05 03:59:28 -04:00
ffzUpdateChatters : function ( add , remove ) {
var chatters = this . get ( "ffz_chatters" ) || { } ;
if ( add )
chatters [ add ] = true ;
if ( remove && chatters [ remove ] )
delete chatters [ remove ] ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( ! f . settings . chatter _count )
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( f . _cindex )
f . _cindex . ffzUpdateChatters ( ) ;
2015-01-20 01:53:18 -05:00
2016-03-23 19:28:22 -04:00
if ( window !== window . parent && parent . postMessage )
2016-05-20 17:30:34 -04:00
parent . postMessage ( { from _ffz : true , command : 'chatter_count' , data : Object . keys ( this . get ( 'ffz_chatters' ) || { } ) . length } , "*" ) ; //location.protocol + "//www.twitch.tv/");
2015-06-05 03:59:28 -04:00
} ,
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
ffzPatchTMI : function ( ) {
2016-05-13 23:56:59 -04:00
var tmi = this . get ( 'tmiRoom' ) ,
room = this ;
2016-05-20 17:30:34 -04:00
if ( this . get ( 'ffz_is_patched' ) || ! tmi )
2015-06-05 03:59:28 -04:00
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( f . settings . chatter _count )
this . ffzInitChatterCount ( ) ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
// Let's get chatter information!
2015-07-04 17:06:36 -04:00
// TODO: Remove this cause it's terrible.
2015-06-05 03:59:28 -04:00
var connection = tmi . _roomConn . _connection ;
if ( ! connection . ffz _cap _patched ) {
connection . ffz _cap _patched = true ;
connection . _send ( "CAP REQ :twitch.tv/membership" ) ;
connection . on ( "opened" , function ( ) {
this . _send ( "CAP REQ :twitch.tv/membership" ) ;
} , connection ) ;
2015-07-04 17:06:36 -04:00
}
2015-06-05 03:59:28 -04:00
2015-07-04 17:06:36 -04:00
// NOTICE for catching slow-mode updates
tmi . on ( 'notice' , function ( msg ) {
if ( msg . msgId === 'msg_slowmode' ) {
var match = /in (\d+) seconds/ . exec ( msg . message ) ;
if ( match ) {
room . updateWait ( parseInt ( match [ 1 ] ) ) ;
}
2015-06-05 03:59:28 -04:00
}
2015-07-04 17:06:36 -04:00
if ( msg . msgId === 'msg_timedout' ) {
var match = /for (\d+) more seconds/ . exec ( msg . message ) ;
if ( match ) {
room . set ( 'ffz_banned' , true ) ;
room . updateWait ( parseInt ( match [ 1 ] ) ) ;
}
}
if ( msg . msgId === 'msg_banned' ) {
room . set ( 'ffz_banned' , true ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
2015-07-13 21:52:44 -04:00
if ( msg . msgId === 'hosts_remaining' ) {
var match = /(\d+) host command/ . exec ( msg . message ) ;
if ( match ) {
room . set ( 'ffz_hosts_left' , parseInt ( match [ 1 ] || 0 ) ) ;
f . _cindex && f . _cindex . ffzUpdateHostButton ( ) ;
}
}
2015-07-04 17:06:36 -04:00
} ) ;
2015-06-05 03:59:28 -04:00
// Check this shit.
tmi . _roomConn . _connection . off ( "message" , tmi . _roomConn . _onIrcMessage , tmi . _roomConn ) ;
tmi . _roomConn . _onIrcMessage = function ( ircMsg ) {
if ( ircMsg . target != this . ircChannel )
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
switch ( ircMsg . command ) {
case "JOIN" :
if ( this . _session && this . _session . nickname === ircMsg . sender ) {
this . _onIrcJoin ( ircMsg ) ;
} else
f . settings . chatter _count && room . ffzUpdateChatters ( ircMsg . sender ) ;
break ;
case "PART" :
if ( this . _session && this . _session . nickname === ircMsg . sender ) {
this . _resetActiveState ( ) ;
this . _connection . _exitedRoomConn ( ) ;
this . _trigger ( "exited" ) ;
} else
f . settings . chatter _count && room . ffzUpdateChatters ( null , ircMsg . sender ) ;
break ;
default :
break ;
}
}
tmi . _roomConn . _connection . on ( "message" , tmi . _roomConn . _onIrcMessage , tmi . _roomConn ) ;
this . set ( 'ffz_is_patched' , true ) ;
2015-07-29 01:03:10 -04:00
} . observes ( 'tmiRoom' ) ,
// Room State Stuff
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
slowMode : function ( ) {
return this . get ( 'slow' ) > 0 ;
} . property ( 'slow' ) ,
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
onSlowOff : function ( ) {
if ( ! this . get ( 'slowMode' ) )
this . updateWait ( 0 ) ;
} . observes ( 'slowMode' )
2015-06-05 03:59:28 -04:00
} ) ;
2015-01-12 17:58:07 -05:00
}