From c289e15352e6b295adaca287b18b07e80849017a Mon Sep 17 00:00:00 2001 From: tonyco97 Date: Mon, 16 Dec 2024 09:43:49 +0100 Subject: [PATCH 1/2] feat: added missing cancel event for physical phones --- .../plugins_rest/astproxy.js | 92 ++++++++++++++++++- .../plugins/config_manager/config_manager.js | 33 +++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/root/usr/lib/node/nethcti-server/plugins/com_astproxy_rest/plugins_rest/astproxy.js b/root/usr/lib/node/nethcti-server/plugins/com_astproxy_rest/plugins_rest/astproxy.js index e2211d8e..8db4a07a 100644 --- a/root/usr/lib/node/nethcti-server/plugins/com_astproxy_rest/plugins_rest/astproxy.js +++ b/root/usr/lib/node/nethcti-server/plugins/com_astproxy_rest/plugins_rest/astproxy.js @@ -718,6 +718,7 @@ var compConfigManager; * 1. [`astproxy/dtmf`](#dtmfpost) * 1. [`astproxy/wakeup`](#wakeuppost) * 1. [`astproxy/intrude`](#intrudepost) + * 1. [`astproxy/cancel`](#cancelpost) * 1. [`astproxy/mute_record`](#mute_recordpost) * 1. [`astproxy/start_record`](#start_recordpost) * 1. [`astproxy/blindtransfer`](#blindtransferpost) @@ -1414,6 +1415,7 @@ var compConfigManager; * @param {string} start_conf Starts a meetme conference * @param {string} toggle_hold Hold/Unhold a conversation of the user. It works only with supported physical phones * @param {string} toggle_mute Mute/Unmute a conversation of the user. It works only with supported physical phones + * @param {string} cancel Cancel the call, before the call is answered * @param {string} join_myconf Joins the extension owner to his meetme conference * @param {string} pickup_conv Pickup a conversation * @param {string} stop_record Stop the recording of a conversation @@ -1465,6 +1467,7 @@ var compConfigManager; 'start_conf', 'toggle_hold', 'toggle_mute', + 'cancel', 'pickup_conv', 'stop_record', 'join_myconf', @@ -2728,6 +2731,58 @@ var compConfigManager; } }, + /** + * Cancel a call registered with a supported + * physical phone with the following REST API: + * + * POST cancel + * + * @method call + * @param {object} req The client request + * @param {object} res The client response + * @param {function} next Function to run the next handler in the chain + */ + cancel: function (req, res, next) { + try { + var username = req.headers.authorization_user; + var result = compUser.getUserInfoJSON(username); + const nethlinkExtensions = result.endpoints[compUser.ENDPOINT_TYPES.extension].filter((endpoint) => endpoint.type === 'nethlink'); + const nethlinkExtension = nethlinkExtensions.length > 0 ? nethlinkExtensions[0].id : null; + var nethlinkStatus = compAstProxy.getExtenStatus(nethlinkExtension); + + // check parameters + if (typeof req.params !== 'object' || typeof req.params.endpointId !== 'string') { + compUtil.net.sendHttp400(IDLOG, res); + return; + } + + // check if the extension of the request is owned by the user: the user + // can only cancel a conversation that belong to him + if (compAuthorization.verifyUserEndpointExten(username, req.params.endpointId) === false) { + + logger.log.warn(IDLOG, 'cancel call from "' + username + '" has been failed: the extension "' + + req.params.endpointId + '" is not owned by him'); + compUtil.net.sendHttp403(IDLOG, res); + return; + } + + var extenAgent = compAstProxy.getExtensionAgent(req.params.endpointId); + var isSupported = compConfigManager.phoneSupportHttpApi(extenAgent); + + if (!isSupported) { + var str = 'cancel conversation with unsupported phone (exten: ' + req.params.endpointId + '/' + extenAgent + ')'; + logger.log.warn(IDLOG, str); + compUtil.net.sendHttp500(IDLOG, res, str); + } else if (isSupported && compAstProxy.isAutoC2CEnabled && nethlinkStatus === 'online') { + sendCancelConversation(username, req, res); + compUtil.net.sendHttp200(IDLOG, res); + } + } catch (err) { + logger.log.error(IDLOG, err.stack); + compUtil.net.sendHttp500(IDLOG, res, err.toString()); + } + }, + /** * Makes a new call to the destination number from any extension with the following REST API: * @@ -5482,7 +5537,8 @@ var compConfigManager; exports.setCompUtil = setCompUtil; exports.join_myconf = astproxy.join_myconf; exports.toggle_hold = astproxy.toggle_hold; - exports.toggle_mute = astproxy.toggle_mute + exports.toggle_mute = astproxy.toggle_mute; + exports.cancel = astproxy.cancel; exports.pickup_conv = astproxy.pickup_conv; exports.stop_record = astproxy.stop_record; exports.setCompUser = setCompUser; @@ -6240,6 +6296,40 @@ function sendPhoneMuteToTcp(username, req, res) { } } +/** + * Send the request to cancel a connected tcp client. The tcp client will do the request + * to the final physical supported phone. + * + * @method sendCancelConversation + * @param {string} username The username + * @param {object} req The client request + * @param {object} res The client response + */ +function sendCancelConversation(username, req, res) { + try { + if (typeof username !== 'string' || typeof req !== 'object' || typeof res !== 'object') { + throw new Error('wrong parameters: ' + JSON.stringify(arguments)); + } + const exten = req.params.endpointId; + const extenIp = compAstProxy.getExtensionIp(exten); + const extenAgent = compAstProxy.getExtensionAgent(exten); + let url = compConfigManager.getCancelUrlFromAgent(extenAgent); + if (typeof url === 'string' && url !== '') { + const phoneUser = compUser.getPhoneWebUser(username, exten); + const phonePass = compUser.getPhoneWebPass(username, exten); + url = url.replace(/\$PHONE_IP/g, extenIp); + url = url.replace(/\$PHONE_USER/g, phoneUser); + url = url.replace(/\$PHONE_PASS/g, phonePass); + let urlType = 'cancel'; + compComNethctiWs.sendRequestToNethLink(username, url, urlType); + } else { + logger.log.warn(IDLOG, `failed answer via TCP request by the user "${username}": extenAgent is not supported`); + } + } catch (error) { + logger.log.error(IDLOG, error.stack); + } +} + /** * Send the request to send DTMF code to a connected tcp client. The tcp client will do the request * to the final physical supported phone. diff --git a/root/usr/lib/node/nethcti-server/plugins/config_manager/config_manager.js b/root/usr/lib/node/nethcti-server/plugins/config_manager/config_manager.js index a52878e4..55ced4a5 100644 --- a/root/usr/lib/node/nethcti-server/plugins/config_manager/config_manager.js +++ b/root/usr/lib/node/nethcti-server/plugins/config_manager/config_manager.js @@ -1053,6 +1053,38 @@ function getMuteUnmuteUrlFromAgent(agent) { } } +/** + * It sequentially test a match of specified agent with the keys of _phoneUrls_ + * object. If the match exists than returns the url phone to cancel the action, + * otherwise it returns an empty string. The keys of _phoneUrls_ are sequentially + * checked, so they must be present from the more restrictive to the least. + * + * @method getCancelUrlFromAgent + * @param {string} agent The phone user agent + * @return {string} The phone url used to cancel the action + */ +function getCancelUrlFromAgent(agent) { + try { + // check parameter + if (typeof agent !== 'string') { + throw new TypeError('wrong parameter: ' + agent); + } + + var re; + for (re in phoneUrls) { + // case insensitive 'i' + if (agent.search(new RegExp(re, 'i')) >= 0) { + return phoneUrls[re].urls.cancel; + } + } + return ''; + + } catch (err) { + logger.log.error(IDLOG, err.stack); + return ''; + } +} + /** * Returns true if the specified phone supports dtmf by HTTP api. * It sequentially test a match of specified agent with the keys of _phoneUrls_ @@ -2028,6 +2060,7 @@ exports.phoneSupportHoldHttpApi = phoneSupportHoldHttpApi; exports.getAllUserEndpointsJSON = getAllUserEndpointsJSON; exports.getHoldUnholdUrlFromAgent = getHoldUnholdUrlFromAgent; exports.getMuteUnmuteUrlFromAgent = getMuteUnmuteUrlFromAgent; +exports.getCancelUrlFromAgent = getCancelUrlFromAgent; exports.getPostitNotificationSmsTo = getPostitNotificationSmsTo; exports.setDefaultUserExtensionConf = setDefaultUserExtensionConf; exports.getDefaultUserExtensionConf = getDefaultUserExtensionConf; From 0c27d12f309e8b167e26e7d1538377b4b6768041 Mon Sep 17 00:00:00 2001 From: tonyco97 Date: Mon, 16 Dec 2024 13:04:39 +0100 Subject: [PATCH 2/2] feat: Created new socket event on default_device update --- .../plugins/com_nethcti_ws/com_nethcti_ws.js | 49 +++++++++++++++++++ .../plugins/config_manager/config_manager.js | 1 + 2 files changed, 50 insertions(+) diff --git a/root/usr/lib/node/nethcti-server/plugins/com_nethcti_ws/com_nethcti_ws.js b/root/usr/lib/node/nethcti-server/plugins/com_nethcti_ws/com_nethcti_ws.js index 541fd134..2df86663 100644 --- a/root/usr/lib/node/nethcti-server/plugins/com_nethcti_ws/com_nethcti_ws.js +++ b/root/usr/lib/node/nethcti-server/plugins/com_nethcti_ws/com_nethcti_ws.js @@ -316,6 +316,25 @@ var EVT_CALL_WEBRTC = 'callWebrtc'; */ var EVT_ACTION_NETHLINK = 'actionNethLink'; +/** + * Emitted to a websocket client connection to update user default_device. + * + * Example: + * + "0721405516" + * + * @event updateDefaultDevice + * @param {string} extension The id of updated default_device + * + */ +/** + * The name of the event to update default device + * + * @property EVT_DISPATCH_DEFAULT_DEVICE_CHANGE + * @type string + */ +var EVT_DISPATCH_DEFAULT_DEVICE_CHANGE = 'updateDefaultDevice'; + // /** // * Emitted to a websocket client connection on user endpoint presence update. // * @@ -1756,6 +1775,35 @@ function sendRequestToNethLink(username, url, type) { } } +/** + * Sends an event to the client to update the user default device. + * + * @method sendUpdateDefaultDevice + * @param {string} username The name of the client user + * @param {string} extension The device id information to be updated + */ +function sendUpdateDefaultDevice(username, extension) { + try { + if (typeof username !== 'string' || typeof extension !== 'string') { + throw new Error('wrong parameters: ' + JSON.stringify(arguments)); + } + // emit the EVT_DISPATCH_DEFAULT_DEVICE_CHANGE event for each logged in user + var socketId; + for (socketId in wsid) { + + if (wsid[socketId].username === username) { + logger.log.info(IDLOG, 'emit event "' + EVT_DISPATCH_DEFAULT_DEVICE_CHANGE + '" to default device ' + extension + ' to user "' + username); + + if (wsServer.sockets.sockets.has(socketId)) { + wsServer.sockets.sockets.get(socketId).emit(EVT_DISPATCH_DEFAULT_DEVICE_CHANGE, extension); + } + } + } + } catch (err) { + logger.log.error(IDLOG, err.stack); + } +} + /** * Send an event to all the clients to inform them about all components reloaded. * @@ -2682,6 +2730,7 @@ exports.sendAllCompReloaded = sendAllCompReloaded; exports.sendEventToAllClients = sendEventToAllClients; exports.sendCallWebrtcToClient = sendCallWebrtcToClient; exports.sendRequestToNethLink = sendRequestToNethLink; +exports.sendUpdateDefaultDevice = sendUpdateDefaultDevice; exports.getNumConnectedClients = getNumConnectedClients; exports.EVT_WS_CLIENT_LOGGEDIN = EVT_WS_CLIENT_LOGGEDIN; exports.EVT_WS_CLIENT_CONNECTED = EVT_WS_CLIENT_CONNECTED; diff --git a/root/usr/lib/node/nethcti-server/plugins/config_manager/config_manager.js b/root/usr/lib/node/nethcti-server/plugins/config_manager/config_manager.js index 55ced4a5..4fc3e025 100644 --- a/root/usr/lib/node/nethcti-server/plugins/config_manager/config_manager.js +++ b/root/usr/lib/node/nethcti-server/plugins/config_manager/config_manager.js @@ -1368,6 +1368,7 @@ function setDefaultUserExtensionConf(username, exten, cb) { } else { // update the configuration in mem userSettings[username][USER_CONFIG_KEYS.default_extension] = exten; + compComNethctiWs.sendUpdateDefaultDevice(username, exten); cb(null); } } catch (error) {