api.askoziamonitor.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. <?php
  2. /**
  3. * AskoziaPBX calls recodrings viewer class
  4. */
  5. class AskoziaMonitor {
  6. /**
  7. * Contains system alter config as key=>value
  8. *
  9. * @var array
  10. */
  11. protected $altCfg = array();
  12. /**
  13. * Contains default recorded calls path
  14. *
  15. * @var string
  16. */
  17. protected $voicePath = '/mnt/askozia/';
  18. /**
  19. * Contains voice recors archive path
  20. *
  21. * @var string
  22. */
  23. protected $archivePath = '/mnt/calls_archive/';
  24. /**
  25. * Contains default recorded files file extension
  26. *
  27. * @var string
  28. */
  29. protected $callsFormat = '*.gsm';
  30. /**
  31. * Flag for telepathy detection of users
  32. *
  33. * @var bool
  34. */
  35. protected $onlyMobileFlag = true;
  36. /**
  37. * Contains user assigned tags as login=>usertags
  38. *
  39. * @var array
  40. */
  41. protected $userTags = array();
  42. /**
  43. * FFmpeg installed?
  44. *
  45. * @var bool
  46. */
  47. protected $ffmpegFlag = false;
  48. /**
  49. * installed ffmpeg path
  50. *
  51. * @var string
  52. */
  53. protected $ffmpegPath = '';
  54. /**
  55. * Basic ffmpeg path to search. May be configurable in future.
  56. *
  57. * @var string
  58. */
  59. protected $baseConverterPath = '/usr/local/bin/ffmpeg';
  60. /**
  61. * File path for converted voice files
  62. *
  63. * @var string
  64. */
  65. protected $convertedPath = 'exports/';
  66. /**
  67. * ffmpeg log path
  68. *
  69. * @var string
  70. */
  71. protected $converterLogPath = 'exports/voiceconvert.log';
  72. /**
  73. * Default icons path
  74. */
  75. const ICON_PATH = 'skins/calls/';
  76. /**
  77. * Default module path
  78. */
  79. const URL_ME = '?module=askoziamonitor';
  80. /**
  81. * URL of user profile route
  82. */
  83. const URL_PROFILE = '?module=userprofile&username=';
  84. /**
  85. * Creates new askozia monitor instance
  86. *
  87. * @return void
  88. */
  89. public function __construct() {
  90. $this->loadConfig();
  91. $this->detectFfmpeg();
  92. }
  93. /**
  94. * Loads all required configs and sets some options
  95. *
  96. * @return void
  97. */
  98. protected function loadConfig() {
  99. global $ubillingConfig;
  100. $this->altCfg = $ubillingConfig->getAlter();
  101. if ((!isset($this->altCfg['WDYC_ONLY_MOBILE'])) OR ( !@$this->altCfg['WDYC_ONLY_MOBILE'])) {
  102. $this->onlyMobileFlag = false;
  103. }
  104. }
  105. /**
  106. * Detects is ffmpeg available on local system and sets ffmpegFlag and path properties.
  107. *
  108. * @return void
  109. */
  110. protected function detectFfmpeg() {
  111. if (file_exists($this->baseConverterPath)) {
  112. $this->ffmpegFlag = true;
  113. $this->ffmpegPath = $this->baseConverterPath;
  114. }
  115. }
  116. /**
  117. * Loads existing tagtypes and usertags into protected props for further usage
  118. *
  119. * @return void
  120. */
  121. protected function loadUserTags() {
  122. $this->userTags = zb_UserGetAllTags();
  123. }
  124. /**
  125. * Catches file download or convert request
  126. *
  127. * @return void
  128. */
  129. public function catchFileDownload() {
  130. if (ubRouting::checkGet('dlaskcall')) {
  131. $origFileName = ubRouting::get('dlaskcall');
  132. $downloadableName = '';
  133. //voice records
  134. if (file_exists($this->voicePath . $origFileName)) {
  135. $downloadableName = $this->voicePath . $origFileName;
  136. } else {
  137. //archive download
  138. if (file_exists($this->archivePath . $origFileName)) {
  139. $downloadableName = $this->archivePath . $origFileName;
  140. }
  141. }
  142. //voice files converter installed?
  143. if ($this->ffmpegFlag) {
  144. if (ubRouting::checkGet('playable')) {
  145. //need to run converter
  146. if (!empty($downloadableName)) {
  147. //original file is already located
  148. $newFileExtension = (ubRouting::checkGet('mp3')) ? '.mp3' : '.ogg';
  149. $newFilePath = $this->convertedPath . $origFileName . $newFileExtension;
  150. $command = $this->ffmpegPath . ' -y -i ' . $downloadableName . ' ' . $newFilePath . ' 2>> ' . $this->converterLogPath;
  151. shell_exec($command);
  152. $downloadableName = $newFilePath;
  153. }
  154. }
  155. } else {
  156. show_error(__('ffmpeg is not installed. Web player and converter not available.'));
  157. }
  158. //file download processing
  159. if (!empty($downloadableName)) {
  160. zb_DownloadFile($downloadableName, 'default');
  161. } else {
  162. show_error(__('File not exist') . ': ' . $origFileName);
  163. }
  164. }
  165. }
  166. /**
  167. * Returns available calls files array
  168. *
  169. * @return array
  170. */
  171. protected function getCallsDir() {
  172. $result = array();
  173. if (file_exists($this->voicePath)) {
  174. $result = rcms_scandir($this->voicePath, $this->callsFormat, 'file');
  175. }
  176. return ($result);
  177. }
  178. /**
  179. * Returns available archived calls files array
  180. *
  181. * @return array
  182. */
  183. protected function getArchiveDir() {
  184. $result = array();
  185. if (file_exists($this->archivePath)) {
  186. $result = rcms_scandir($this->archivePath, $this->callsFormat, 'file');
  187. }
  188. return ($result);
  189. }
  190. /**
  191. * Returns calls list container
  192. *
  193. * @return string
  194. */
  195. public function renderCallsList() {
  196. $opts = '"order": [[ 0, "desc" ]]';
  197. $columns = array(__('Date'), __('Number'), __('User'), __('Tags'), __('File'));
  198. if (ubRouting::checkGet('username')) {
  199. $loginFilter = '&loginfilter=' . ubRouting::get('username');
  200. } else {
  201. $loginFilter = '';
  202. }
  203. if (ubRouting::checkGet('renderall')) {
  204. $filterNumber = '&renderall=true';
  205. } else {
  206. $filterNumber = '';
  207. }
  208. $result = wf_JqDtLoader($columns, self::URL_ME . '&ajax=true' . $loginFilter . $filterNumber, false, __('Calls records'), 100, $opts);
  209. return ($result);
  210. }
  211. /**
  212. * Renders user tags if available
  213. *
  214. * @param string $userLogin
  215. *
  216. * @return string
  217. */
  218. protected function renderUserTags($userLogin) {
  219. $result = '';
  220. if (!empty($userLogin)) {
  221. if (isset($this->userTags[$userLogin])) {
  222. if (!empty($this->userTags[$userLogin])) {
  223. $result .= implode(', ', $this->userTags[$userLogin]);
  224. }
  225. }
  226. }
  227. return ($result);
  228. }
  229. /**
  230. * Renders json recorded calls list
  231. *
  232. * @param string $filterLogin
  233. * @param bool $renderAll
  234. *
  235. * @return void
  236. */
  237. public function jsonCallsList($filterLogin = '', $renderAll = false) {
  238. $allAddress = zb_AddressGetFulladdresslistCached();
  239. $allRealnames = zb_UserGetAllRealnames();
  240. $this->loadUserTags();
  241. $json = new wf_JqDtHelper();
  242. $allVoiceFiles = $this->getCallsDir();
  243. $allArchiveFiles = $this->getArchiveDir();
  244. $telepathy = new Telepathy(false, true);
  245. $telepathy->usePhones();
  246. $askCalls = new nya_askcalls();
  247. $previousCalls = $askCalls->getAll('filename');
  248. $curYear = curyear() . '-';
  249. //current year filter for all calls
  250. if (empty($filterLogin) AND ! $renderAll) {
  251. $renderAll = false;
  252. } else {
  253. $renderAll = true;
  254. }
  255. $allCallsLabel = ($renderAll) ? wf_img('skins/allcalls.png', __('All time')) . ' ' : '';
  256. //normal voice records rendering
  257. if (!empty($allVoiceFiles)) {
  258. /**
  259. * Fuck a fucking placement, I don't need you motherfuckers
  260. * I'ma get it on my own before I get on your production
  261. * 'Cause you fucking pieces of shit don't show no motherfucking love to me
  262. * I see right through your guise, you try and hide but you can't run from me
  263. */
  264. foreach ($allVoiceFiles as $io => $each) {
  265. $fileName = $each;
  266. $explodedFile = explode('_', $fileName);
  267. $cleanDate = explode('.', $explodedFile[2]);
  268. $cleanDate = $cleanDate[0];
  269. $callingNumber = $explodedFile[1];
  270. $callDirection = ($explodedFile[0] == 'in') ? self::ICON_PATH . 'incoming.png' : self::ICON_PATH . 'outgoing.png';
  271. //unfinished calls
  272. if ((!ispos($cleanDate, 'in')) AND ( !ispos($cleanDate, 'out'))) {
  273. if (!isset($previousCalls[$fileName])) {
  274. //here onlyMobile flag used for mobile normalizing too
  275. $userLogin = $telepathy->getByPhoneFast($callingNumber, $this->onlyMobileFlag, $this->onlyMobileFlag);
  276. $askCalls->data('filename', ubRouting::filters($fileName, 'mres'));
  277. $askCalls->data('login', ubRouting::filters($userLogin, 'mres'));
  278. $askCalls->create();
  279. } else {
  280. $userLogin = $previousCalls[$fileName]['login'];
  281. }
  282. $userLink = (!empty($userLogin)) ? wf_Link('?module=userprofile&username=' . $userLogin, web_profile_icon() . ' ' . @$allAddress[$userLogin]) . ' ' . @$allRealnames[$userLogin] : '';
  283. $newDateString = date_format(date_create_from_format('Y-m-d-H-i-s', $cleanDate), 'Y-m-d H:i:s');
  284. $cleanDate = $newDateString;
  285. $fileUrl = self::URL_ME . '&dlaskcall=' . $fileName;
  286. if ((empty($filterLogin)) OR ( $filterLogin == $userLogin)) {
  287. if ($renderAll) {
  288. $data[] = wf_img($callDirection) . ' ' . $cleanDate;
  289. $data[] = $callingNumber;
  290. $data[] = $userLink;
  291. $data[] = $this->renderUserTags($userLogin);
  292. $data[] = $this->getSoundcontrols($fileUrl) . $allCallsLabel;
  293. $json->addRow($data);
  294. } else {
  295. if (ispos($cleanDate, $curYear)) {
  296. $data[] = wf_img($callDirection) . ' ' . $cleanDate;
  297. $data[] = $callingNumber;
  298. $data[] = $userLink;
  299. $data[] = $this->renderUserTags($userLogin);
  300. $data[] = $this->getSoundcontrols($fileUrl) . $allCallsLabel;
  301. $json->addRow($data);
  302. }
  303. }
  304. }
  305. unset($data);
  306. }
  307. }
  308. }
  309. //archived records rendering
  310. if (!empty($allArchiveFiles)) {
  311. $archiveLabel = wf_img('skins/calls/archived.png', __('Archive'));
  312. foreach ($allArchiveFiles as $io => $each) {
  313. $fileName = $each;
  314. $explodedFile = explode('_', $fileName);
  315. $cleanDate = explode('.', $explodedFile[2]);
  316. $cleanDate = $cleanDate[0];
  317. $callingNumber = $explodedFile[1];
  318. $callDirection = ($explodedFile[0] == 'in') ? self::ICON_PATH . 'incoming.png' : self::ICON_PATH . 'outgoing.png';
  319. //unfinished calls
  320. if ((!ispos($cleanDate, 'in')) AND ( !ispos($cleanDate, 'out'))) {
  321. if (!isset($previousCalls[$fileName])) {
  322. //here onlyMobile flag used for mobile normalizing too
  323. $userLogin = $telepathy->getByPhoneFast($callingNumber, $this->onlyMobileFlag, $this->onlyMobileFlag);
  324. $askCalls->data('filename', ubRouting::filters($fileName, 'mres'));
  325. $askCalls->data('login', ubRouting::filters($userLogin, 'mres'));
  326. $askCalls->create();
  327. } else {
  328. $userLogin = $previousCalls[$fileName]['login'];
  329. }
  330. $userLink = (!empty($userLogin)) ? wf_Link('?module=userprofile&username=' . $userLogin, web_profile_icon() . ' ' . @$allAddress[$userLogin]) . ' ' . @$allRealnames[$userLogin] : '';
  331. $newDateString = date_format(date_create_from_format('Y-m-d-H-i-s', $cleanDate), 'Y-m-d H:i:s');
  332. $cleanDate = $newDateString;
  333. $fileUrl = self::URL_ME . '&dlaskcall=' . $fileName;
  334. if ((empty($filterLogin)) OR ( $filterLogin == $userLogin)) {
  335. if ($renderAll) {
  336. $data[] = wf_img($callDirection) . ' ' . $cleanDate;
  337. $data[] = $callingNumber;
  338. $data[] = $userLink;
  339. $data[] = $this->renderUserTags($userLogin);
  340. $data[] = $this->getSoundcontrols($fileUrl) . ' ' . $archiveLabel . $allCallsLabel;
  341. $json->addRow($data);
  342. } else {
  343. if (ispos($cleanDate, $curYear)) {
  344. $data[] = wf_img($callDirection) . ' ' . $cleanDate;
  345. $data[] = $callingNumber;
  346. $data[] = $userLink;
  347. $data[] = $this->renderUserTags($userLogin);
  348. $data[] = $this->getSoundcontrols($fileUrl) . ' ' . $archiveLabel . $allCallsLabel;
  349. $json->addRow($data);
  350. }
  351. }
  352. }
  353. unset($data);
  354. }
  355. }
  356. }
  357. $telepathy->savePhoneTelepathyCache();
  358. $json->getJson();
  359. }
  360. /**
  361. * Returns controls for some recorded call file
  362. *
  363. * @param string $fileUrl
  364. *
  365. * @return string
  366. */
  367. protected function getSoundcontrols($fileUrl) {
  368. $result = '';
  369. if (!empty($fileUrl)) {
  370. if ($this->ffmpegFlag) {
  371. $playableUrl = $fileUrl . '&playable=true';
  372. $iconPlay = wf_img('skins/play.png', __('Play'));
  373. $iconPause = wf_img('skins/pause.png', __('Pause'));
  374. $playerId = 'player_' . wf_InputId();
  375. $playControlId = 'controller_' . wf_InputId();
  376. $result .= wf_tag('audio', false, '', 'id="' . $playerId . '" src="' . $playableUrl . '" preload=none') . wf_tag('audio', true);
  377. $playController = 'document.getElementById(\'' . $playerId . '\').play();';
  378. $result .= wf_Link('#', $iconPlay, false, '', 'id="' . $playControlId . '" onclick="' . $playController . '"') . ' ';
  379. $result .= wf_Link('#', $iconPause, false, '', 'onclick="document.getElementById(\'' . $playerId . '\').pause();"') . ' ';
  380. $result .= wf_Link($playableUrl, wf_img('skins/icon_ogg.png', __('Download') . ' ' . __('as OGG'))) . ' ';
  381. $result .= wf_Link($playableUrl . '&mp3=true', wf_img('skins/icon_mp3.png', __('Download') . ' ' . __('as MP3'))) . ' ';
  382. } else {
  383. $result .= wf_Link('#', wf_img('skins/factorcontrol.png', __('ffmpeg is not installed. Web player and converter not available.'))) . ' ';
  384. }
  385. //basic download control
  386. $result .= wf_Link($fileUrl, wf_img('skins/icon_download.png', __('Download') . ' ' . __('as is')));
  387. }
  388. return ($result);
  389. }
  390. }