2018-02-27 17:20:49 +01:00
|
|
|
/// <reference path="channel.ts" />
|
2018-04-11 17:56:09 +02:00
|
|
|
/// <reference path="modal/ModalChangeVolume.ts" />
|
2018-02-27 17:20:49 +01:00
|
|
|
|
2018-04-30 23:57:21 +02:00
|
|
|
enum ClientType {
|
|
|
|
CLIENT_VOICE,
|
|
|
|
CLIENT_QUERY,
|
|
|
|
CLIENT_INTERNAL,
|
|
|
|
CLIENT_WEB,
|
|
|
|
CLIENT_MUSIC,
|
|
|
|
CLIENT_UNDEFINED
|
|
|
|
}
|
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
class ClientProperties {
|
2018-04-30 23:57:21 +02:00
|
|
|
client_type: ClientType = ClientType.CLIENT_VOICE; //TeamSpeaks type
|
|
|
|
client_type_exact: ClientType = ClientType.CLIENT_VOICE;
|
2018-08-10 21:30:58 +02:00
|
|
|
|
|
|
|
client_database_id: number = 0;
|
2018-04-16 20:38:35 +02: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;
|
|
|
|
client_lastconnected: number = 0;
|
|
|
|
|
|
|
|
client_flag_avatar: string = "";
|
2018-08-12 14:14:50 +02:00
|
|
|
client_icon_id: number = 0;
|
2018-04-16 20:38:35 +02:00
|
|
|
|
|
|
|
client_away_message: string = "";
|
|
|
|
client_away: boolean = false;
|
|
|
|
|
|
|
|
|
|
|
|
client_input_hardware: boolean = false;
|
2018-04-30 23:57:21 +02:00
|
|
|
client_output_hardware: boolean = false;
|
2018-04-16 20:38:35 +02:00
|
|
|
client_input_muted: boolean = false;
|
2018-04-30 23:57:21 +02:00
|
|
|
client_output_muted: boolean = false;
|
2018-04-16 20:38:35 +02:00
|
|
|
client_is_channel_commander: boolean = false;
|
2018-04-30 23:57:21 +02:00
|
|
|
|
|
|
|
client_teaforum_id: number = 0;
|
|
|
|
client_teaforum_name: string = "";
|
2018-08-12 13:26:56 +02:00
|
|
|
|
|
|
|
client_talk_power: number = 0;
|
2018-04-16 20:38:35 +02:00
|
|
|
}
|
|
|
|
|
2018-02-27 17:20:49 +01:00
|
|
|
class ClientEntry {
|
2018-04-30 23:57:21 +02:00
|
|
|
protected _clientId: number;
|
|
|
|
protected _channel: ChannelEntry;
|
|
|
|
protected _tag: JQuery<HTMLElement>;
|
2018-04-16 20:38:35 +02:00
|
|
|
|
2018-04-30 23:57:21 +02:00
|
|
|
protected _properties: ClientProperties;
|
|
|
|
protected lastVariableUpdate: number = 0;
|
|
|
|
protected _speaking: boolean = false;
|
2018-02-27 17:20:49 +01:00
|
|
|
|
|
|
|
channelTree: ChannelTree;
|
|
|
|
audioController: AudioController;
|
|
|
|
|
2018-04-30 23:57:21 +02:00
|
|
|
constructor(clientId, clientName, properties: ClientProperties = new ClientProperties()) {
|
|
|
|
this._properties = properties;
|
|
|
|
this._properties.client_nickname = clientName;
|
2018-02-27 17:20:49 +01:00
|
|
|
this._clientId = clientId;
|
|
|
|
this.channelTree = null;
|
|
|
|
this._channel = null;
|
|
|
|
this.audioController = new AudioController();
|
|
|
|
|
|
|
|
const _this = this;
|
|
|
|
this.audioController.onSpeaking = function () {
|
|
|
|
_this.speaking = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
this.audioController.onSilence = function () {
|
|
|
|
_this.speaking = false;
|
|
|
|
};
|
2018-04-11 17:56:09 +02:00
|
|
|
this.audioController.initialize();
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
2018-04-30 23:57:21 +02:00
|
|
|
get properties() : ClientProperties {
|
|
|
|
return this._properties;
|
|
|
|
}
|
|
|
|
|
2018-02-27 17:20:49 +01:00
|
|
|
currentChannel() { return this._channel; }
|
|
|
|
clientNickName(){ return this.properties.client_nickname; }
|
|
|
|
clientUid(){ return this.properties.client_unique_identifier; }
|
|
|
|
clientId(){ return this._clientId; }
|
|
|
|
|
|
|
|
getAudioController() : AudioController {
|
|
|
|
return this.audioController;
|
|
|
|
}
|
|
|
|
|
|
|
|
initializeListener(){
|
|
|
|
const _this = this;
|
2018-04-16 20:38:35 +02:00
|
|
|
this.tag.click(event => {
|
2018-02-27 17:20:49 +01:00
|
|
|
_this.channelTree.onSelect(_this);
|
|
|
|
});
|
2018-04-16 20:38:35 +02:00
|
|
|
|
|
|
|
if(!settings.static(Settings.KEY_DISABLE_CONTEXT_MENU, false)) {
|
|
|
|
this.tag.on("contextmenu", function (event) {
|
|
|
|
event.preventDefault();
|
|
|
|
_this.channelTree.onSelect(_this);
|
|
|
|
_this.showContextMenu(event.pageX, event.pageY, () => {
|
|
|
|
_this.channelTree.onSelect(undefined);
|
|
|
|
});
|
|
|
|
return false;
|
2018-02-27 17:20:49 +01:00
|
|
|
});
|
2018-04-16 20:38:35 +02:00
|
|
|
}
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
showContextMenu(x: number, y: number, on_close: () => void = undefined) {
|
|
|
|
const _this = this;
|
|
|
|
|
|
|
|
spawnMenu(x, y,
|
|
|
|
{
|
|
|
|
type: MenuEntryType.ENTRY,
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-change_nickname",
|
2018-02-27 17:20:49 +01:00
|
|
|
name: "<b>Open text chat</b>",
|
|
|
|
callback: function () {
|
|
|
|
chat.activeChat = _this.chat(true);
|
|
|
|
chat.focus();
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
type: MenuEntryType.ENTRY,
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-poke",
|
|
|
|
name: "Poke client",
|
2018-02-27 17:20:49 +01:00
|
|
|
callback: function () {
|
2018-05-09 11:50:05 +02:00
|
|
|
createInputModal("Poke client", "Poke message:<br>", text => true, result => {
|
2018-02-27 17:20:49 +01:00
|
|
|
if(result) {
|
2018-05-09 11:50:05 +02:00
|
|
|
console.log("Poking client " + _this.clientNickName() + " with message " + result);
|
2018-02-27 17:20:49 +01:00
|
|
|
_this.channelTree.client.serverConnection.sendCommand("clientpoke", {
|
|
|
|
clid: _this.clientId(),
|
|
|
|
msg: result
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}, { width: 400, maxLength: 512 }).open();
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
type: MenuEntryType.ENTRY,
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-edit",
|
2018-02-27 17:20:49 +01:00
|
|
|
name: "Change description",
|
|
|
|
callback: function () {
|
2018-05-09 11:50:05 +02:00
|
|
|
createInputModal("Change client description", "New description:<br>", text => true, result => {
|
2018-02-27 17:20:49 +01:00
|
|
|
if(result) {
|
|
|
|
console.log("Changing " + _this.clientNickName() + "'s description to " + result);
|
|
|
|
_this.channelTree.client.serverConnection.sendCommand("clientedit", {
|
|
|
|
clid: _this.clientId(),
|
|
|
|
client_description: result
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}, { width: 400, maxLength: 1024 }).open();
|
|
|
|
}
|
|
|
|
},
|
2018-04-11 17:56:09 +02:00
|
|
|
MenuEntry.HR(), {
|
|
|
|
type: MenuEntryType.ENTRY,
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-move_client_to_own_channel",
|
|
|
|
name: "Move client to your channel",
|
2018-04-11 17:56:09 +02:00
|
|
|
callback: () => {
|
|
|
|
this.channelTree.client.serverConnection.sendCommand("clientmove", {
|
|
|
|
clid: this.clientId(),
|
|
|
|
cid: this.channelTree.client.getClient().currentChannel().getChannelId()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}, {
|
2018-02-27 17:20:49 +01:00
|
|
|
type: MenuEntryType.ENTRY,
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-kick_channel",
|
|
|
|
name: "Kick client from channel",
|
2018-02-27 17:20:49 +01:00
|
|
|
callback: function () {
|
2018-05-09 11:50:05 +02:00
|
|
|
createInputModal("Kick client from channel", "Kick reason:<br>", text => true, result => {
|
2018-02-27 17:20:49 +01:00
|
|
|
if(result) {
|
2018-05-09 11:50:05 +02:00
|
|
|
console.log("Kicking client " + _this.clientNickName() + " from channel with reason " + result);
|
2018-02-27 17:20:49 +01:00
|
|
|
_this.channelTree.client.serverConnection.sendCommand("clientkick", {
|
|
|
|
clid: _this.clientId(),
|
|
|
|
reasonid: ViewReasonId.VREASON_CHANNEL_KICK,
|
|
|
|
reasonmsg: result
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}, { width: 400, maxLength: 255 }).open();
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
type: MenuEntryType.ENTRY,
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-kick_server",
|
|
|
|
name: "Kick client fom server",
|
2018-02-27 17:20:49 +01:00
|
|
|
callback: function () {
|
2018-05-09 11:50:05 +02:00
|
|
|
createInputModal("Kick client from server", "Kick reason:<br>", text => true, result => {
|
2018-02-27 17:20:49 +01:00
|
|
|
if(result) {
|
2018-05-09 11:50:05 +02:00
|
|
|
console.log("Kicking client " + _this.clientNickName() + " from server with reason " + result);
|
2018-02-27 17:20:49 +01:00
|
|
|
_this.channelTree.client.serverConnection.sendCommand("clientkick", {
|
|
|
|
clid: _this.clientId(),
|
|
|
|
reasonid: ViewReasonId.VREASON_SERVER_KICK,
|
|
|
|
reasonmsg: result
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}, { width: 400, maxLength: 255 }).open();
|
|
|
|
}
|
2018-04-11 17:56:09 +02:00
|
|
|
}, {
|
|
|
|
type: MenuEntryType.ENTRY,
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-ban_client",
|
|
|
|
name: "Ban client",
|
2018-04-30 23:57:21 +02:00
|
|
|
invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1),
|
|
|
|
callback: () => {
|
|
|
|
Modals.spawnBanClient(this.properties.client_nickname, (duration, reason) => {
|
|
|
|
this.channelTree.client.serverConnection.sendCommand("banclient", {
|
|
|
|
uid: this.properties.client_unique_identifier,
|
|
|
|
banreason: reason,
|
|
|
|
time: duration
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2018-04-11 17:56:09 +02:00
|
|
|
},
|
|
|
|
MenuEntry.HR(),
|
|
|
|
{
|
|
|
|
type: MenuEntryType.ENTRY,
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-volume",
|
2018-04-11 17:56:09 +02:00
|
|
|
name: "Change Volume",
|
|
|
|
callback: () => {
|
|
|
|
Modals.spawnChangeVolume(this.audioController.volume, volume => {
|
2018-04-16 20:38:35 +02:00
|
|
|
settings.changeServer("volume_client_" + this.clientUid(), volume);
|
2018-04-11 17:56:09 +02:00
|
|
|
this.audioController.volume = volume;
|
|
|
|
if(globalClient.selectInfo.currentSelected == this)
|
|
|
|
globalClient.selectInfo.update();
|
|
|
|
});
|
|
|
|
}
|
2018-02-27 17:20:49 +01:00
|
|
|
},
|
|
|
|
MenuEntry.CLOSE(on_close)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
get tag() : JQuery<HTMLElement> {
|
|
|
|
if(this._tag) return this._tag;
|
2018-02-27 17:20:49 +01:00
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
let tag = $.spawn("div");
|
2018-02-27 17:20:49 +01:00
|
|
|
|
|
|
|
tag.attr("id", "client_" + this.clientId());
|
|
|
|
tag.addClass("client");
|
2018-04-16 20:38:35 +02:00
|
|
|
tag.append($.spawn("div").addClass("icon_empty"));
|
2018-02-27 17:20:49 +01:00
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
tag.append($.spawn("div").addClass("icon_client_state").attr("title", "Client state"));
|
2018-02-27 17:20:49 +01:00
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
tag.append($.spawn("div").addClass("name").text(this.clientNickName()));
|
|
|
|
tag.append($.spawn("div").addClass("away").text(this.clientNickName()));
|
2018-02-27 17:20:49 +01:00
|
|
|
|
|
|
|
let clientIcons = $.spawn("span");
|
2018-08-12 13:26:56 +02:00
|
|
|
clientIcons.append($.spawn("div").addClass("icon icon_talk_power client-input_muted").hide());
|
2018-08-12 14:14:50 +02:00
|
|
|
clientIcons.append($.spawn("span").addClass("group_icons"));
|
|
|
|
clientIcons.append($.spawn("span").addClass("client_icon"));
|
2018-02-27 17:20:49 +01:00
|
|
|
tag.append(clientIcons);
|
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
return this._tag = tag;
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 17:56:09 +02:00
|
|
|
static chatTag(id: number, name: string, uid: string, braces: boolean = false) : JQuery {
|
2018-02-27 17:20:49 +01:00
|
|
|
let tag = $.spawn("div");
|
|
|
|
|
2018-04-30 23:57:21 +02:00
|
|
|
tag.css("cursor", "pointer")
|
|
|
|
.css("font-weight", "bold")
|
|
|
|
.css("color", "darkblue")
|
|
|
|
.css("display", "inline-block")
|
|
|
|
.css("margin", 0);
|
2018-03-07 19:06:52 +01:00
|
|
|
|
2018-02-27 17:20:49 +01:00
|
|
|
if(braces)
|
|
|
|
tag.text("\"" + name + "\"");
|
|
|
|
else
|
|
|
|
tag.text(name);
|
2018-04-30 23:57:21 +02:00
|
|
|
|
|
|
|
tag.contextmenu(event => {
|
|
|
|
if(event.isDefaultPrevented()) return;
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
let client = globalClient.channelTree.findClient(id);
|
|
|
|
if(!client) return;
|
|
|
|
if(client.properties.client_unique_identifier != uid) return;
|
|
|
|
client.showContextMenu(event.pageX, event.pageY);
|
|
|
|
});
|
2018-02-27 17:20:49 +01:00
|
|
|
tag.attr("clientId", id);
|
|
|
|
tag.attr("clientUid", uid);
|
|
|
|
tag.attr("clientName", name);
|
2018-04-30 23:57:21 +02:00
|
|
|
return tag;
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
2018-04-11 17:56:09 +02:00
|
|
|
createChatTag(braces: boolean = false) : JQuery {
|
2018-02-27 17:20:49 +01:00
|
|
|
return ClientEntry.chatTag(this.clientId(), this.clientNickName(), this.clientUid(), braces);
|
|
|
|
}
|
|
|
|
|
|
|
|
set speaking(flag) {
|
|
|
|
if(flag == this._speaking) return;
|
|
|
|
this._speaking = flag;
|
2018-08-12 13:26:56 +02:00
|
|
|
this.updateClientSpeakIcon();
|
|
|
|
}
|
|
|
|
|
|
|
|
updateClientStatusIcons() {
|
|
|
|
let talk_power = this.properties.client_talk_power >= this._channel.properties.channel_needed_talk_power;
|
|
|
|
if(talk_power)
|
|
|
|
this.tag.find("span").find(".icon_talk_power").hide();
|
|
|
|
else
|
|
|
|
this.tag.find("span").find(".icon_talk_power").show();
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
2018-08-12 13:26:56 +02:00
|
|
|
updateClientSpeakIcon() {
|
2018-02-27 17:20:49 +01:00
|
|
|
let icon: string = "";
|
|
|
|
let clicon: string = "";
|
2018-04-16 20:38:35 +02:00
|
|
|
if(this.properties.client_away) {
|
2018-05-09 11:50:05 +02:00
|
|
|
icon = "client-away";
|
2018-04-16 20:38:35 +02:00
|
|
|
} else if(this.properties.client_output_muted) {
|
2018-05-09 11:50:05 +02:00
|
|
|
icon = "client-hardware_output_muted";
|
2018-04-16 20:38:35 +02:00
|
|
|
} else if(!this.properties.client_input_hardware) {
|
2018-05-09 11:50:05 +02:00
|
|
|
icon = "client-hardware_input_muted";
|
2018-04-16 20:38:35 +02:00
|
|
|
} else if(this.properties.client_input_muted) {
|
2018-05-09 11:50:05 +02:00
|
|
|
icon = "client-input_muted";
|
2018-02-27 17:20:49 +01:00
|
|
|
} else {
|
|
|
|
if(this._speaking) {
|
2018-04-16 20:38:35 +02:00
|
|
|
if(this.properties.client_is_channel_commander)
|
2018-02-27 17:20:49 +01:00
|
|
|
clicon = "client_cc_talk";
|
|
|
|
else
|
|
|
|
clicon = "client_talk";
|
|
|
|
} else {
|
2018-04-16 20:38:35 +02:00
|
|
|
if(this.properties.client_is_channel_commander)
|
2018-02-27 17:20:49 +01:00
|
|
|
clicon = "client_cc_idle";
|
|
|
|
else
|
|
|
|
clicon = "client_idle";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(clicon.length > 0)
|
2018-04-16 20:38:35 +02:00
|
|
|
this.tag.find(".icon_client_state").attr('class', 'icon_client_state clicon ' + clicon);
|
2018-02-27 17:20:49 +01:00
|
|
|
else if(icon.length > 0)
|
2018-04-16 20:38:35 +02:00
|
|
|
this.tag.find(".icon_client_state").attr('class', 'icon_client_state icon ' + icon);
|
2018-02-27 17:20:49 +01:00
|
|
|
else
|
2018-04-16 20:38:35 +02:00
|
|
|
this.tag.find(".icon_client_state").attr('class', 'icon_client_state icon_empty');
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
updateAwayMessage() {
|
2018-04-16 20:38:35 +02:00
|
|
|
let tag = this.tag.find(".away");
|
|
|
|
if(this.properties.client_away == true && this.properties.client_away_message){
|
2018-02-27 17:20:49 +01:00
|
|
|
tag.text("[" + this.properties.client_away_message + "]");
|
|
|
|
tag.show();
|
|
|
|
} else {
|
|
|
|
tag.hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
updateVariables(...variables: {key: string, value: string}[]) {
|
|
|
|
let group = log.group(log.LogType.DEBUG, LogCategory.CLIENT, "Update properties (%i) of %s (%i)", variables.length, this.clientNickName(), this.clientId());
|
|
|
|
|
|
|
|
for(let variable of variables) {
|
2018-08-10 21:30:58 +02:00
|
|
|
JSON.map_field_to(this._properties, variable.value, variable.key);
|
|
|
|
|
2018-05-09 11:50:05 +02:00
|
|
|
group.log("Updating client " + this.clientId() + ". Key " + variable.key + " Value: '" + variable.value + "' (" + typeof (this.properties[variable.key]) + ")");
|
2018-04-16 20:38:35 +02:00
|
|
|
if(variable.key == "client_nickname") {
|
|
|
|
this.tag.find(".name").text(variable.value);
|
|
|
|
let chat = this.chat(false);
|
|
|
|
if(chat) chat.name = variable.value;
|
2018-08-12 14:14:50 +02:00
|
|
|
if(this._channel)
|
|
|
|
this._channel.reorderClients();
|
2018-04-16 20:38:35 +02:00
|
|
|
}
|
|
|
|
if(variable.key == "client_away" || variable.key == "client_output_muted" || variable.key == "client_input_hardware" || variable.key == "client_input_muted" || variable.key == "client_is_channel_commander"){
|
2018-08-12 13:26:56 +02:00
|
|
|
this.updateClientSpeakIcon();
|
2018-04-16 20:38:35 +02:00
|
|
|
}
|
|
|
|
if(variable.key == "client_away_message" || variable.key == "client_away") {
|
|
|
|
this.updateAwayMessage();
|
|
|
|
}
|
|
|
|
if(variable.key == "client_unique_identifier") {
|
|
|
|
this.audioController.volume = parseFloat(settings.server("volume_client_" + this.clientUid(), "1"));
|
|
|
|
console.error("Updated volume from config " + this.audioController.volume + " - " + "volume_client_" + this.clientUid() + " - " + settings.server("volume_client_" + this.clientUid(), "1"));
|
|
|
|
console.log(this.avatarId());
|
|
|
|
}
|
2018-08-12 13:26:56 +02:00
|
|
|
if(variable.key == "client_talk_power") {
|
|
|
|
if(this._channel) {
|
|
|
|
this._channel.reorderClients();
|
|
|
|
this.updateClientStatusIcons();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2018-08-12 14:14:50 +02:00
|
|
|
if(variable.key == "client_icon_id")
|
|
|
|
this.updateClientIcon();
|
2018-04-11 17:56:09 +02:00
|
|
|
}
|
2018-04-16 20:38:35 +02:00
|
|
|
|
|
|
|
group.end();
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
updateClientVariables(){
|
2018-02-27 17:20:49 +01:00
|
|
|
if(this.lastVariableUpdate == 0 || new Date().getTime() - 10 * 60 * 1000 > this.lastVariableUpdate){ //Cache these only 10 min
|
|
|
|
this.lastVariableUpdate = new Date().getTime();
|
|
|
|
this.channelTree.client.serverConnection.sendCommand("clientgetvariables", {clid: this.clientId()});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chat(create: boolean = false) : ChatEntry {
|
|
|
|
let chatName = "client_" + this.clientUid() + ":" + this.clientId();
|
|
|
|
let c = chat.findChat(chatName);
|
|
|
|
if((!c) && create) {
|
|
|
|
c = chat.createChat(chatName);
|
|
|
|
c.closeable = true;
|
|
|
|
c.name = this.clientNickName();
|
|
|
|
|
|
|
|
const _this = this;
|
|
|
|
c.onMessageSend = function (text: string) {
|
|
|
|
_this.channelTree.client.serverConnection.sendMessage(text, ChatType.CLIENT, _this);
|
|
|
|
};
|
|
|
|
|
|
|
|
c.onClose = function () : boolean {
|
|
|
|
//TODO check online?
|
|
|
|
_this.channelTree.client.serverConnection.sendCommand("clientchatclosed", {"clid": _this.clientId()});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2018-08-12 14:14:50 +02:00
|
|
|
updateClientIcon() {
|
|
|
|
this.tag.find("span .client_icon").children().detach();
|
|
|
|
if(this.properties.client_icon_id > 0) {
|
|
|
|
this.channelTree.client.fileManager.icons.generateTag(this.properties.client_icon_id).attr("title", "Client icon")
|
|
|
|
.appendTo(this.tag.find("span .client_icon"));
|
|
|
|
}
|
|
|
|
}
|
2018-04-16 20:38:35 +02:00
|
|
|
|
2018-02-27 17:20:49 +01:00
|
|
|
updateGroupIcon(group: Group) {
|
|
|
|
//TODO group icon order
|
2018-08-12 14:14:50 +02:00
|
|
|
this.tag.find(".group_icons .icon_group_" + group.id).detach();
|
2018-02-27 17:20:49 +01:00
|
|
|
|
2018-08-12 14:14:50 +02:00
|
|
|
if (group.properties.iconid > 0) {
|
|
|
|
this.tag.find("span .group_icons").append(
|
|
|
|
$.spawn("div").addClass("icon_group_" + group.id).append(this.channelTree.client.fileManager.icons.generateTag(group.properties.iconid)).attr("title", group.name)
|
|
|
|
);
|
|
|
|
}
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
assignedServerGroupIds() : number[] {
|
|
|
|
let result = [];
|
|
|
|
for(let id of this.properties.client_servergroups.split(",")){
|
|
|
|
if(id.length == 0) continue;
|
|
|
|
result.push(Number.parseInt(id));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
assignedChannelGroup() : number {
|
2018-04-16 20:38:35 +02:00
|
|
|
return this.properties.client_channel_group_id;
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
groupAssigned(group: Group) : boolean {
|
|
|
|
if(group.target == GroupTarget.SERVER) {
|
|
|
|
for(let id of this.assignedServerGroupIds())
|
|
|
|
if(id == group.id) return true;
|
|
|
|
return false;
|
|
|
|
} else return group.id == this.assignedChannelGroup();
|
|
|
|
}
|
|
|
|
|
|
|
|
onDelete() {
|
|
|
|
this.audioController.close();
|
|
|
|
this.audioController = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
calculateOnlineTime() : number {
|
2018-04-30 23:57:21 +02:00
|
|
|
return Date.now() / 1000 - this.properties.client_lastconnected;
|
2018-04-16 20:38:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
avatarId?() : string {
|
|
|
|
function str2ab(str) {
|
|
|
|
let buf = new ArrayBuffer(str.length); // 2 bytes for each char
|
|
|
|
let bufView = new Uint8Array(buf);
|
|
|
|
for (let i=0, strLen=str.length; i<strLen; i++) {
|
|
|
|
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 = "";
|
|
|
|
for(let index = 0; index < input.length; index++) {
|
|
|
|
let c = input.charAt(index);
|
|
|
|
let offset: number = 0;
|
|
|
|
if(c >= '0' && c <= '9')
|
|
|
|
offset = c.charCodeAt(0) - '0'.charCodeAt(0);
|
|
|
|
else if(c >= 'A' && c <= 'F')
|
|
|
|
offset = c.charCodeAt(0) - 'A'.charCodeAt(0) + 0x0A;
|
|
|
|
else if(c >= 'a' && c <= 'f')
|
|
|
|
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 17:20:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class LocalClientEntry extends ClientEntry {
|
|
|
|
handle: TSClient;
|
|
|
|
|
|
|
|
private renaming: boolean;
|
|
|
|
|
|
|
|
constructor(handle: TSClient) {
|
2018-05-09 11:50:05 +02:00
|
|
|
super(0, "local client");
|
2018-02-27 17:20:49 +01:00
|
|
|
this.handle = handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
showContextMenu(x: number, y: number, on_close: () => void = undefined): void {
|
|
|
|
const _self = this;
|
|
|
|
|
|
|
|
spawnMenu(x, y,
|
|
|
|
{
|
|
|
|
name: "<b>Change name</b>",
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-change_nickname",
|
2018-02-27 17:20:49 +01:00
|
|
|
callback: () =>_self.openRename(),
|
|
|
|
type: MenuEntryType.ENTRY
|
|
|
|
}, {
|
|
|
|
name: "Change description",
|
2018-05-09 11:50:05 +02:00
|
|
|
icon: "client-edit",
|
2018-02-27 17:20:49 +01:00
|
|
|
callback: () => {
|
|
|
|
createInputModal("Change own description", "New description:<br>", text => true, result => {
|
|
|
|
if(result) {
|
|
|
|
console.log("Changing own description to " + result);
|
|
|
|
_self.channelTree.client.serverConnection.sendCommand("clientedit", {
|
|
|
|
clid: _self.clientId(),
|
|
|
|
client_description: result
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}, { width: 400, maxLength: 1024 }).open();
|
|
|
|
},
|
|
|
|
type: MenuEntryType.ENTRY
|
|
|
|
},
|
|
|
|
MenuEntry.CLOSE(on_close)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
initializeListener(): void {
|
|
|
|
super.initializeListener();
|
2018-04-16 20:38:35 +02:00
|
|
|
this.tag.find(".name").addClass("own_name");
|
2018-02-27 17:20:49 +01:00
|
|
|
|
|
|
|
const _self = this;
|
2018-04-16 20:38:35 +02:00
|
|
|
this.tag.dblclick(function () {
|
2018-02-27 17:20:49 +01:00
|
|
|
_self.openRename();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
openRename() : void {
|
|
|
|
const _self = this;
|
|
|
|
|
2018-04-16 20:38:35 +02:00
|
|
|
const elm = this.tag.find(".name");
|
2018-02-27 17:20:49 +01:00
|
|
|
elm.attr("contenteditable", "true");
|
|
|
|
elm.removeClass("own_name");
|
|
|
|
elm.css("background-color", "white");
|
|
|
|
elm.focus();
|
|
|
|
_self.renaming = true;
|
|
|
|
|
|
|
|
elm.keypress(function (e) {
|
2018-04-19 18:42:34 +02:00
|
|
|
if(e.keyCode == JQuery.Key.Enter) {
|
2018-02-27 17:20:49 +01:00
|
|
|
$(this).trigger("focusout");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
elm.focusout(function (e) {
|
|
|
|
if(!_self.renaming) return;
|
|
|
|
_self.renaming = false;
|
|
|
|
|
|
|
|
elm.css("background-color", "");
|
|
|
|
elm.removeAttr("contenteditable");
|
|
|
|
elm.addClass("own_name");
|
|
|
|
let text = elm.text().toString();
|
|
|
|
if(_self.clientNickName() == text) return;
|
|
|
|
|
|
|
|
elm.text(_self.clientNickName());
|
|
|
|
_self.handle.serverConnection.updateClient("client_nickname", text).then((e) => {
|
|
|
|
chat.serverChat().appendMessage("Nickname successfully changed");
|
|
|
|
}).catch((e: CommandResult) => {
|
|
|
|
chat.serverChat().appendError("Could not change nickname (" + e.extra_message + ")");
|
|
|
|
_self.openRename();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2018-06-20 19:06:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class MusicClientProperties extends ClientProperties {
|
2018-08-10 21:30:58 +02:00
|
|
|
player_state: number = 0;
|
|
|
|
player_volume: number = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
class MusicClientPlayerInfo {
|
|
|
|
botid: number = 0;
|
|
|
|
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 = "";
|
|
|
|
|
|
|
|
song_id: number = 0;
|
|
|
|
song_url: string = "";
|
|
|
|
song_invoker: number = 0;
|
|
|
|
song_loaded: boolean = false;
|
|
|
|
song_title: string = "";
|
|
|
|
song_thumbnail: string = "";
|
|
|
|
song_length: number = 0;
|
2018-06-20 19:06:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
class MusicClientEntry extends ClientEntry {
|
2018-08-10 21:30:58 +02: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 19:06:55 +02:00
|
|
|
constructor(clientId, clientName) {
|
|
|
|
super(clientId, clientName, new MusicClientProperties());
|
|
|
|
}
|
|
|
|
|
|
|
|
get properties() : MusicClientProperties {
|
|
|
|
return this._properties as MusicClientProperties;
|
|
|
|
}
|
|
|
|
|
|
|
|
showContextMenu(x: number, y: number, on_close: () => void = undefined): void {
|
|
|
|
spawnMenu(x, y,
|
|
|
|
{
|
|
|
|
name: "<b>Change bot name</b>",
|
|
|
|
icon: "client-change_nickname",
|
|
|
|
disabled: true,
|
|
|
|
callback: () => {},
|
|
|
|
type: MenuEntryType.ENTRY
|
|
|
|
}, {
|
|
|
|
name: "Change bot description",
|
|
|
|
icon: "client-edit",
|
|
|
|
disabled: true,
|
|
|
|
callback: () => {},
|
|
|
|
type: MenuEntryType.ENTRY
|
|
|
|
}, {
|
|
|
|
name: "Open music panel",
|
|
|
|
icon: "client-edit",
|
|
|
|
disabled: true,
|
|
|
|
callback: () => {},
|
|
|
|
type: MenuEntryType.ENTRY
|
|
|
|
},
|
|
|
|
MenuEntry.HR(),
|
|
|
|
{
|
|
|
|
name: "Delete bot",
|
|
|
|
icon: "client-delete",
|
|
|
|
disabled: true,
|
|
|
|
callback: () => {
|
|
|
|
//TODO
|
|
|
|
},
|
|
|
|
type: MenuEntryType.ENTRY
|
|
|
|
},
|
|
|
|
MenuEntry.CLOSE(on_close)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
initializeListener(): void {
|
|
|
|
super.initializeListener();
|
|
|
|
}
|
2018-08-10 21:30:58 +02:00
|
|
|
|
|
|
|
handlePlayerInfo(json) {
|
|
|
|
if(json) {
|
|
|
|
let info = JSON.map_to(new MusicClientPlayerInfo(), json);
|
|
|
|
if(this._info_promise_resolve)
|
|
|
|
this._info_promise_resolve(info);
|
|
|
|
this._info_promise_reject = undefined;
|
|
|
|
}
|
|
|
|
if(this._info_promise) {
|
|
|
|
if(this._info_promise_reject)
|
|
|
|
this._info_promise_reject("timeout");
|
|
|
|
this._info_promise = undefined;
|
|
|
|
this._info_promise_age = undefined;
|
|
|
|
this._info_promise_reject = undefined;
|
|
|
|
this._info_promise_resolve = undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
requestPlayerInfo(max_age: number = 1000) : Promise<MusicClientPlayerInfo> {
|
|
|
|
if(this._info_promise && this._info_promise_age && Date.now() - max_age <= this._info_promise_age) return this._info_promise;
|
|
|
|
this._info_promise_age = Date.now();
|
|
|
|
this._info_promise = new Promise<MusicClientPlayerInfo>((resolve, reject) => {
|
|
|
|
this._info_promise_reject = reject;
|
|
|
|
this._info_promise_resolve = resolve;
|
|
|
|
});
|
|
|
|
|
|
|
|
this.channelTree.client.serverConnection.sendCommand("musicbotplayerinfo", {botid: this.properties.client_database_id });
|
|
|
|
return this._info_promise;
|
|
|
|
}
|
2018-02-27 17:20:49 +01:00
|
|
|
}
|