2020-09-12 13:49:20 +00:00
import * as contextmenu from "../ui/elements/ContextMenu" ;
2023-11-21 00:44:46 +00:00
import { Registry } from "../events" ;
import { ChannelTree } from "./ChannelTree" ;
2020-09-12 13:49:20 +00:00
import * as log from "../log" ;
2023-11-21 00:44:46 +00:00
import { LogCategory , logDebug , logError , logInfo , LogType } from "../log" ;
import { Settings , settings } from "../settings" ;
import { Sound } from "../audio/Sounds" ;
import { Group , GroupManager , GroupTarget , GroupType } from "../permission/GroupManager" ;
2020-09-12 13:49:20 +00:00
import PermissionType from "../permission/PermissionType" ;
2023-11-21 00:44:46 +00:00
import { createErrorModal , createInputModal } from "../ui/elements/Modal" ;
2020-09-12 13:49:20 +00:00
import * as htmltags from "../ui/htmltags" ;
2023-11-21 00:44:46 +00:00
import { CommandResult } from "../connection/ServerConnectionDeclaration" ;
import { ChannelEntry } from "./Channel" ;
import { ConnectionHandler , ViewReasonId } from "../ConnectionHandler" ;
import { openClientInfo } from "../ui/modal/ModalClientInfo" ;
import { spawnBanClient } from "../ui/modal/ModalBanClient" ;
import { spawnChangeLatency } from "../ui/modal/ModalChangeLatency" ;
2020-09-12 13:49:20 +00:00
import * as hex from "../crypto/hex" ;
2023-11-21 00:44:46 +00:00
import { ChannelTreeEntry , ChannelTreeEntryEvents } from "./ChannelTreeEntry" ;
import { spawnClientVolumeChange , spawnMusicBotVolumeChange } from "../ui/modal/ModalChangeVolumeNew" ;
import { spawnPermissionEditorModal } from "../ui/modal/permission/ModalController" ;
import { global_client_actions } from "../events/GlobalEvents" ;
import { ClientIcon } from "svg-sprites/client-icons" ;
import { VoiceClient } from "../voice/VoiceClient" ;
import { VoicePlayerEvents , VoicePlayerState } from "../voice/VoicePlayer" ;
import { ChannelTreeUIEvents } from "tc-shared/ui/tree/Definitions" ;
import { VideoClient } from "tc-shared/connection/VideoConnection" ;
import { tr , tra } from "tc-shared/i18n/localize" ;
import { EventClient } from "tc-shared/connectionlog/Definitions" ;
import { W2GPluginCmdHandler } from "tc-shared/ui/modal/video-viewer/W2GPlugin" ;
import { spawnServerGroupAssignments } from "tc-shared/ui/modal/group-assignment/Controller" ;
import { promptYesNo } from "tc-shared/ui/modal/yes-no/Controller" ;
2020-03-30 11:44:18 +00:00
2021-04-27 11:30:33 +00:00
/* Must be the same as the TeaSpeak servers enum values */
2020-03-30 11:44:18 +00:00
export enum ClientType {
2021-04-27 11:30:33 +00:00
CLIENT_VOICE = 0 ,
CLIENT_QUERY = 1 ,
CLIENT_WEB = 3 ,
CLIENT_MUSIC = 4 ,
CLIENT_TEASPEAK = 5 ,
CLIENT_UNDEFINED = 5
2018-04-30 21:57:21 +00:00
}
2020-03-30 11:44:18 +00:00
export class ClientProperties {
2018-04-30 21:57:21 +00:00
client_type : ClientType = ClientType . CLIENT_VOICE ; //TeamSpeaks type
2021-04-27 11:30:33 +00:00
client_type_exact : ClientType = ClientType . CLIENT_UNDEFINED ;
2018-08-10 19:30:58 +00:00
client_database_id : number = 0 ;
2018-04-16 18:38:35 +00:00
client_version : string = "" ;
client_platform : string = "" ;
client_nickname : string = "unknown" ;
client_unique_identifier : string = "unknown" ;
client_description : string = "" ;
client_servergroups : string = "" ;
client_channel_group_id : number = 0 ;
2020-12-07 18:37:06 +00:00
client_channel_group_inherited_channel_id : number = 0 ;
2018-04-16 18:38:35 +00:00
client_lastconnected : number = 0 ;
2019-08-30 21:06:39 +00:00
client_created : number = 0 ;
client_totalconnections : number = 0 ;
2018-04-16 18:38:35 +00:00
client_flag_avatar : string = "" ;
2018-08-12 12:14:50 +00:00
client_icon_id : number = 0 ;
2018-04-16 18:38:35 +00:00
client_away_message : string = "" ;
client_away : boolean = false ;
2019-08-21 08:00:01 +00:00
client_country : string = "" ;
2018-04-16 18:38:35 +00:00
client_input_hardware : boolean = false ;
2018-04-30 21:57:21 +00:00
client_output_hardware : boolean = false ;
2018-04-16 18:38:35 +00:00
client_input_muted : boolean = false ;
2018-04-30 21:57:21 +00:00
client_output_muted : boolean = false ;
2018-04-16 18:38:35 +00:00
client_is_channel_commander : boolean = false ;
2018-04-30 21:57:21 +00:00
2019-08-21 08:00:01 +00:00
client_teaforo_id : number = 0 ;
client_teaforo_name : string = "" ;
client_teaforo_flags : number = 0 ; /* 0x01 := Banned | 0x02 := Stuff | 0x04 := Premium */
2018-08-12 11:26:56 +00:00
2019-08-30 21:06:39 +00:00
/* not updated in view! */
client_month_bytes_uploaded : number = 0 ;
client_month_bytes_downloaded : number = 0 ;
client_total_bytes_uploaded : number = 0 ;
client_total_bytes_downloaded : number = 0 ;
2018-08-12 11:26:56 +00:00
client_talk_power : number = 0 ;
2020-09-26 19:34:46 +00:00
client_talk_request : number = 0 ;
client_talk_request_msg : string = "" ;
client_is_talker : boolean = false ;
2020-02-22 13:30:17 +00:00
client_is_priority_speaker : boolean = false ;
2018-04-16 18:38:35 +00:00
}
2020-03-30 11:44:18 +00:00
export class ClientConnectionInfo {
2019-08-30 21:06:39 +00:00
connection_bandwidth_received_last_minute_control : number = - 1 ;
connection_bandwidth_received_last_minute_keepalive : number = - 1 ;
connection_bandwidth_received_last_minute_speech : number = - 1 ;
connection_bandwidth_received_last_second_control : number = - 1 ;
connection_bandwidth_received_last_second_keepalive : number = - 1 ;
connection_bandwidth_received_last_second_speech : number = - 1 ;
connection_bandwidth_sent_last_minute_control : number = - 1 ;
connection_bandwidth_sent_last_minute_keepalive : number = - 1 ;
connection_bandwidth_sent_last_minute_speech : number = - 1 ;
connection_bandwidth_sent_last_second_control : number = - 1 ;
connection_bandwidth_sent_last_second_keepalive : number = - 1 ;
connection_bandwidth_sent_last_second_speech : number = - 1 ;
connection_bytes_received_control : number = - 1 ;
connection_bytes_received_keepalive : number = - 1 ;
connection_bytes_received_speech : number = - 1 ;
connection_bytes_sent_control : number = - 1 ;
connection_bytes_sent_keepalive : number = - 1 ;
connection_bytes_sent_speech : number = - 1 ;
connection_packets_received_control : number = - 1 ;
connection_packets_received_keepalive : number = - 1 ;
connection_packets_received_speech : number = - 1 ;
connection_packets_sent_control : number = - 1 ;
connection_packets_sent_keepalive : number = - 1 ;
connection_packets_sent_speech : number = - 1 ;
connection_ping : number = - 1 ;
connection_ping_deviation : number = - 1 ;
connection_server2client_packetloss_control : number = - 1 ;
connection_server2client_packetloss_keepalive : number = - 1 ;
connection_server2client_packetloss_speech : number = - 1 ;
connection_server2client_packetloss_total : number = - 1 ;
connection_client2server_packetloss_speech : number = - 1 ;
connection_client2server_packetloss_keepalive : number = - 1 ;
connection_client2server_packetloss_control : number = - 1 ;
connection_client2server_packetloss_total : number = - 1 ;
connection_filetransfer_bandwidth_sent : number = - 1 ;
connection_filetransfer_bandwidth_received : number = - 1 ;
connection_connected_time : number = - 1 ;
connection_idle_time : number = - 1 ;
connection_client_ip : string | undefined ;
connection_client_port : number = - 1 ;
}
2020-04-18 17:37:30 +00:00
export interface ClientEvents extends ChannelTreeEntryEvents {
notify_properties_updated : {
2021-03-24 17:39:19 +00:00
updated_properties : Partial < ClientProperties > ;
2020-04-18 17:37:30 +00:00
client_properties : ClientProperties
} ,
notify_mute_state_change : { muted : boolean }
2020-05-20 18:47:48 +00:00
notify_speak_state_change : { speaking : boolean } ,
2020-09-07 10:42:00 +00:00
notify_audio_level_changed : { newValue : number } ,
2020-09-24 20:16:08 +00:00
notify_status_icon_changed : { newIcon : ClientIcon } ,
2020-04-18 17:37:30 +00:00
2020-11-07 12:16:07 +00:00
notify_video_handle_changed : { oldHandle : VideoClient | undefined , newHandle : VideoClient | undefined } ,
2020-04-18 17:37:30 +00:00
}
2020-09-24 20:16:08 +00:00
const StatusIconUpdateKeys : ( keyof ClientProperties ) [ ] = [
"client_away" ,
"client_input_hardware" ,
"client_output_hardware" ,
"client_output_muted" ,
"client_input_muted" ,
"client_is_channel_commander" ,
"client_talk_power"
] ;
2020-12-29 15:53:04 +00:00
export class ClientEntry < Events extends ClientEvents = ClientEvents > extends ChannelTreeEntry < Events > {
readonly events : Registry < Events > ;
2020-09-07 10:42:00 +00:00
channelTree : ChannelTree ;
2020-02-02 14:05:36 +00:00
2018-04-30 21:57:21 +00:00
protected _clientId : number ;
protected _channel : ChannelEntry ;
2018-04-16 18:38:35 +00:00
2018-04-30 21:57:21 +00:00
protected _properties : ClientProperties ;
protected lastVariableUpdate : number = 0 ;
2019-08-21 08:00:01 +00:00
protected _speaking : boolean ;
2020-09-07 10:42:00 +00:00
protected voiceHandle : VoiceClient ;
protected voiceVolume : number ;
protected voiceMuted : boolean ;
private readonly voiceCallbackStateChanged ;
2018-02-27 16:20:49 +00:00
2020-11-07 12:16:07 +00:00
protected videoHandle : VideoClient ;
2020-09-07 10:42:00 +00:00
private promiseClientInfo : Promise < void > ;
private promiseClientInfoTimestamp : number ;
2019-08-30 21:06:39 +00:00
2020-09-07 10:42:00 +00:00
private promiseConnectionInfo : Promise < ClientConnectionInfo > ;
private promiseConnectionInfoTimestamp : number ;
private promiseConnectionInfoResolve : any ;
private promiseConnectionInfoReject : any ;
2018-02-27 16:20:49 +00:00
2019-04-04 19:47:52 +00:00
constructor ( clientId : number , clientName , properties : ClientProperties = new ClientProperties ( ) ) {
2020-04-18 17:37:30 +00:00
super ( ) ;
2020-12-29 15:53:04 +00:00
this . events = new Registry < Events > ( ) ;
2020-02-02 14:05:36 +00:00
2018-04-30 21:57:21 +00:00
this . _properties = properties ;
this . _properties . client_nickname = clientName ;
2018-02-27 16:20:49 +00:00
this . _clientId = clientId ;
this . channelTree = null ;
this . _channel = null ;
2020-09-07 10:42:00 +00:00
this . voiceCallbackStateChanged = this . handleVoiceStateChange . bind ( this ) ;
2020-09-24 20:16:08 +00:00
2020-11-07 12:16:07 +00:00
this . events . on ( [ "notify_speak_state_change" , "notify_mute_state_change" ] , ( ) = > this . events . fire_later ( "notify_status_icon_changed" , { newIcon : this.getStatusIcon ( ) } ) ) ;
2020-09-24 20:16:08 +00:00
this . events . on ( "notify_properties_updated" , event = > {
for ( const key of StatusIconUpdateKeys ) {
if ( key in event . updated_properties ) {
2020-11-07 12:16:07 +00:00
this . events . fire_later ( "notify_status_icon_changed" , { newIcon : this.getStatusIcon ( ) } )
2020-09-24 20:16:08 +00:00
return ;
}
}
} ) ;
2019-04-04 19:47:52 +00:00
}
2019-08-21 08:00:01 +00:00
destroy() {
2023-11-21 00:44:46 +00:00
if ( this . voiceHandle ) {
2021-01-10 16:36:57 +00:00
logError ( LogCategory . AUDIO , tr ( "Destroying client with an active audio handle. This could cause memory leaks!" ) ) ;
2020-09-28 08:13:20 +00:00
this . setVoiceClient ( undefined ) ;
2019-08-21 08:00:01 +00:00
}
2023-11-21 00:44:46 +00:00
if ( this . videoHandle ) {
2021-01-10 16:36:57 +00:00
logError ( LogCategory . AUDIO , tr ( "Destroying client with an active video handle. This could cause memory leaks!" ) ) ;
2020-11-07 12:16:07 +00:00
this . setVideoClient ( undefined ) ;
}
2019-08-21 08:00:01 +00:00
this . _channel = undefined ;
2020-09-24 20:16:08 +00:00
this . events . destroy ( ) ;
2019-08-21 08:00:01 +00:00
}
2020-09-07 10:42:00 +00:00
setVoiceClient ( handle : VoiceClient ) {
2023-11-21 00:44:46 +00:00
if ( this . voiceHandle === handle )
2020-09-07 10:42:00 +00:00
return ;
2023-11-21 00:44:46 +00:00
if ( this . voiceHandle ) {
2020-09-07 10:42:00 +00:00
this . voiceHandle . events . off ( this . voiceCallbackStateChanged ) ;
2019-08-21 08:00:01 +00:00
}
2020-09-07 10:42:00 +00:00
this . voiceHandle = handle ;
2023-11-21 00:44:46 +00:00
if ( handle ) {
2020-09-07 10:42:00 +00:00
this . voiceHandle . events . on ( "notify_state_changed" , this . voiceCallbackStateChanged ) ;
this . handleVoiceStateChange ( { oldState : VoicePlayerState.STOPPED , newState : handle.getState ( ) } ) ;
}
2019-08-21 08:00:01 +00:00
}
2020-11-07 12:16:07 +00:00
setVideoClient ( handle : VideoClient ) {
2023-11-21 00:44:46 +00:00
if ( this . videoHandle === handle ) {
2020-11-07 12:16:07 +00:00
return ;
}
const oldHandle = this . videoHandle ;
this . videoHandle = handle ;
this . events . fire ( "notify_video_handle_changed" , { oldHandle : oldHandle , newHandle : handle } ) ;
}
2020-09-07 10:42:00 +00:00
private handleVoiceStateChange ( event : VoicePlayerEvents [ "notify_state_changed" ] ) {
switch ( event . newState ) {
case VoicePlayerState . PLAYING :
case VoicePlayerState . STOPPING :
2021-04-05 21:05:44 +00:00
this . setSpeaking ( true ) ;
2020-09-07 10:42:00 +00:00
break ;
case VoicePlayerState . STOPPED :
case VoicePlayerState . INITIALIZING :
2021-04-05 21:05:44 +00:00
default :
this . setSpeaking ( false ) ;
2020-09-07 10:42:00 +00:00
break ;
2019-04-04 19:47:52 +00:00
}
2020-09-07 10:42:00 +00:00
}
2018-02-27 16:20:49 +00:00
2020-09-07 10:42:00 +00:00
private updateVoiceVolume() {
let volume = this . voiceMuted ? 0 : this.voiceVolume ;
/* TODO: If a whisper session has been set, update this as well */
this . voiceHandle ? . setVolume ( volume ) ;
2019-04-04 19:47:52 +00:00
}
2018-02-27 16:20:49 +00:00
2023-11-21 00:44:46 +00:00
getVoiceClient ( ) : VoiceClient {
2020-09-07 10:42:00 +00:00
return this . voiceHandle ;
2018-02-27 16:20:49 +00:00
}
2023-11-21 00:44:46 +00:00
getVideoClient ( ) : VideoClient {
2020-11-07 12:16:07 +00:00
return this . videoHandle ;
}
2023-11-21 00:44:46 +00:00
get properties ( ) : ClientProperties {
2018-04-30 21:57:21 +00:00
return this . _properties ;
}
2023-11-21 00:44:46 +00:00
getStatusIcon ( ) : ClientIcon {
2021-01-07 13:27:36 +00:00
if ( this . properties . client_type == ClientType . CLIENT_QUERY ) {
2020-09-24 13:51:22 +00:00
return ClientIcon . ServerQuery ;
} else if ( this . properties . client_away ) {
return ClientIcon . Away ;
} else if ( ! this . getVoiceClient ( ) && ! ( this instanceof LocalClientEntry ) ) {
return ClientIcon . InputMutedLocal ;
} else if ( ! this . properties . client_output_hardware ) {
return ClientIcon . HardwareOutputMuted ;
} else if ( this . properties . client_output_muted ) {
return ClientIcon . OutputMuted ;
} else if ( ! this . properties . client_input_hardware ) {
return ClientIcon . HardwareInputMuted ;
} else if ( this . properties . client_input_muted ) {
return ClientIcon . InputMuted ;
} else {
if ( this . isSpeaking ( ) ) {
if ( this . properties . client_is_channel_commander ) {
return ClientIcon . PlayerCommanderOn ;
} else {
return ClientIcon . PlayerOn ;
}
} else {
if ( this . properties . client_is_channel_commander ) {
return ClientIcon . PlayerCommanderOff ;
} else {
return ClientIcon . PlayerOff ;
}
}
}
}
2023-11-21 00:44:46 +00:00
currentChannel ( ) : ChannelEntry { return this . _channel ; }
clientNickName() { return this . properties . client_nickname ; }
clientUid() { return this . properties . client_unique_identifier ; }
clientId() { return this . _clientId ; }
2018-02-27 16:20:49 +00:00
2020-09-07 10:42:00 +00:00
isMuted() { return ! ! this . voiceMuted ; }
/* TODO: Move this method to the view (e.g. channel tree) and rename with to setClientMuted */
setMuted ( flagMuted : boolean , force : boolean ) {
2023-11-21 00:44:46 +00:00
if ( this . voiceMuted === flagMuted && ! force ) {
2019-08-21 08:00:01 +00:00
return ;
2020-09-07 10:42:00 +00:00
}
2019-08-21 08:00:01 +00:00
2023-11-21 00:44:46 +00:00
if ( flagMuted ) {
2019-08-21 08:00:01 +00:00
this . channelTree . client . serverConnection . send_command ( 'clientmute' , {
clid : this.clientId ( )
2023-11-21 00:44:46 +00:00
} ) . then ( ( ) = > { } ) ;
} else if ( this . voiceMuted ) {
2019-08-21 08:00:01 +00:00
this . channelTree . client . serverConnection . send_command ( 'clientunmute' , {
clid : this.clientId ( )
2023-11-21 00:44:46 +00:00
} ) . then ( ( ) = > { } ) ;
2019-08-21 08:00:01 +00:00
}
2020-09-07 10:42:00 +00:00
this . voiceMuted = flagMuted ;
2019-08-21 08:00:01 +00:00
2021-01-10 15:13:15 +00:00
this . channelTree . client . settings . setValue ( Settings . FN_CLIENT_MUTED ( this . clientUid ( ) ) , flagMuted ) ;
2020-09-07 10:42:00 +00:00
this . updateVoiceVolume ( ) ;
2019-08-21 08:00:01 +00:00
2020-09-07 10:42:00 +00:00
this . events . fire ( "notify_mute_state_change" , { muted : flagMuted } ) ;
2023-11-21 00:44:46 +00:00
for ( const client of this . channelTree . clients ) {
if ( client === this as any || client . properties . client_unique_identifier !== this . properties . client_unique_identifier ) {
2019-08-21 08:00:01 +00:00
continue ;
2020-12-29 15:53:04 +00:00
}
2020-09-07 10:42:00 +00:00
client . setMuted ( flagMuted , false ) ;
2019-08-21 08:00:01 +00:00
}
}
2023-11-21 00:44:46 +00:00
protected contextmenu_info ( ) : contextmenu . MenuEntry [ ] {
2019-08-21 08:00:01 +00:00
return [
{
type : contextmenu . MenuEntryType . ENTRY ,
name : this.properties.client_type_exact === ClientType . CLIENT_MUSIC ? tr ( "Show bot info" ) : tr ( "Show client info" ) ,
callback : ( ) = > {
2020-12-29 15:53:04 +00:00
this . channelTree . client . getSideBar ( ) . showClientInfo ( this as any ) ;
2019-08-21 08:00:01 +00:00
} ,
icon_class : "client-about" ,
2021-01-10 15:13:15 +00:00
visible : ! settings . getValue ( Settings . KEY_SWITCH_INSTANT_CLIENT )
2019-08-21 08:00:01 +00:00
} , {
2023-11-21 00:44:46 +00:00
callback : ( ) = > { } ,
2019-08-21 08:00:01 +00:00
type : contextmenu . MenuEntryType . HR ,
name : "" ,
2021-01-10 15:13:15 +00:00
visible : ! settings . getValue ( Settings . KEY_SWITCH_INSTANT_CLIENT )
2019-08-21 08:00:01 +00:00
}
]
}
2023-11-21 00:44:46 +00:00
protected assignment_context ( ) : contextmenu . MenuEntry [ ] {
2019-06-30 14:03:28 +00:00
let server_groups : contextmenu.MenuEntry [ ] = [ ] ;
2023-11-21 00:44:46 +00:00
for ( let group of this . channelTree . client . groups . serverGroups . sort ( GroupManager . sorter ( ) ) ) {
if ( group . type != GroupType . NORMAL ) continue ;
2018-09-30 19:50:59 +00:00
2019-06-30 14:03:28 +00:00
let entry : contextmenu.MenuEntry = { } as any ;
2018-09-30 19:50:59 +00:00
2019-06-30 14:03:28 +00:00
//TODO: May add the server group icon?
entry . checkbox_checked = this . groupAssigned ( group ) ;
2018-09-30 19:50:59 +00:00
entry . name = group . name + " [" + ( group . properties . savedb ? "perm" : "tmp" ) + "]" ;
2023-11-21 00:44:46 +00:00
if ( this . groupAssigned ( group ) ) {
2018-09-30 19:50:59 +00:00
entry . callback = ( ) = > {
2019-02-23 13:15:22 +00:00
this . channelTree . client . serverConnection . send_command ( "servergroupdelclient" , {
2018-09-30 19:50:59 +00:00
sgid : group.id ,
cldbid : this.properties.client_database_id
2023-11-21 00:44:46 +00:00
} ) . then ( ( ) = > { } ) ;
2018-09-30 19:50:59 +00:00
} ;
entry . disabled = ! this . channelTree . client . permissions . neededPermission ( PermissionType . I_GROUP_MEMBER_ADD_POWER ) . granted ( group . requiredMemberRemovePower ) ;
} else {
entry . callback = ( ) = > {
2019-02-23 13:15:22 +00:00
this . channelTree . client . serverConnection . send_command ( "servergroupaddclient" , {
2018-09-30 19:50:59 +00:00
sgid : group.id ,
cldbid : this.properties.client_database_id
2023-11-21 00:44:46 +00:00
} ) . then ( ( ) = > { } ) ;
2018-09-30 19:50:59 +00:00
} ;
entry . disabled = ! this . channelTree . client . permissions . neededPermission ( PermissionType . I_GROUP_MEMBER_REMOVE_POWER ) . granted ( group . requiredMemberAddPower ) ;
}
2019-06-30 14:03:28 +00:00
entry . type = contextmenu . MenuEntryType . CHECKBOX ;
2018-09-30 19:50:59 +00:00
server_groups . push ( entry ) ;
}
2019-06-30 14:03:28 +00:00
let channel_groups : contextmenu.MenuEntry [ ] = [ ] ;
2023-11-21 00:44:46 +00:00
for ( let group of this . channelTree . client . groups . channelGroups . sort ( GroupManager . sorter ( ) ) ) {
if ( group . type != GroupType . NORMAL ) continue ;
2018-09-30 19:50:59 +00:00
2019-06-30 14:03:28 +00:00
let entry : contextmenu.MenuEntry = { } as any ;
//TODO: May add the channel group icon?
entry . checkbox_checked = this . assignedChannelGroup ( ) == group . id ;
2018-09-30 19:50:59 +00:00
entry . name = group . name + " [" + ( group . properties . savedb ? "perm" : "tmp" ) + "]" ;
entry . callback = ( ) = > {
2019-02-23 13:15:22 +00:00
this . channelTree . client . serverConnection . send_command ( "setclientchannelgroup" , {
2018-09-30 19:50:59 +00:00
cldbid : this.properties.client_database_id ,
cgid : group.id ,
cid : this.currentChannel ( ) . channelId
2023-11-21 00:44:46 +00:00
} ) . then ( ( ) = > { } ) ;
2018-09-30 19:50:59 +00:00
} ;
entry . disabled = ! this . channelTree . client . permissions . neededPermission ( PermissionType . I_GROUP_MEMBER_ADD_POWER ) . granted ( group . requiredMemberRemovePower ) ;
2019-06-30 14:03:28 +00:00
entry . type = contextmenu . MenuEntryType . CHECKBOX ;
2018-09-30 19:50:59 +00:00
channel_groups . push ( entry ) ;
}
return [ {
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . SUB_MENU ,
icon_class : "client-permission_server_groups" ,
2018-12-05 19:46:33 +00:00
name : tr ( "Set server group" ) ,
2018-09-30 19:50:59 +00:00
sub_menu : [
{
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
2019-08-21 08:00:01 +00:00
icon_class : "client-permission_server_groups" ,
2018-09-30 19:50:59 +00:00
name : "Server groups dialog" ,
2019-09-18 23:25:57 +00:00
callback : ( ) = > this . open_assignment_modal ( )
2018-09-30 19:50:59 +00:00
} ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . HR ( ) ,
2018-09-30 19:50:59 +00:00
. . . server_groups
]
2023-11-21 00:44:46 +00:00
} , {
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . SUB_MENU ,
icon_class : "client-permission_channel" ,
2018-12-05 19:46:33 +00:00
name : tr ( "Set channel group" ) ,
2018-09-30 19:50:59 +00:00
sub_menu : [
. . . channel_groups
]
2023-11-21 00:44:46 +00:00
} , {
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . SUB_MENU ,
icon_class : "client-permission_client" ,
2018-12-05 19:46:33 +00:00
name : tr ( "Permissions" ) ,
2019-08-21 08:00:01 +00:00
sub_menu : [
{
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : "client-permission_client" ,
name : tr ( "Client permissions" ) ,
2020-06-15 14:56:05 +00:00
callback : ( ) = > spawnPermissionEditorModal ( this . channelTree . client , "client" , { clientDatabaseId : this.properties.client_database_id } )
2019-08-21 08:00:01 +00:00
} ,
{
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : "client-permission_client" ,
name : tr ( "Client channel permissions" ) ,
2021-03-23 11:03:00 +00:00
callback : ( ) = > spawnPermissionEditorModal ( this . channelTree . client , "client-channel" , {
clientDatabaseId : this.properties.client_database_id ,
channelId : this.currentChannel ( ) ? . channelId
} )
2019-08-21 08:00:01 +00:00
}
]
2018-09-30 19:50:59 +00:00
} ] ;
}
2019-09-18 23:25:57 +00:00
open_assignment_modal() {
2021-03-21 15:51:30 +00:00
spawnServerGroupAssignments ( this . channelTree . client , this . properties . client_database_id ) ;
2019-09-18 23:25:57 +00:00
}
2019-08-21 08:00:01 +00:00
open_text_chat() {
2020-12-09 12:36:56 +00:00
const privateConversations = this . channelTree . client . getPrivateConversations ( ) ;
2020-12-29 15:53:04 +00:00
const conversation = privateConversations . findOrCreateConversation ( this as any ) ;
conversation . setActiveClientEntry ( this as any ) ;
2020-12-09 12:36:56 +00:00
privateConversations . setSelectedConversation ( conversation ) ;
2020-12-09 19:44:33 +00:00
this . channelTree . client . getSideBar ( ) . showPrivateConversations ( ) ;
/* FIXME: Draw focus to the input box! */
//sideBar.privateConversationsController().focusInput();
2019-08-21 08:00:01 +00:00
}
2018-02-27 16:20:49 +00:00
showContextMenu ( x : number , y : number , on_close : ( ) = > void = undefined ) {
2020-08-08 13:20:32 +00:00
const w2gPlugin = this . channelTree . client . getPluginCmdRegistry ( ) . getPluginHandler < W2GPluginCmdHandler > ( W2GPluginCmdHandler . kPluginChannel ) ;
2019-03-07 14:30:53 +00:00
let trigger_close = true ;
2019-06-30 14:03:28 +00:00
contextmenu . spawn_context_menu ( x , y ,
2019-08-21 08:00:01 +00:00
. . . this . contextmenu_info ( ) , {
2023-11-21 00:44:46 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : ClientIcon.ChangeNickname ,
name : ( contextmenu . get_provider ( ) . html_format_enabled ( ) ? "<b>" : "" ) +
tr ( "Open text chat" ) +
( contextmenu . get_provider ( ) . html_format_enabled ( ) ? "</b>" : "" ) ,
callback : ( ) = > {
this . open_text_chat ( ) ;
}
} , {
type : contextmenu . MenuEntryType . ENTRY ,
name : tr ( "Watch clients video" ) ,
icon_class : ClientIcon.W2g ,
visible : w2gPlugin?.getCurrentWatchers ( ) . findIndex ( e = > e . clientId === this . clientId ( ) ) !== - 1 ,
callback : ( ) = > {
global_client_actions . fire ( "action_w2g" , {
following : this.clientId ( ) ,
handlerId : this.channelTree.client.handlerId
} ) ;
}
} ,
2019-09-18 23:25:57 +00:00
contextmenu . Entry . HR ( ) ,
{
type : contextmenu . MenuEntryType . ENTRY ,
2020-08-09 00:09:42 +00:00
icon_class : ClientIcon.About ,
2019-09-18 23:25:57 +00:00
name : tr ( "Show client info" ) ,
2020-12-29 15:53:04 +00:00
callback : ( ) = > openClientInfo ( this as any )
2019-09-18 23:25:57 +00:00
} ,
contextmenu . Entry . HR ( ) ,
{
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
2020-08-09 00:09:42 +00:00
icon_class : ClientIcon.Poke ,
2018-12-05 19:46:33 +00:00
name : tr ( "Poke client" ) ,
2019-03-07 14:30:53 +00:00
callback : ( ) = > {
2020-08-26 10:33:53 +00:00
createInputModal ( tr ( "Poke client" ) , tr ( "Poke message:<br>" ) , ( ) = > true , result = > {
2023-11-21 00:44:46 +00:00
if ( typeof ( result ) === "string" ) {
2019-03-07 14:30:53 +00:00
this . channelTree . client . serverConnection . send_command ( "clientpoke" , {
clid : this.clientId ( ) ,
2018-02-27 16:20:49 +00:00
msg : result
2020-07-21 22:55:28 +00:00
} ) . then ( ( ) = > {
2020-12-18 16:06:38 +00:00
this . channelTree . client . log . log ( "client.poke.send" , {
2020-07-21 22:55:28 +00:00
target : this.log_data ( ) ,
message : result
} ) ;
2018-02-27 16:20:49 +00:00
} ) ;
}
} , { width : 400 , maxLength : 512 } ) . open ( ) ;
}
} , {
2023-11-21 00:44:46 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : ClientIcon.Edit ,
name : tr ( "Change description" ) ,
callback : ( ) = > {
createInputModal ( tr ( "Change client description" ) , tr ( "New description:<br>" ) , ( ) = > true , result = > {
if ( typeof ( result ) === "string" ) {
this . channelTree . client . serverConnection . send_command ( "clientedit" , {
clid : this.clientId ( ) ,
client_description : result
} ) . then ( ( ) = > { } ) ;
2018-02-27 16:20:49 +00:00
2023-11-21 00:44:46 +00:00
}
} , { width : 400 , maxLength : 1024 } ) . open ( ) ;
}
} ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . HR ( ) ,
2018-09-30 19:50:59 +00:00
. . . this . assignment_context ( ) ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . HR ( ) , {
2023-11-21 00:44:46 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : ClientIcon.MoveClientToOwnChannel ,
name : tr ( "Move client to your channel" ) ,
callback : ( ) = > {
this . channelTree . client . serverConnection . send_command ( "clientmove" , {
clid : this.clientId ( ) ,
cid : this.channelTree.client.getClient ( ) . currentChannel ( ) . getChannelId ( )
} ) . then ( ( ) = > { } ) ;
}
} , {
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : ClientIcon.KickChannel ,
name : tr ( "Kick client from channel" ) ,
callback : ( ) = > {
createInputModal ( tr ( "Kick client from channel" ) , tr ( "Kick reason:<br>" ) , ( ) = > true , result = > {
if ( typeof ( result ) !== 'boolean' || result ) {
logInfo ( LogCategory . CLIENT , tr ( "Kicking client %s from channel with reason %s" ) , this . clientNickName ( ) , result ) ;
this . channelTree . client . serverConnection . send_command ( "clientkick" , {
clid : this.clientId ( ) ,
reasonid : ViewReasonId.VREASON_CHANNEL_KICK ,
reasonmsg : result
} ) . then ( ( ) = > { } ) ;
2018-02-27 16:20:49 +00:00
2023-11-21 00:44:46 +00:00
}
} , { width : 400 , maxLength : 255 } ) . open ( ) ;
}
} , {
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : ClientIcon.KickServer ,
name : tr ( "Kick client fom server" ) ,
callback : ( ) = > {
createInputModal ( tr ( "Kick client from server" ) , tr ( "Kick reason:<br>" ) , ( ) = > true , result = > {
if ( typeof ( result ) !== 'boolean' || result ) {
logInfo ( LogCategory . CLIENT , tr ( "Kicking client %s from server with reason %s" ) , this . clientNickName ( ) , result ) ;
this . channelTree . client . serverConnection . send_command ( "clientkick" , {
clid : this.clientId ( ) ,
reasonid : ViewReasonId.VREASON_SERVER_KICK ,
reasonmsg : result
} ) . then ( ( ) = > { } ) ;
}
} , { width : 400 , maxLength : 255 } ) . open ( ) ;
}
} , {
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : ClientIcon.BanClient ,
name : tr ( "Ban client" ) ,
invalidPermission : ! this . channelTree . client . permissions . neededPermission ( PermissionType . I_CLIENT_BAN_MAX_BANTIME ) . granted ( 1 ) ,
callback : ( ) = > {
spawnBanClient ( this . channelTree . client , [ {
name : this.properties.client_nickname ,
unique_id : this.properties.client_unique_identifier
} ] , ( data ) = > {
this . channelTree . client . serverConnection . send_command ( "banclient" , {
uid : this.properties.client_unique_identifier ,
banreason : data.reason ,
time : data.length
} , {
flagset : [ data . no_ip ? "no-ip" : "" , data . no_hwid ? "no-hardware-id" : "" , data . no_name ? "no-nickname" : "" ]
} ) . then ( ( ) = > {
this . channelTree . client . sound . play ( Sound . USER_BANNED ) ;
2018-04-30 21:57:21 +00:00
} ) ;
2023-11-21 00:44:46 +00:00
} ) ;
}
} ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . HR ( ) ,
2018-09-26 13:04:56 +00:00
/ *
{
type : MenuEntryType . ENTRY ,
icon : "client-kick_server" ,
name : "Add group to client" ,
invalidPermission : true , //!this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
callback : ( ) = > {
Modals . spawnBanClient ( this . properties . client_nickname , ( duration , reason ) = > {
2019-02-23 13:15:22 +00:00
this . channelTree . client . serverConnection . send_command ( "banclient" , {
2018-09-26 13:04:56 +00:00
uid : this.properties.client_unique_identifier ,
banreason : reason ,
time : duration
} ) ;
} ) ;
}
} ,
MenuEntry . HR ( ) ,
* /
2018-04-11 15:56:09 +00:00
{
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
2020-08-09 00:09:42 +00:00
icon_class : ClientIcon.Volume ,
2018-12-05 19:46:33 +00:00
name : tr ( "Change Volume" ) ,
2020-12-29 15:53:04 +00:00
callback : ( ) = > spawnClientVolumeChange ( this as any )
2019-11-09 14:56:01 +00:00
} ,
{
type : contextmenu . MenuEntryType . ENTRY ,
name : tr ( "Change playback latency" ) ,
callback : ( ) = > {
2020-12-29 15:53:04 +00:00
spawnChangeLatency ( this as any , this . voiceHandle . getLatencySettings ( ) , ( ) = > {
2020-09-07 10:42:00 +00:00
this . voiceHandle . resetLatencySettings ( ) ;
return this . voiceHandle . getLatencySettings ( ) ;
} , settings = > this . voiceHandle . setLatencySettings ( settings ) , ( ) = > this . voiceHandle . flushBuffer ( ) ) ;
2019-11-09 14:56:01 +00:00
} ,
2020-09-07 10:42:00 +00:00
visible : ! ! this . voiceHandle
2019-08-21 08:00:01 +00:00
} , {
2023-11-21 00:44:46 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : ClientIcon.InputMutedLocal ,
name : tr ( "Mute client" ) ,
visible : ! this . voiceMuted ,
callback : ( ) = > this . setMuted ( true , false )
} , {
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : ClientIcon.InputMutedLocal ,
name : tr ( "Unmute client" ) ,
visible : this.voiceMuted ,
callback : ( ) = > this . setMuted ( false , false )
} ,
2020-02-02 14:05:36 +00:00
contextmenu . Entry . CLOSE ( ( ) = > trigger_close && on_close ? on_close ( ) : { } )
2018-02-27 16:20:49 +00:00
) ;
}
2023-11-21 00:44:46 +00:00
static bbcodeTag ( id : number , name : string , uid : string ) : string {
2019-01-20 17:43:14 +00:00
return "[url=client://" + id + "/" + uid + "~" + encodeURIComponent ( name ) + "]" + name + "[/url]" ;
}
2018-04-30 21:57:21 +00:00
2023-11-21 00:44:46 +00:00
static chatTag ( id : number , name : string , uid : string , braces : boolean = false ) : JQuery {
2019-01-20 17:43:14 +00:00
return $ ( htmltags . generate_client ( {
client_name : name ,
client_id : id ,
client_unique_id : uid ,
add_braces : braces
} ) ) ;
}
2018-04-30 21:57:21 +00:00
2023-11-21 00:44:46 +00:00
create_bbcode ( ) : string {
2019-01-20 17:43:14 +00:00
return ClientEntry . bbcodeTag ( this . clientId ( ) , this . clientNickName ( ) , this . clientUid ( ) ) ;
2018-02-27 16:20:49 +00:00
}
2023-11-21 00:44:46 +00:00
createChatTag ( braces : boolean = false ) : JQuery {
2018-02-27 16:20:49 +00:00
return ClientEntry . chatTag ( this . clientId ( ) , this . clientNickName ( ) , this . clientUid ( ) , braces ) ;
}
2021-04-05 21:05:44 +00:00
/** @deprecated Don't use this any more! */
set speaking ( flag : boolean ) {
this . setSpeaking ( ! ! flag ) ;
2018-02-27 16:20:49 +00:00
}
2020-04-18 17:37:30 +00:00
isSpeaking() { return this . _speaking ; }
2018-02-27 16:20:49 +00:00
2021-04-05 21:05:44 +00:00
protected setSpeaking ( flag : boolean ) {
2023-11-21 00:44:46 +00:00
if ( this . _speaking === flag ) {
2021-04-05 21:05:44 +00:00
return ;
}
this . _speaking = flag ;
this . events . fire ( "notify_speak_state_change" , { speaking : flag } ) ;
}
2023-11-21 00:44:46 +00:00
updateVariables ( . . . variables : { key : string , value : string } [ ] ) {
2018-04-16 18:38:35 +00:00
2018-11-20 14:06:18 +00:00
let reorder_channel = false ;
2019-08-21 08:00:01 +00:00
let update_avatar = false ;
2018-11-20 14:06:18 +00:00
2021-03-16 14:59:22 +00:00
let group ;
2023-11-21 00:44:46 +00:00
if ( __build . mode === "debug" ) {
2021-03-16 14:59:22 +00:00
group = log . group ( log . LogType . DEBUG , LogCategory . CLIENT , tr ( "Update properties (%i) of %s (%i)" ) , variables . length , this . clientNickName ( ) , this . clientId ( ) ) ;
{
const entries = [ ] ;
2023-11-21 00:44:46 +00:00
for ( const variable of variables )
2021-03-16 14:59:22 +00:00
entries . push ( {
key : variable.key ,
value : variable.value ,
type : typeof ( this . properties [ variable . key ] )
} ) ;
log . table ( LogType . DEBUG , LogCategory . PERMISSIONS , "Client update properties" , entries ) ;
}
2019-03-25 19:04:04 +00:00
}
2023-11-21 00:44:46 +00:00
for ( const variable of variables ) {
2019-08-21 08:00:01 +00:00
const old_value = this . _properties [ variable . key ] ;
2018-08-10 19:30:58 +00:00
JSON . map_field_to ( this . _properties , variable . value , variable . key ) ;
2023-11-21 00:44:46 +00:00
if ( variable . key == "client_nickname" ) {
if ( variable . value !== old_value && typeof ( old_value ) === "string" ) {
if ( ! ( this instanceof LocalClientEntry ) ) { /* own changes will be logged somewhere else */
2020-12-18 16:06:38 +00:00
this . channelTree . client . log . log ( "client.nickname.changed" , {
2019-08-21 08:00:01 +00:00
client : this.log_data ( ) ,
new_name : variable.value ,
old_name : old_value
} ) ;
}
}
2018-11-20 14:06:18 +00:00
reorder_channel = true ;
2018-04-16 18:38:35 +00:00
}
2023-11-21 00:44:46 +00:00
if ( variable . key == "client_unique_identifier" ) {
2021-01-10 15:13:15 +00:00
this . voiceVolume = this . channelTree . client . settings . getValue ( Settings . FN_CLIENT_VOLUME ( this . clientUid ( ) ) , 1 ) ;
const mute_status = this . channelTree . client . settings . getValue ( Settings . FN_CLIENT_MUTED ( this . clientUid ( ) ) , false ) ;
2020-09-07 10:42:00 +00:00
this . setMuted ( mute_status , mute_status ) ; /* force only needed when we want to mute the client */
this . updateVoiceVolume ( ) ;
2021-01-10 16:36:57 +00:00
logDebug ( LogCategory . CLIENT , tr ( "Loaded client (%s) server specific properties. Volume: %o Muted: %o." ) , this . clientUid ( ) , this . voiceVolume , this . voiceMuted ) ;
2018-04-16 18:38:35 +00:00
}
2023-11-21 00:44:46 +00:00
if ( variable . key == "client_talk_power" ) {
2018-11-20 14:06:18 +00:00
reorder_channel = true ;
2020-04-18 17:37:30 +00:00
//update_icon_status = true; DONE
2018-08-12 11:26:56 +00:00
}
2023-11-21 00:44:46 +00:00
if ( variable . key == "client_icon_id" ) {
2019-04-25 18:21:50 +00:00
/ * y e a h w e l i k e j a v a s c r i p t . D u e t o J S w i e r e d i n t e g e r b e h a v i o u r p a r s i n g f o r e x a m p l e f a i l s f o r 1 8 4 4 6 7 4 4 0 7 3 4 0 9 8 2 9 8 6 3 .
* parseInt ( "18446744073409829863" ) evaluates to 18446744073409829000 .
* In opposite "18446744073409829863" >>> 0 evaluates to 3995244544 , which is the icon id : )
* /
this . properties . client_icon_id = variable . value as any >>> 0 ;
}
2023-11-21 00:44:46 +00:00
else if ( variable . key == "client_flag_avatar" )
2019-08-21 08:00:01 +00:00
update_avatar = true ;
2018-04-11 15:56:09 +00:00
}
2018-04-16 18:38:35 +00:00
2023-11-21 00:44:46 +00:00
if ( update_avatar ) {
2020-08-22 15:50:38 +00:00
this . channelTree . client ? . fileManager ? . avatars . updateCache ( this . avatarId ( ) , this . properties . client_flag_avatar ) ;
2020-12-07 18:37:06 +00:00
}
2019-08-21 08:00:01 +00:00
2021-03-16 14:59:22 +00:00
group ? . end ( ) ;
2018-12-17 20:21:52 +00:00
2020-04-18 17:37:30 +00:00
{
let properties = { } ;
2023-11-21 00:44:46 +00:00
for ( const property of variables )
2020-04-18 17:37:30 +00:00
properties [ property . key ] = this . properties [ property . key ] ;
this . events . fire ( "notify_properties_updated" , { updated_properties : properties as any , client_properties : this.properties } ) ;
2018-12-17 20:21:52 +00:00
}
2018-09-30 20:36:17 +00:00
}
2023-11-21 00:44:46 +00:00
updateClientVariables ( force_update? : boolean ) : Promise < void > {
if ( Date . now ( ) - 10 * 60 * 1000 < this . promiseClientInfoTimestamp && this . promiseClientInfo && ( typeof ( force_update ) !== "boolean" || force_update ) ) {
2020-09-07 10:42:00 +00:00
return this . promiseClientInfo ;
2020-12-07 18:37:06 +00:00
}
2019-08-30 21:06:39 +00:00
2020-09-07 10:42:00 +00:00
this . promiseClientInfoTimestamp = Date . now ( ) ;
return ( this . promiseClientInfo = new Promise < void > ( ( resolve , reject ) = > {
2023-11-21 00:44:46 +00:00
this . channelTree . client . serverConnection . send_command ( "clientgetvariables" , { clid : this.clientId ( ) } ) . then ( ( ) = > resolve ( ) ) . catch ( error = > {
2020-09-07 10:42:00 +00:00
this . promiseConnectionInfoTimestamp = 0 ; /* not succeeded */
2019-08-30 21:06:39 +00:00
reject ( error ) ;
} ) ;
} ) ) ;
2018-02-27 16:20:49 +00:00
}
2023-11-21 00:44:46 +00:00
assignedServerGroupIds ( ) : number [ ] {
2018-02-27 16:20:49 +00:00
let result = [ ] ;
2023-11-21 00:44:46 +00:00
for ( let id of this . properties . client_servergroups . split ( "," ) ) {
if ( id . length == 0 ) continue ;
2018-02-27 16:20:49 +00:00
result . push ( Number . parseInt ( id ) ) ;
}
return result ;
}
2023-11-21 00:44:46 +00:00
assignedChannelGroup ( ) : number {
2018-04-16 18:38:35 +00:00
return this . properties . client_channel_group_id ;
2018-02-27 16:20:49 +00:00
}
2023-11-21 00:44:46 +00:00
groupAssigned ( group : Group ) : boolean {
if ( group . target == GroupTarget . SERVER ) {
for ( let id of this . assignedServerGroupIds ( ) )
if ( id == group . id ) return true ;
2018-02-27 16:20:49 +00:00
return false ;
} else return group . id == this . assignedChannelGroup ( ) ;
}
2019-04-04 19:47:52 +00:00
onDelete() { }
2018-02-27 16:20:49 +00:00
2023-11-21 00:44:46 +00:00
calculateOnlineTime ( ) : number {
2018-04-30 21:57:21 +00:00
return Date . now ( ) / 1000 - this . properties . client_lastconnected ;
2018-04-16 18:38:35 +00:00
}
2023-11-21 00:44:46 +00:00
avatarId ? ( ) : string {
2018-04-16 18:38:35 +00:00
function str2ab ( str ) {
let buf = new ArrayBuffer ( str . length ) ; // 2 bytes for each char
let bufView = new Uint8Array ( buf ) ;
2023-11-21 00:44:46 +00:00
for ( let i = 0 , strLen = str . length ; i < strLen ; i ++ ) {
2018-04-16 18:38:35 +00:00
bufView [ i ] = str . charCodeAt ( i ) ;
}
return buf ;
}
try {
let raw = atob ( this . properties . client_unique_identifier ) ;
let input = hex . encode ( str2ab ( raw ) ) ;
let result : string = "" ;
2023-11-21 00:44:46 +00:00
for ( let index = 0 ; index < input . length ; index ++ ) {
2018-04-16 18:38:35 +00:00
let c = input . charAt ( index ) ;
let offset : number = 0 ;
2023-11-21 00:44:46 +00:00
if ( c >= '0' && c <= '9' )
2018-04-16 18:38:35 +00:00
offset = c . charCodeAt ( 0 ) - '0' . charCodeAt ( 0 ) ;
2023-11-21 00:44:46 +00:00
else if ( c >= 'A' && c <= 'F' )
2018-04-16 18:38:35 +00:00
offset = c . charCodeAt ( 0 ) - 'A' . charCodeAt ( 0 ) + 0x0A ;
2023-11-21 00:44:46 +00:00
else if ( c >= 'a' && c <= 'f' )
2018-04-16 18:38:35 +00:00
offset = c . charCodeAt ( 0 ) - 'a' . charCodeAt ( 0 ) + 0x0A ;
result += String . fromCharCode ( 'a' . charCodeAt ( 0 ) + offset ) ;
}
return result ;
} catch ( e ) { //invalid base 64 (like music bot etc)
return undefined ;
}
2018-02-27 16:20:49 +00:00
}
2019-02-17 15:08:10 +00:00
2023-11-21 00:44:46 +00:00
log_data ( ) : EventClient {
2019-07-09 22:52:08 +00:00
return {
client_unique_id : this.properties.client_unique_identifier ,
client_name : this.clientNickName ( ) ,
client_id : this._clientId
}
2019-02-17 15:08:10 +00:00
}
2019-08-30 21:06:39 +00:00
/* max 1s ago, so we could update every second */
2023-11-21 00:44:46 +00:00
request_connection_info ( ) : Promise < ClientConnectionInfo > {
if ( Date . now ( ) - 900 < this . promiseConnectionInfoTimestamp && this . promiseConnectionInfo )
2020-09-07 10:42:00 +00:00
return this . promiseConnectionInfo ;
2019-08-30 21:06:39 +00:00
2023-11-21 00:44:46 +00:00
if ( this . promiseConnectionInfoReject )
2020-09-07 10:42:00 +00:00
this . promiseConnectionInfoResolve ( "timeout" ) ;
2019-08-30 21:06:39 +00:00
let _local_reject ; /* to ensure we're using the right resolve! */
2020-09-07 10:42:00 +00:00
this . promiseConnectionInfo = new Promise < ClientConnectionInfo > ( ( resolve , reject ) = > {
this . promiseConnectionInfoResolve = resolve ;
this . promiseConnectionInfoReject = reject ;
2019-08-30 21:06:39 +00:00
_local_reject = reject ;
} ) ;
2020-09-07 10:42:00 +00:00
this . promiseConnectionInfoTimestamp = Date . now ( ) ;
2023-11-21 00:44:46 +00:00
this . channelTree . client . serverConnection . send_command ( "getconnectioninfo" , { clid : this._clientId } ) . catch ( error = > _local_reject ( error ) ) ;
2020-09-07 10:42:00 +00:00
return this . promiseConnectionInfo ;
2019-08-30 21:06:39 +00:00
}
set_connection_info ( info : ClientConnectionInfo ) {
2023-11-21 00:44:46 +00:00
if ( ! this . promiseConnectionInfoResolve )
2019-08-30 21:06:39 +00:00
return ;
2020-09-07 10:42:00 +00:00
this . promiseConnectionInfoResolve ( info ) ;
this . promiseConnectionInfoResolve = undefined ;
this . promiseConnectionInfoReject = undefined ;
2019-08-30 21:06:39 +00:00
}
2020-05-20 18:47:48 +00:00
setAudioVolume ( value : number ) {
2023-11-21 00:44:46 +00:00
if ( this . voiceVolume == value ) {
2020-05-20 18:47:48 +00:00
return ;
2020-12-07 18:37:06 +00:00
}
2020-05-20 18:47:48 +00:00
2020-09-07 10:42:00 +00:00
this . voiceVolume = value ;
2020-05-20 18:47:48 +00:00
2020-09-07 10:42:00 +00:00
this . updateVoiceVolume ( ) ;
2021-01-10 15:13:15 +00:00
this . channelTree . client . settings . setValue ( Settings . FN_CLIENT_VOLUME ( this . clientUid ( ) ) , value ) ;
2020-05-20 18:47:48 +00:00
this . events . fire ( "notify_audio_level_changed" , { newValue : value } ) ;
}
getAudioVolume() {
2020-09-07 10:42:00 +00:00
return this . voiceVolume ;
2020-05-20 18:47:48 +00:00
}
2021-04-27 11:30:33 +00:00
2023-11-21 00:44:46 +00:00
getClientType ( ) : ClientType {
if ( this . properties . client_type_exact === ClientType . CLIENT_UNDEFINED ) {
2021-04-27 11:30:33 +00:00
/* We're on a TS3 server */
2023-11-21 00:44:46 +00:00
switch ( this . properties . client_type ) {
2021-04-27 11:30:33 +00:00
case 0 :
return ClientType . CLIENT_VOICE ;
case 1 :
return ClientType . CLIENT_QUERY ;
default :
return ClientType . CLIENT_UNDEFINED ;
}
} else {
2023-11-21 00:44:46 +00:00
switch ( this . properties . client_type_exact as ClientType ) {
2021-04-27 11:30:33 +00:00
case 0 :
return ClientType . CLIENT_VOICE ;
case 1 :
return ClientType . CLIENT_QUERY ;
case 3 :
return ClientType . CLIENT_WEB ;
case 4 :
return ClientType . CLIENT_MUSIC ;
case 5 :
return ClientType . CLIENT_TEASPEAK ;
2023-11-21 00:44:46 +00:00
// @ts-ignore
2021-04-27 11:30:33 +00:00
case 2 :
2023-11-21 00:44:46 +00:00
/* 2 is the internal client type which should never be visible for the target user */
2021-04-27 11:30:33 +00:00
default :
return ClientType . CLIENT_UNDEFINED ;
}
}
}
2018-02-27 16:20:49 +00:00
}
2020-03-30 11:44:18 +00:00
export class LocalClientEntry extends ClientEntry {
2019-04-04 19:47:52 +00:00
handle : ConnectionHandler ;
2018-02-27 16:20:49 +00:00
2019-04-04 19:47:52 +00:00
constructor ( handle : ConnectionHandler ) {
2018-05-09 09:50:05 +00:00
super ( 0 , "local client" ) ;
2018-02-27 16:20:49 +00:00
this . handle = handle ;
}
2021-04-05 21:05:44 +00:00
setSpeaking ( flag : boolean ) {
super . setSpeaking ( flag ) ;
}
2018-02-27 16:20:49 +00:00
showContextMenu ( x : number , y : number , on_close : ( ) = > void = undefined ) : void {
2019-06-30 14:03:28 +00:00
contextmenu . spawn_context_menu ( x , y ,
2019-08-21 08:00:01 +00:00
. . . this . contextmenu_info ( ) , {
2019-06-30 14:03:28 +00:00
2023-11-21 00:44:46 +00:00
name : ( contextmenu . get_provider ( ) . html_format_enabled ( ) ? "<b>" : "" ) +
tr ( "Change name" ) +
( contextmenu . get_provider ( ) . html_format_enabled ( ) ? "</b>" : "" ) ,
icon_class : "client-change_nickname" ,
callback : ( ) = > this . openRenameModal ( ) , /* FIXME: Pass the UI event registry */
type : contextmenu . MenuEntryType . ENTRY
} , {
name : tr ( "Change description" ) ,
icon_class : "client-edit" ,
callback : ( ) = > {
createInputModal ( tr ( "Change own description" ) , tr ( "New description:<br>" ) , ( ) = > true , result = > {
if ( result ) {
logInfo ( LogCategory . CLIENT , tr ( "Changing own description to %s" ) , result ) ;
this . channelTree . client . serverConnection . send_command ( "clientedit" , {
clid : this.clientId ( ) ,
client_description : result
} ) . then ( ( ) = > { } ) ;
2018-02-27 16:20:49 +00:00
2023-11-21 00:44:46 +00:00
}
} , { width : 400 , maxLength : 1024 } ) . open ( ) ;
2018-02-27 16:20:49 +00:00
} ,
2023-11-21 00:44:46 +00:00
type : contextmenu . MenuEntryType . ENTRY
} ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . HR ( ) ,
2018-09-30 20:58:53 +00:00
. . . this . assignment_context ( ) ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . CLOSE ( on_close )
2018-02-27 16:20:49 +00:00
) ;
}
2023-11-21 00:44:46 +00:00
renameSelf ( new_name : string ) : Promise < boolean > {
2020-04-18 17:37:30 +00:00
const old_name = this . properties . client_nickname ;
this . updateVariables ( { key : "client_nickname" , value : new_name } ) ; /* change it locally */
2020-08-26 10:33:53 +00:00
return this . handle . serverConnection . send_command ( "clientupdate" , { client_nickname : new_name } ) . then ( ( ) = > {
2021-01-10 15:13:15 +00:00
settings . setValue ( Settings . KEY_CONNECT_USERNAME , new_name ) ;
2020-12-18 16:06:38 +00:00
this . channelTree . client . log . log ( "client.nickname.changed.own" , {
2020-04-18 17:37:30 +00:00
client : this.log_data ( ) ,
old_name : old_name ,
new_name : new_name ,
} ) ;
return true ;
} ) . catch ( ( e : CommandResult ) = > {
this . updateVariables ( { key : "client_nickname" , value : old_name } ) ; /* change it back */
2020-12-18 16:06:38 +00:00
this . channelTree . client . log . log ( "client.nickname.change.failed" , {
2020-04-18 17:37:30 +00:00
reason : e.extra_message
} ) ;
return false ;
2018-02-27 16:20:49 +00:00
} ) ;
}
2020-09-26 19:34:46 +00:00
openRenameModal() {
createInputModal ( tr ( "Enter your new name" ) , tr ( "Enter your new client name" ) , text = > text . length >= 3 && text . length <= 30 , value = > {
2023-11-21 00:44:46 +00:00
if ( value ) {
2020-09-26 19:34:46 +00:00
this . renameSelf ( value as string ) . then ( result = > {
2023-11-21 00:44:46 +00:00
if ( ! result ) {
2020-09-26 19:34:46 +00:00
createErrorModal ( tr ( "Failed change nickname" ) , tr ( "Failed to change your client nickname" ) ) . open ( ) ;
}
} ) ;
2018-02-27 16:20:49 +00:00
}
2020-09-26 19:34:46 +00:00
} ) . open ( ) ;
}
2018-02-27 16:20:49 +00:00
2023-11-21 00:44:46 +00:00
openRename ( events : Registry < ChannelTreeUIEvents > ) : void {
2020-09-26 19:34:46 +00:00
events . fire ( "notify_client_name_edit" , { initialValue : this.clientNickName ( ) , treeEntryId : this.uniqueEntryId } ) ;
2018-02-27 16:20:49 +00:00
}
2018-06-20 17:06:55 +00:00
}
2020-09-07 10:42:00 +00:00
export enum MusicClientPlayerState {
SLEEPING ,
LOADING ,
PLAYING ,
PAUSED ,
STOPPED
}
2020-03-30 11:44:18 +00:00
export class MusicClientProperties extends ClientProperties {
2020-12-29 15:53:04 +00:00
player_state : number = 0 ;
2018-08-10 19:30:58 +00:00
player_volume : number = 0 ;
2019-01-20 17:43:14 +00:00
2020-12-29 15:53:04 +00:00
client_playlist_id : number = - 1 ;
2019-01-20 17:43:14 +00:00
client_disabled : boolean = false ;
2020-02-22 13:30:17 +00:00
client_flag_notify_song_change : boolean = false ;
client_bot_type : number = 0 ;
client_uptime_mode : number = 0 ;
2018-08-10 19:30:58 +00:00
}
2020-03-30 11:44:18 +00:00
export class SongInfo {
2020-02-02 14:05:36 +00:00
song_id : number = 0 ;
song_url : string = "" ;
song_invoker : number = 0 ;
song_loaded : boolean = false ;
/* only if song_loaded = true */
song_title : string = "" ;
song_description : string = "" ;
song_thumbnail : string = "" ;
song_length : number = 0 ;
}
2020-03-30 11:44:18 +00:00
export class MusicClientPlayerInfo extends SongInfo {
2018-11-26 19:52:56 +00:00
bot_id : number = 0 ;
2018-08-10 19:30:58 +00:00
player_state : number = 0 ;
player_buffered_index : number = 0 ;
player_replay_index : number = 0 ;
player_max_index : number = 0 ;
player_seekable : boolean = false ;
player_title : string = "" ;
player_description : string = "" ;
2018-06-20 17:06:55 +00:00
}
2020-12-29 15:53:04 +00:00
export interface MusicClientEvents extends ClientEvents {
notify_music_player_song_change : { newSong : SongInfo | undefined } ,
notify_music_player_timestamp : {
bufferedIndex : number ,
replayIndex : number
} ,
notify_subscribe_state_changed : { subscribed : boolean } ,
}
export class MusicClientEntry extends ClientEntry < MusicClientEvents > {
private subscribed : boolean ;
2018-08-10 19:30:58 +00:00
private _info_promise : Promise < MusicClientPlayerInfo > ;
private _info_promise_age : number = 0 ;
private _info_promise_resolve : any ;
private _info_promise_reject : any ;
2018-06-20 17:06:55 +00:00
constructor ( clientId , clientName ) {
super ( clientId , clientName , new MusicClientProperties ( ) ) ;
2020-12-29 15:53:04 +00:00
this . subscribed = false ;
2018-06-20 17:06:55 +00:00
}
2019-08-21 08:00:01 +00:00
destroy() {
super . destroy ( ) ;
this . _info_promise = undefined ;
this . _info_promise_reject = undefined ;
this . _info_promise_resolve = undefined ;
}
2023-11-21 00:44:46 +00:00
get properties ( ) : MusicClientProperties {
2018-06-20 17:06:55 +00:00
return this . _properties as MusicClientProperties ;
}
2023-11-21 00:44:46 +00:00
isSubscribed ( ) : boolean {
2020-12-29 15:53:04 +00:00
return this . subscribed ;
}
2023-11-21 00:44:46 +00:00
async subscribe ( ) : Promise < void > {
if ( this . subscribed ) {
2020-12-29 15:53:04 +00:00
return ;
}
await this . channelTree . client . serverConnection . send_command ( "musicbotsetsubscription" , { bot_id : this.properties.client_database_id } ) ;
this . channelTree . clients . forEach ( client = > {
2023-11-21 00:44:46 +00:00
if ( client instanceof MusicClientEntry ) {
if ( client . subscribed ) {
2020-12-29 15:53:04 +00:00
client . subscribed = false ;
client . events . fire ( "notify_subscribe_state_changed" , { subscribed : false } ) ;
}
}
} )
this . subscribed = true ;
this . events . fire ( "notify_subscribe_state_changed" , { subscribed : this.subscribed } ) ;
}
2018-06-20 17:06:55 +00:00
showContextMenu ( x : number , y : number , on_close : ( ) = > void = undefined ) : void {
2019-03-07 14:30:53 +00:00
let trigger_close = true ;
2019-06-30 14:03:28 +00:00
contextmenu . spawn_context_menu ( x , y ,
2019-08-21 08:00:01 +00:00
. . . this . contextmenu_info ( ) , {
2023-11-21 00:44:46 +00:00
name : ( contextmenu . get_provider ( ) . html_format_enabled ( ) ? "<b>" : "" ) +
tr ( "Change bot name" ) +
( contextmenu . get_provider ( ) . html_format_enabled ( ) ? "</b>" : "" ) ,
icon_class : "client-change_nickname" ,
disabled : false ,
callback : ( ) = > {
createInputModal ( tr ( "Change music bots nickname" ) , tr ( "New nickname:<br>" ) , text = > text . length >= 3 && text . length <= 31 , result = > {
if ( result ) {
this . channelTree . client . serverConnection . send_command ( "clientedit" , {
clid : this.clientId ( ) ,
client_nickname : result
} ) . then ( ( ) = > { } ) ;
}
} , { width : "40em" , min_width : "10em" , maxLength : 255 } ) . open ( ) ;
} ,
type : contextmenu . MenuEntryType . ENTRY
} , {
name : tr ( "Change bot description" ) ,
icon_class : "client-edit" ,
disabled : false ,
callback : ( ) = > {
createInputModal ( tr ( "Change music bots description" ) , tr ( "New description:<br>" ) , ( ) = > true , result = > {
if ( typeof ( result ) === 'string' ) {
this . channelTree . client . serverConnection . send_command ( "clientedit" , {
clid : this.clientId ( ) ,
client_description : result
} ) . then ( ( ) = > { } ) ;
}
} , { width : "60em" , min_width : "10em" , maxLength : 255 } ) . open ( ) ;
2019-01-20 17:43:14 +00:00
} ,
2023-11-21 00:44:46 +00:00
type : contextmenu . MenuEntryType . ENTRY
} ,
2019-01-20 17:43:14 +00:00
/ *
{
2018-12-05 19:46:33 +00:00
name : tr ( "Open music panel" ) ,
2018-06-20 17:06:55 +00:00
icon : "client-edit" ,
disabled : true ,
callback : ( ) = > { } ,
type : MenuEntryType . ENTRY
2019-01-20 17:43:14 +00:00
} ,
* /
{
2018-12-05 19:46:33 +00:00
name : tr ( "Quick url replay" ) ,
2019-06-30 14:03:28 +00:00
icon_class : "client-edit" ,
2018-11-04 12:54:18 +00:00
disabled : false ,
callback : ( ) = > {
2020-08-26 10:33:53 +00:00
createInputModal ( tr ( "Please enter the URL" ) , tr ( "URL:" ) , ( ) = > true , result = > {
2023-11-21 00:44:46 +00:00
if ( result ) {
2019-02-23 13:15:22 +00:00
this . channelTree . client . serverConnection . send_command ( "musicbotqueueadd" , {
2018-11-26 19:52:56 +00:00
bot_id : this.properties.client_database_id ,
2018-11-04 12:54:18 +00:00
type : "yt" , //Its a hint not a force!
url : result
} ) . catch ( error = > {
2023-11-21 00:44:46 +00:00
if ( error instanceof CommandResult ) {
2018-11-04 12:54:18 +00:00
error = error . extra_message || error . message ;
}
2018-12-05 19:46:33 +00:00
//TODO tr
createErrorModal ( tr ( "Failed to replay url" ) , "Failed to enqueue url:<br>" + error ) . open ( ) ;
2018-11-04 12:54:18 +00:00
} ) ;
}
} , { width : 400 , maxLength : 255 } ) . open ( ) ;
} ,
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . ENTRY
2018-06-20 17:06:55 +00:00
} ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . HR ( ) ,
2018-09-30 19:50:59 +00:00
. . . super . assignment_context ( ) ,
2023-11-21 00:44:46 +00:00
contextmenu . Entry . HR ( ) , {
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : "client-move_client_to_own_channel" ,
name : tr ( "Move client to your channel" ) ,
callback : ( ) = > {
this . channelTree . client . serverConnection . send_command ( "clientmove" , {
clid : this.clientId ( ) ,
cid : this.channelTree.client.getClient ( ) . currentChannel ( ) . getChannelId ( )
} ) . then ( ( ) = > { } ) ;
}
} , {
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : "client-kick_channel" ,
name : tr ( "Kick client from channel" ) ,
callback : ( ) = > {
createInputModal ( tr ( "Kick client from channel" ) , tr ( "Kick reason:<br>" ) , ( ) = > true , result = > {
if ( typeof ( result ) !== 'boolean' || result ) {
logInfo ( LogCategory . CLIENT , tr ( "Kicking client %o from channel with reason %o" ) , this . clientNickName ( ) , result ) ;
this . channelTree . client . serverConnection . send_command ( "clientkick" , {
clid : this.clientId ( ) ,
reasonid : ViewReasonId.VREASON_CHANNEL_KICK ,
reasonmsg : result
} ) . then ( ( ) = > { } ) ;
}
} , { width : 400 , maxLength : 255 } ) . open ( ) ;
}
} ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . HR ( ) ,
2018-11-03 23:39:29 +00:00
{
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : "client-volume" ,
2019-01-20 17:43:14 +00:00
name : tr ( "Change local volume" ) ,
2020-12-29 15:53:04 +00:00
callback : ( ) = > spawnClientVolumeChange ( this as any )
2019-01-20 17:43:14 +00:00
} ,
{
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . ENTRY ,
icon_class : "client-volume" ,
2019-01-20 17:43:14 +00:00
name : tr ( "Change remote volume" ) ,
callback : ( ) = > {
let max_volume = this . channelTree . client . permissions . neededPermission ( PermissionType . I_CLIENT_MUSIC_CREATE_MODIFY_MAX_VOLUME ) . value ;
2023-11-21 00:44:46 +00:00
if ( max_volume < 0 ) {
2019-01-20 17:43:14 +00:00
max_volume = 100 ;
2020-10-04 13:52:15 +00:00
}
2019-01-20 17:43:14 +00:00
2020-05-20 18:47:48 +00:00
spawnMusicBotVolumeChange ( this , max_volume / 100 ) ;
2018-11-03 23:39:29 +00:00
}
} ,
2019-11-09 14:56:01 +00:00
{
type : contextmenu . MenuEntryType . ENTRY ,
name : tr ( "Change playback latency" ) ,
callback : ( ) = > {
2020-12-29 15:53:04 +00:00
spawnChangeLatency ( this as any , this . voiceHandle . getLatencySettings ( ) , ( ) = > {
2020-09-07 10:42:00 +00:00
this . voiceHandle . resetLatencySettings ( ) ;
return this . voiceHandle . getLatencySettings ( ) ;
} , settings = > this . voiceHandle . setLatencySettings ( settings ) , ( ) = > this . voiceHandle . flushBuffer ( ) ) ;
2019-11-09 14:56:01 +00:00
} ,
2020-09-07 10:42:00 +00:00
visible : ! ! this . voiceHandle
2019-11-09 14:56:01 +00:00
} ,
2019-06-30 14:03:28 +00:00
contextmenu . Entry . HR ( ) ,
2018-06-20 17:06:55 +00:00
{
2018-12-05 19:46:33 +00:00
name : tr ( "Delete bot" ) ,
2019-06-30 14:03:28 +00:00
icon_class : "client-delete" ,
2018-11-04 12:54:18 +00:00
disabled : false ,
2018-06-20 17:06:55 +00:00
callback : ( ) = > {
2021-04-24 12:52:08 +00:00
promptYesNo ( {
title : tr ( "Are you sure?" ) ,
question : tra ( "Do you really want to delete {0}" , this . clientNickName ( ) )
} ) . then ( result = > {
2023-11-21 00:44:46 +00:00
if ( ! result ) {
2021-04-24 12:52:08 +00:00
return ;
2020-05-20 18:47:48 +00:00
}
2021-04-24 12:52:08 +00:00
this . channelTree . client . serverConnection . send_command ( "musicbotdelete" , {
bot_id : this.properties.client_database_id
2023-11-21 00:44:46 +00:00
} ) . then ( ( ) = > { } ) ;
2018-11-04 12:54:18 +00:00
} ) ;
2018-06-20 17:06:55 +00:00
} ,
2019-06-30 14:03:28 +00:00
type : contextmenu . MenuEntryType . ENTRY
2018-06-20 17:06:55 +00:00
} ,
2020-02-02 14:05:36 +00:00
contextmenu . Entry . CLOSE ( ( ) = > trigger_close && on_close ? on_close ( ) : { } )
2018-06-20 17:06:55 +00:00
) ;
}
2018-08-10 19:30:58 +00:00
handlePlayerInfo ( json ) {
2023-11-21 00:44:46 +00:00
if ( json ) {
2019-09-12 21:59:35 +00:00
const info = new MusicClientPlayerInfo ( ) ;
2020-05-20 18:47:48 +00:00
JSON . map_to ( info , json ) ;
2023-11-21 00:44:46 +00:00
if ( this . _info_promise_resolve )
2018-08-10 19:30:58 +00:00
this . _info_promise_resolve ( info ) ;
this . _info_promise_reject = undefined ;
this . _info_promise_resolve = undefined ;
}
}
2023-11-21 00:44:46 +00:00
requestPlayerInfo ( max_age : number = 1000 ) : Promise < MusicClientPlayerInfo > {
if ( this . _info_promise !== undefined && this . _info_promise_age > 0 && Date . now ( ) - max_age <= this . _info_promise_age ) return this . _info_promise ;
2018-08-10 19:30:58 +00:00
this . _info_promise_age = Date . now ( ) ;
this . _info_promise = new Promise < MusicClientPlayerInfo > ( ( resolve , reject ) = > {
this . _info_promise_reject = reject ;
this . _info_promise_resolve = resolve ;
} ) ;
2023-11-21 00:44:46 +00:00
this . channelTree . client . serverConnection . send_command ( "musicbotplayerinfo" , { bot_id : this.properties.client_database_id } ) . then ( ( ) = > { } ) ;
2018-08-10 19:30:58 +00:00
return this . _info_promise ;
}
2020-09-07 10:42:00 +00:00
isCurrentlyPlaying() {
switch ( this . properties . player_state ) {
case MusicClientPlayerState . PLAYING :
case MusicClientPlayerState . LOADING :
return true ;
default :
return false ;
}
}
2018-02-27 16:20:49 +00:00
}