///
namespace connection {
import Conversation = chat.channel.Conversation;
export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss {
constructor(connection: AbstractServerConnection) {
super(connection);
}
}
export class ConnectionCommandHandler extends AbstractCommandHandler {
readonly connection: AbstractServerConnection;
readonly connection_handler: ConnectionHandler;
constructor(connection: AbstractServerConnection) {
super(connection);
this.connection_handler = connection.client;
this["error"] = this.handleCommandResult;
this["channellist"] = this.handleCommandChannelList;
this["channellistfinished"] = this.handleCommandChannelListFinished;
this["notifychannelcreated"] = this.handleCommandChannelCreate;
this["notifychanneldeleted"] = this.handleCommandChannelDelete;
this["notifychannelhide"] = this.handleCommandChannelHide;
this["notifychannelshow"] = this.handleCommandChannelShow;
this["notifyserverconnectioninfo"] = this.handleNotifyServerConnectionInfo;
this["notifyconnectioninfo"] = this.handleNotifyConnectionInfo;
this["notifycliententerview"] = this.handleCommandClientEnterView;
this["notifyclientleftview"] = this.handleCommandClientLeftView;
this["notifyclientmoved"] = this.handleNotifyClientMoved;
this["initserver"] = this.handleCommandServerInit;
this["notifychannelmoved"] = this.handleNotifyChannelMoved;
this["notifychanneledited"] = this.handleNotifyChannelEdited;
this["notifytextmessage"] = this.handleNotifyTextMessage;
this["notifyclientchatcomposing"] = this.notifyClientChatComposing;
this["notifyclientchatclosed"] = this.handleNotifyClientChatClosed;
this["notifyclientupdated"] = this.handleNotifyClientUpdated;
this["notifyserveredited"] = this.handleNotifyServerEdited;
this["notifyserverupdated"] = this.handleNotifyServerUpdated;
this["notifyclientpoke"] = this.handleNotifyClientPoke;
this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo;
this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd;
this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove;
this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged;
this["notifychannelsubscribed"] = this.handleNotifyChannelSubscribed;
this["notifychannelunsubscribed"] = this.handleNotifyChannelUnsubscribed;
this["notifyconversationhistory"] = this.handleNotifyConversationHistory;
this["notifyconversationmessagedelete"] = this.handleNotifyConversationMessageDelete;
}
private loggable_invoker(unique_id, client_id, name) : log.server.base.Client | undefined {
const id = parseInt(client_id);
if(typeof(client_id) === "undefined" || Number.isNaN(id))
return undefined;
if(id == 0)
return {
client_id: 0,
client_unique_id: this.connection_handler.channelTree.server.properties.virtualserver_unique_identifier,
client_name: this.connection_handler.channelTree.server.properties.virtualserver_name,
};
return {
client_unique_id: unique_id,
client_name: name,
client_id: client_id
};
}
proxy_command_promise(promise: Promise, options: connection.CommandOptions) {
if(!options.process_result)
return promise;
return promise.catch(ex => {
if(options.process_result) {
if(ex instanceof CommandResult) {
let res = ex;
if(!res.success) {
if(res.id == ErrorID.PERMISSION_ERROR) { //Permission error
const permission = this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number);
res.message = tr("Insufficient client permissions. Failed on permission ") + (permission ? permission.name : "unknown");
this.connection_handler.log.log(log.server.Type.ERROR_PERMISSION, {
permission: this.connection_handler.permissions.resolveInfo(res.json["failed_permid"] as number)
});
this.connection_handler.sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS);
} else if(res.id != ErrorID.EMPTY_RESULT) {
this.connection_handler.log.log(log.server.Type.ERROR_CUSTOM, {
message: res.extra_message.length == 0 ? res.message : res.extra_message
});
}
}
} else if(typeof(ex) === "string") {
this.connection_handler.log.log(log.server.Type.CONNECTION_COMMAND_ERROR, {error: ex});
} else {
log.error(LogCategory.NETWORKING, tr("Invalid promise result type: %s. Result: %o"), typeof (ex), ex);
}
}
return Promise.reject(ex);
});
}
handle_command(command: ServerCommand) : boolean {
if(this[command.command]) {
this[command.command](command.arguments);
return true;
}
return false;
}
set_handler(command: string, handler: any) {
this[command] = handler;
}
unset_handler(command: string, handler?: any) {
if(handler && this[command] != handler) return;
this[command] = undefined;
}
handleCommandResult(json) {
json = json[0]; //Only one bulk
let code : string = json["return_code"];
if(!code || code.length == 0) {
log.warn(LogCategory.NETWORKING, tr("Invalid return code! (%o)"), json);
return;
}
let retListeners = this.connection["_retListener"];
for(let e of retListeners) {
if(e.code != code) continue;
retListeners.remove(e);
let result = new CommandResult(json);
if(result.success)
e.resolve(result);
else
e.reject(result);
break;
}
}
handleCommandServerInit(json){
//We could setup the voice channel
if(this.connection.support_voice()) {
log.debug(LogCategory.NETWORKING, tr("Setting up voice"));
} else {
log.debug(LogCategory.NETWORKING, tr("Skipping voice setup (No voice bridge available)"));
}
json = json[0]; //Only one bulk
this.connection_handler.channelTree.registerClient(this.connection_handler.getClient());
this.connection.client.side_bar.channel_conversations().reset();
this.connection.client.clientId = parseInt(json["aclid"]);
this.connection.client.getClient().updateVariables( {key: "client_nickname", value: json["acn"]});
let updates: {
key: string,
value: string
}[] = [];
for(let key in json) {
if(key === "aclid") continue;
if(key === "acn") continue;
updates.push({key: key, value: json[key]});
}
this.connection.client.channelTree.server.updateVariables(false, ...updates);
const properties = this.connection.client.channelTree.server.properties;
/* host message */
if(properties.virtualserver_hostmessage_mode > 0) {
if(properties.virtualserver_hostmessage_mode == 1) {
/* show in log */
this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE, {
message: properties.virtualserver_hostmessage
});
} else {
/* create modal/create modal and quit */
createModal({
header: tr("Host message"),
body: MessageHelper.bbcode_chat(properties.virtualserver_hostmessage),
footer: undefined
}).open();
if(properties.virtualserver_hostmessage_mode == 3) {
/* first let the client initialize his stuff */
setTimeout(() => {
this.connection_handler.log.log(log.server.Type.SERVER_HOST_MESSAGE_DISCONNECT, {
message: properties.virtualserver_welcomemessage
});
this.connection.disconnect("host message disconnect");
this.connection_handler.handleDisconnect(DisconnectReason.SERVER_HOSTMESSAGE);
this.connection_handler.sound.play(Sound.CONNECTION_DISCONNECTED);
}, 100);
}
}
}
/* welcome message */
if(properties.virtualserver_welcomemessage) {
this.connection_handler.log.log(log.server.Type.SERVER_WELCOME_MESSAGE, {
message: properties.virtualserver_welcomemessage
});
}
/* priviledge key */
if(properties.virtualserver_ask_for_privilegekey) {
createInputModal(tr("Use a privilege key"), tr("This is a newly created server for which administrator privileges have not yet been claimed.
Please enter the \"privilege key\" that was automatically generated when this server was created to gain administrator permissions."), message => message.length > 0, result => {
if(!result) return;
const scon = server_connections.active_connection_handler();
if(scon.serverConnection.connected)
scon.serverConnection.send_command("tokenuse", {
token: result
}).then(() => {
createInfoModal(tr("Use privilege key"), tr("Privilege key successfully used!")).open();
}).catch(error => {
createErrorModal(tr("Use privilege key"), MessageHelper.formatMessage(tr("Failed to use privilege key: {}"), error instanceof CommandResult ? error.message : error)).open();
});
}, { field_placeholder: 'Enter Privilege Key' }).open();
}
this.connection_handler.log.log(log.server.Type.CONNECTION_CONNECTED, {
own_client: this.connection_handler.getClient().log_data()
});
this.connection_handler.sound.play(Sound.CONNECTION_CONNECTED);
this.connection.client.onConnected();
}
handleNotifyServerConnectionInfo(json) {
json = json[0];
/* everything is a number, so lets parse it */
for(const key of Object.keys(json))
json[key] = parseFloat(json[key]);
this.connection_handler.channelTree.server.set_connection_info(json);
}
handleNotifyConnectionInfo(json) {
json = json[0];
const object = new ClientConnectionInfo();
/* everything is a number (except ip), so lets parse it */
for(const key of Object.keys(json)) {
if(key === "connection_client_ip")
object[key] = json[key];
else
object[key] = parseFloat(json[key]);
}
const client = this.connection_handler.channelTree.findClient(parseInt(json["clid"]));
if(!client) {
log.warn(LogCategory.NETWORKING, tr("Received client connection info for unknown client (%o)"), json["clid"]);
return;
}
client.set_connection_info(object);
}
private createChannelFromJson(json, ignoreOrder: boolean = false) {
let tree = this.connection.client.channelTree;
let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"]));
tree.insertChannel(channel);
if(json["channel_order"] !== "0") {
let prev = tree.findChannel(json["channel_order"]);
if(!prev && json["channel_order"] != 0) {
if(!ignoreOrder) {
log.error(LogCategory.NETWORKING, tr("Invalid channel order id!"));
return;
}
}
let parent = tree.findChannel(json["cpid"]);
if(!parent && json["cpid"] != 0) {
log.error(LogCategory.NETWORKING, tr("Invalid channel parent"));
return;
}
tree.moveChannel(channel, prev, parent); //TODO test if channel exists!
}
if(ignoreOrder) {
for(let ch of tree.channels) {
if(ch.properties.channel_order == channel.channelId) {
tree.moveChannel(ch, channel, channel.parent); //Corrent the order :)
}
}
}
let updates: {
key: string,
value: string
}[] = [];
for(let key in json) {
if(key === "cid") continue;
if(key === "cpid") continue;
if(key === "invokerid") continue;
if(key === "invokername") continue;
if(key === "invokeruid") continue;
if(key === "reasonid") continue;
updates.push({key: key, value: json[key]});
}
channel.updateVariables(...updates);
}
handleCommandChannelList(json) {
this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */
log.debug(LogCategory.NETWORKING, tr("Got %d new channels"), json.length);
for(let index = 0; index < json.length; index++)
this.createChannelFromJson(json[index], true);
}
handleCommandChannelListFinished(json) {
this.connection.client.channelTree.show_channel_tree();
}
handleCommandChannelCreate(json) {
this.createChannelFromJson(json[0]);
}
handleCommandChannelShow(json) {
this.createChannelFromJson(json[0]); //TODO may chat?
}
handleCommandChannelDelete(json) {
let tree = this.connection.client.channelTree;
const conversations = this.connection.client.side_bar.channel_conversations();
log.info(LogCategory.NETWORKING, tr("Got %d channel deletions"), json.length);
for(let index = 0; index < json.length; index++) {
conversations.delete_conversation(parseInt(json[index]["cid"]));
let channel = tree.findChannel(json[index]["cid"]);
if(!channel) {
log.error(LogCategory.NETWORKING, tr("Invalid channel onDelete (Unknown channel)"));
continue;
}
tree.deleteChannel(channel);
}
}
handleCommandChannelHide(json) {
let tree = this.connection.client.channelTree;
const conversations = this.connection.client.side_bar.channel_conversations();
log.info(LogCategory.NETWORKING, tr("Got %d channel hides"), json.length);
for(let index = 0; index < json.length; index++) {
conversations.delete_conversation(parseInt(json[index]["cid"]));
let channel = tree.findChannel(json[index]["cid"]);
if(!channel) {
log.error(LogCategory.NETWORKING, tr("Invalid channel on hide (Unknown channel)"));
continue;
}
tree.deleteChannel(channel);
}
}
handleCommandClientEnterView(json) {
let tree = this.connection.client.channelTree;
let client: ClientEntry;
let channel = undefined;
let old_channel = undefined;
let reason_id, reason_msg;
let invokerid, invokername, invokeruid;
for(const entry of json) {
/* attempt to update properties if given */
channel = typeof(entry["ctid"]) !== "undefined" ? tree.findChannel(parseInt(entry["ctid"])) : channel;
old_channel = typeof(entry["cfid"]) !== "undefined" ? tree.findChannel(parseInt(entry["cfid"])) : old_channel;
reason_id = typeof(entry["reasonid"]) !== "undefined" ? entry["reasonid"] : reason_id;
reason_msg = typeof(entry["reason_msg"]) !== "undefined" ? entry["reason_msg"] : reason_msg;
invokerid = typeof(entry["invokerid"]) !== "undefined" ? parseInt(entry["invokerid"]) : invokerid;
invokername = typeof(entry["invokername"]) !== "undefined" ? entry["invokername"] : invokername;
invokeruid = typeof(entry["invokeruid"]) !== "undefined" ? entry["invokeruid"] : invokeruid;
client = tree.findClient(parseInt(entry["clid"]));
if(!client) {
if(parseInt(entry["client_type_exact"]) == ClientType.CLIENT_MUSIC) {
client = new MusicClientEntry(parseInt(entry["clid"]), entry["client_nickname"]);
} else {
client = new ClientEntry(parseInt(entry["clid"]), entry["client_nickname"]);
}
client.properties.client_type = parseInt(entry["client_type"]);
client = tree.insertClient(client, channel);
} else {
tree.moveClient(client, channel);
}
if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) {
const own_channel = this.connection.client.getClient().currentChannel();
this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_ENTER, {
channel_from: old_channel ? old_channel.log_data() : undefined,
channel_to: channel ? channel.log_data() : undefined,
client: client.log_data(),
invoker: this.loggable_invoker(invokeruid, invokerid, invokername),
message:reason_msg,
reason: parseInt(reason_id),
own_channel: channel == own_channel
});
if(reason_id == ViewReasonId.VREASON_USER_ACTION) {
if(own_channel == channel)
if(old_channel)
this.connection_handler.sound.play(Sound.USER_ENTERED);
else
this.connection_handler.sound.play(Sound.USER_ENTERED_CONNECT);
} else if(reason_id == ViewReasonId.VREASON_MOVED) {
if(own_channel == channel)
this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED);
} else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) {
if(own_channel == channel)
this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED);
} else if(reason_id == ViewReasonId.VREASON_SYSTEM) {
} else {
console.warn(tr("Unknown reasonid for %o"), reason_id);
}
}
let updates: {
key: string,
value: string
}[] = [];
for(let key in entry) {
if(key == "cfid") continue;
if(key == "ctid") continue;
if(key === "invokerid") continue;
if(key === "invokername") continue;
if(key === "invokeruid") continue;
if(key === "reasonid") continue;
updates.push({key: key, value: entry[key]});
}
client.updateVariables(...updates);
/* if its a new client join, or a system reason (like we joined) */
if(!old_channel || reason_id == 2) {
/* client new join */
const conversation_manager = this.connection_handler.side_bar.private_conversations();
const conversation = conversation_manager.find_conversation({
unique_id: client.properties.client_unique_identifier,
client_id: client.clientId(),
name: client.clientNickName()
}, {
create: false,
attach: true
});
if(conversation)
client.flag_text_unread = conversation.is_unread();
}
if(client instanceof LocalClientEntry) {
client.initializeListener();
this.connection_handler.update_voice_status();
this.connection_handler.side_bar.info_frame().update_channel_talk();
const conversations = this.connection.client.side_bar.channel_conversations();
conversations.set_current_channel(client.currentChannel().channelId);
}
}
}
handleCommandClientLeftView(json) {
let reason_id = -1;
for(const entry of json) {
reason_id = entry["reasonid"] || reason_id;
let tree = this.connection.client.channelTree;
let client = tree.findClient(entry["clid"]);
if(!client) {
log.error(LogCategory.NETWORKING, tr("Unknown client left!"));
return 0;
}
if(client == this.connection.client.getClient()) {
if(reason_id == ViewReasonId.VREASON_BAN) {
this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, entry);
} else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) {
this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, entry);
} else if(reason_id == ViewReasonId.VREASON_SERVER_SHUTDOWN) {
this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry);
} else if(reason_id == ViewReasonId.VREASON_SERVER_STOPPED) {
this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, entry);
} else {
this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, entry);
}
this.connection_handler.side_bar.info_frame().update_channel_talk();
return;
}
if(this.connection_handler.client_status.queries_visible || client.properties.client_type != ClientType.CLIENT_QUERY) {
const own_channel = this.connection.client.getClient().currentChannel();
let channel_from = tree.findChannel(entry["cfid"]);
let channel_to = tree.findChannel(entry["ctid"]);
const is_own_channel = channel_from == own_channel;
this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_LEAVE, {
channel_from: channel_from ? channel_from.log_data() : undefined,
channel_to: channel_to ? channel_to.log_data() : undefined,
client: client.log_data(),
invoker: this.loggable_invoker(entry["invokeruid"], entry["invokerid"], entry["invokername"]),
message: entry["reasonmsg"],
reason: parseInt(entry["reasonid"]),
ban_time: parseInt(entry["bantime"]),
own_channel: is_own_channel
});
if(is_own_channel) {
if(reason_id == ViewReasonId.VREASON_USER_ACTION) {
this.connection_handler.sound.play(Sound.USER_LEFT);
} else if(reason_id == ViewReasonId.VREASON_SERVER_LEFT) {
this.connection_handler.sound.play(Sound.USER_LEFT_DISCONNECT);
} else if(reason_id == ViewReasonId.VREASON_SERVER_KICK) {
this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_SERVER);
} else if(reason_id == ViewReasonId.VREASON_CHANNEL_KICK) {
this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL);
} else if(reason_id == ViewReasonId.VREASON_BAN) {
this.connection_handler.sound.play(Sound.USER_LEFT_BANNED);
} else if(reason_id == ViewReasonId.VREASON_TIMEOUT) {
this.connection_handler.sound.play(Sound.USER_LEFT_TIMEOUT);
} else if(reason_id == ViewReasonId.VREASON_MOVED) {
this.connection_handler.sound.play(Sound.USER_LEFT_MOVED);
} else {
log.error(LogCategory.NETWORKING, tr("Unknown client left reason %d!"), reason_id);
}
}
if(!channel_to) {
/* client left the server */
const conversation_manager = this.connection_handler.side_bar.private_conversations();
const conversation = conversation_manager.find_conversation({
unique_id: client.properties.client_unique_identifier,
client_id: client.clientId(),
name: client.clientNickName()
}, {
create: false,
attach: false
});
if(conversation) {
conversation.set_state(chat.PrivateConversationState.DISCONNECTED);
}
}
}
tree.deleteClient(client);
}
}
handleNotifyClientMoved(json) {
json = json[0]; //Only one bulk
let tree = this.connection.client.channelTree;
let client = tree.findClient(json["clid"]);
let self = client instanceof LocalClientEntry;
let channel_to = tree.findChannel(parseInt(json["ctid"]));
let channel_from = tree.findChannel(parseInt(json["cfid"]));
if(!client) {
log.error(LogCategory.NETWORKING, tr("Unknown client move (Client)!"));
return 0;
}
if(!channel_to) {
log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel to)!"));
return 0;
}
if(!self) {
if(!channel_from) {
log.error(LogCategory.NETWORKING, tr("Unknown client move (Channel from)!"));
channel_from = client.currentChannel();
} else if(channel_to !== client.currentChannel()) {
log.error(LogCategory.NETWORKING,
tr("Client move from invalid source channel! Local client registered in channel %d but server send %d."),
client.currentChannel().channelId, channel_from.channelId
);
}
}
let current_clients: ClientEntry[];
if(self) {
channel_from = client.currentChannel();
current_clients = client.channelTree.clientsByChannel(client.currentChannel());
this.connection_handler.update_voice_status(channel_to);
}
tree.moveClient(client, channel_to);
for(const entry of current_clients || [])
if(entry !== client && entry.get_audio_handle())
entry.get_audio_handle().abort_replay();
if(self) {
const side_bar = this.connection_handler.side_bar;
side_bar.info_frame().update_channel_talk();
const conversation_to = side_bar.channel_conversations().conversation(channel_to.channelId, false);
if(conversation_to)
conversation_to.update_private_state();
if(channel_from) {
const conversation_from = side_bar.channel_conversations().conversation(channel_from.channelId, false);
if(conversation_from)
conversation_from.update_private_state();
}
side_bar.channel_conversations().update_chat_box();
}
const own_channel = this.connection.client.getClient().currentChannel();
this.connection_handler.log.log(log.server.Type.CLIENT_VIEW_MOVE, {
channel_from: channel_from ? {
channel_id: channel_from.channelId,
channel_name: channel_from.channelName()
} : undefined,
channel_from_own: channel_from == own_channel,
channel_to: channel_to ? {
channel_id: channel_to.channelId,
channel_name: channel_to.channelName()
} : undefined,
channel_to_own: channel_to == own_channel,
client: {
client_id: client.clientId(),
client_name: client.clientNickName(),
client_unique_id: client.properties.client_unique_identifier
},
client_own: self,
invoker: this.loggable_invoker(json["invokeruid"], json["invokerid"], json["invokername"]),
message: json["reasonmsg"],
reason: parseInt(json["reasonid"]),
});
if(json["reasonid"] == ViewReasonId.VREASON_MOVED) {
if(self)
this.connection_handler.sound.play(Sound.USER_MOVED_SELF);
else if(own_channel == channel_to)
this.connection_handler.sound.play(Sound.USER_ENTERED_MOVED);
else if(own_channel == channel_from)
this.connection_handler.sound.play(Sound.USER_LEFT_MOVED);
} else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) {
if(self) {} //If we do an action we wait for the error response
else if(own_channel == channel_to)
this.connection_handler.sound.play(Sound.USER_ENTERED);
else if(own_channel == channel_from)
this.connection_handler.sound.play(Sound.USER_LEFT);
} else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) {
if(self) {
this.connection_handler.sound.play(Sound.CHANNEL_KICKED);
} else if(own_channel == channel_to)
this.connection_handler.sound.play(Sound.USER_ENTERED_KICKED);
else if(own_channel == channel_from)
this.connection_handler.sound.play(Sound.USER_LEFT_KICKED_CHANNEL);
} else {
console.warn(tr("Unknown reason id %o"), json["reasonid"]);
}
}
handleNotifyChannelMoved(json) {
json = json[0]; //Only one bulk
let tree = this.connection.client.channelTree;
let channel = tree.findChannel(json["cid"]);
if(!channel) {
log.error(LogCategory.NETWORKING, tr("Unknown channel move (Channel)!"));
return 0;
}
let prev = tree.findChannel(json["order"]);
if(!prev && json["order"] != 0) {
log.error(LogCategory.NETWORKING, tr("Unknown channel move (prev)!"));
return 0;
}
let parent = tree.findChannel(json["cpid"]);
if(!parent && json["cpid"] != 0) {
log.error(LogCategory.NETWORKING, tr("Unknown channel move (parent)!"));
return 0;
}
tree.moveChannel(channel, prev, parent);
}
handleNotifyChannelEdited(json) {
json = json[0]; //Only one bulk
let tree = this.connection.client.channelTree;
let channel = tree.findChannel(json["cid"]);
if(!channel) {
log.error(LogCategory.NETWORKING, tr("Unknown channel edit (Channel)!"));
return 0;
}
let updates: {
key: string,
value: string
}[] = [];
for(let key in json) {
if(key === "cid") continue;
if(key === "invokerid") continue;
if(key === "invokername") continue;
if(key === "invokeruid") continue;
if(key === "reasonid") continue;
updates.push({key: key, value: json[key]});
}
channel.updateVariables(...updates);
if(this.connection_handler.getClient().currentChannel() === channel) {
//TODO: Playback sound that your channel has been edited
this.connection_handler.update_voice_status();
}
}
handleNotifyTextMessage(json) {
json = json[0]; //Only one bulk
let mode = json["targetmode"];
if(mode == 1){
//json["invokerid"], json["invokername"], json["invokeruid"]
const target_client_id = parseInt(json["target"]);
const target_own = target_client_id === this.connection.client.getClientId();
if(target_own && target_client_id === json["invokerid"]) {
log.error(LogCategory.NETWORKING, tr("Received conversation message from invalid client id. Data: %o", json));
return;
}
const conversation_manager = this.connection_handler.side_bar.private_conversations();
const conversation = conversation_manager.find_conversation({
client_id: target_own ? parseInt(json["invokerid"]) : target_client_id,
unique_id: target_own ? json["invokeruid"] : undefined,
name: target_own ? json["invokername"] : undefined
}, {
create: target_own,
attach: target_own
});
if(!conversation) {
log.error(LogCategory.NETWORKING, tr("Received conversation message for unknown conversation! (%s)"), target_own ? tr("Remote message") : tr("Own message"));
return;
}
conversation.append_message(json["msg"], {
type: target_own ? "partner" : "self",
name: json["invokername"],
unique_id: json["invokeruid"],
client_id: parseInt(json["invokerid"])
});
if(target_own) {
this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
const client = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"]));
if(client) /* the client itself might be invisible */
client.flag_text_unread = conversation.is_unread();
} else {
this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
}
} else if(mode == 2) {
const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"]));
const own_channel_id = this.connection.client.getClient().currentChannel().channelId;
const channel_id = typeof(json["cid"]) !== "undefined" ? parseInt(json["cid"]) : own_channel_id;
const channel = this.connection_handler.channelTree.findChannel(channel_id);
if(json["invokerid"] == this.connection.client.clientId)
this.connection_handler.sound.play(Sound.MESSAGE_SEND, {default_volume: .5});
else if(channel_id == own_channel_id) {
this.connection_handler.sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5});
}
const conversations = this.connection_handler.side_bar.channel_conversations();
const conversation = conversations.conversation(channel_id);
conversation.register_new_message({
sender_database_id: invoker ? invoker.properties.client_database_id : 0,
sender_name: json["invokername"],
sender_unique_id: json["invokeruid"],
timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]),
message: json["msg"]
});
if(conversation.is_unread())
channel.flag_text_unread = true;
} else if(mode == 3) {
this.connection_handler.log.log(log.server.Type.GLOBAL_MESSAGE, {
message: json["msg"],
sender: {
client_unique_id: json["invokeruid"],
client_name: json["invokername"],
client_id: parseInt(json["invokerid"])
}
});
const invoker = this.connection_handler.channelTree.findClient(parseInt(json["invokerid"]));
const conversations = this.connection_handler.side_bar.channel_conversations();
const conversation = conversations.conversation(0);
conversation.register_new_message({
sender_database_id: invoker ? invoker.properties.client_database_id : 0,
sender_name: json["invokername"],
sender_unique_id: json["invokeruid"],
timestamp: typeof(json["timestamp"]) === "undefined" ? Date.now() : parseInt(json["timestamp"]),
message: json["msg"]
});
this.connection_handler.channelTree.server.flag_text_unread = conversation.is_unread();
}
}
notifyClientChatComposing(json) {
json = json[0];
const conversation_manager = this.connection_handler.side_bar.private_conversations();
const conversation = conversation_manager.find_conversation({
client_id: parseInt(json["clid"]),
unique_id: json["cluid"],
name: undefined
}, {
create: false,
attach: false
});
if(!conversation)
return;
conversation.trigger_typing();
}
handleNotifyClientChatClosed(json) {
json = json[0]; //Only one bulk
//Chat partner has closed the conversation
//clid: "6"
//cluid: "YoWmG+dRGKD+Rxb7SPLAM5+B9tY="
const conversation_manager = this.connection_handler.side_bar.private_conversations();
const conversation = conversation_manager.find_conversation({
client_id: parseInt(json["clid"]),
unique_id: json["cluid"],
name: undefined
}, {
create: false,
attach: false
});
if(!conversation) {
log.warn(LogCategory.GENERAL, tr("Received chat close for client, but we haven't a chat open."));
return;
}
conversation.set_state(chat.PrivateConversationState.CLOSED);
}
handleNotifyClientUpdated(json) {
json = json[0]; //Only one bulk
let client = this.connection.client.channelTree.findClient(json["clid"]);
if(!client) {
log.error(LogCategory.NETWORKING, tr("Tried to update an non existing client"));
return;
}
let updates: {
key: string,
value: string
}[] = [];
for(let key in json) {
if(key == "clid") continue;
updates.push({key: key, value: json[key]});
}
client.updateVariables(...updates);
}
handleNotifyServerEdited(json) {
json = json[0];
let updates: {
key: string,
value: string
}[] = [];
for(let key in json) {
if(key === "invokerid") continue;
if(key === "invokername") continue;
if(key === "invokeruid") continue;
if(key === "reasonid") continue;
updates.push({key: key, value: json[key]});
}
this.connection.client.channelTree.server.updateVariables(false, ...updates);
}
handleNotifyServerUpdated(json) {
json = json[0];
let updates: {
key: string,
value: string
}[] = [];
for(let key in json) {
if(key === "invokerid") continue;
if(key === "invokername") continue;
if(key === "invokeruid") continue;
if(key === "reasonid") continue;
updates.push({key: key, value: json[key]});
}
this.connection.client.channelTree.server.updateVariables(true, ...updates);
}
handleNotifyMusicPlayerInfo(json) {
json = json[0];
let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]);
if(!bot || !(bot instanceof MusicClientEntry)) {
log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot);
return;
}
bot.handlePlayerInfo(json);
}
handleNotifyClientPoke(json) {
json = json[0];
Modals.spawnPoke(this.connection_handler, {
id: parseInt(json["invokerid"]),
name: json["invokername"],
unique_id: json["invokeruid"]
}, json["msg"]);
this.connection_handler.sound.play(Sound.USER_POKED_SELF);
}
//TODO server chat message
handleNotifyServerGroupClientAdd(json) {
json = json[0];
const self = this.connection.client.getClient();
if(json["clid"] == self.clientId())
this.connection_handler.sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF);
}
//TODO server chat message
handleNotifyServerGroupClientRemove(json) {
json = json[0];
const self = this.connection.client.getClient();
if(json["clid"] == self.clientId()) {
this.connection_handler.sound.play(Sound.GROUP_SERVER_REVOKED_SELF);
} else {
}
}
//TODO server chat message
handleNotifyClientChannelGroupChanged(json) {
json = json[0];
const self = this.connection.client.getClient();
if(json["clid"] == self.clientId()) {
this.connection_handler.sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF);
}
}
handleNotifyChannelSubscribed(json) {
for(const entry of json) {
const channel = this.connection.client.channelTree.findChannel(entry["cid"]);
if(!channel) {
console.warn(tr("Received channel subscribed for not visible channel (cid: %d)"), entry['cid']);
continue;
}
channel.flag_subscribed = true;
}
}
handleNotifyChannelUnsubscribed(json) {
for(const entry of json) {
const channel = this.connection.client.channelTree.findChannel(entry["cid"]);
if(!channel) {
console.warn(tr("Received channel unsubscribed for not visible channel (cid: %d)"), entry['cid']);
continue;
}
channel.flag_subscribed = false;
for(const client of channel.clients(false))
this.connection.client.channelTree.deleteClient(client);
}
}
handleNotifyConversationHistory(json: any[]) {
const conversations = this.connection.client.side_bar.channel_conversations();
const conversation = conversations.conversation(parseInt(json[0]["cid"]));
if(!conversation) {
log.warn(LogCategory.NETWORKING, tr("Received conversation history for invalid or unknown conversation (%o)"), json[0]["cid"]);
return;
}
for(const entry of json) {
conversation.register_new_message({
message: entry["msg"],
sender_unique_id: entry["sender_unique_id"],
sender_name: entry["sender_name"],
timestamp: parseInt(entry["timestamp"]),
sender_database_id: parseInt(entry["sender_database_id"])
}, false);
}
/* now update the boxes */
/* No update needed because the command which triggers this notify should update the chat box on success
conversation.fix_scroll(true);
conversation.handle.update_chat_box();
*/
}
handleNotifyConversationMessageDelete(json: any[]) {
let conversation: Conversation;
const conversations = this.connection.client.side_bar.channel_conversations();
for(const entry of json) {
if(typeof(entry["cid"]) !== "undefined")
conversation = conversations.conversation(parseInt(entry["cid"]), false);
if(!conversation)
continue;
conversation.delete_messages(parseInt(entry["timestamp_begin"]), parseInt(entry["timestamp_end"]), parseInt(entry["cldbid"]), parseInt(entry["limit"]));
}
}
}
}