From 9c9de75826198b3d420355620c5ef61fda3ebe98 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 7 Jul 2006 14:57:59 +0000 Subject: [PATCH] 1.1.10 release git-svn-id: svn://192.168.202.10@1 3d104415-ff17-0410-8863-d5cf3c621b8a --- ADMIN_adjust_GMTnow_on_leads.pl | 670 +++ ADMIN_area_code_populate.pl | 57 + ADMIN_keepalive_AST_VDautodial.pl | 120 + ADMIN_keepalive_AST_VDremote_agents.pl | 119 + ADMIN_keepalive_AST_send_listen.pl | 169 + ADMIN_keepalive_AST_update.pl | 121 + ADMIN_keepalive_send_listen.at | 6 + ADMIN_restart_roll_logs.pl | 52 + AST_CRON_mix_recordings.pl | 122 + AST_CRON_mix_recordings_BASIC.pl | 97 + AST_CRON_mix_recordings_GSM.pl | 151 + AST_DB_optimize.pl | 153 + AST_SERVER_conf.pl | 25 + AST_VDauto_dial.pl | 939 +++ AST_VDhopper.pl | 570 ++ AST_VDremote_agents.pl | 493 ++ AST_cleanup_agent_log.pl | 236 + AST_conf_update.pl | 220 + AST_flush_DBqueue.pl | 189 + AST_manager_kill_hung_congested.pl | 335 ++ AST_manager_listen.pl | 526 ++ AST_manager_send.pl | 384 ++ AST_reset_mysql_vars.pl | 72 + AST_send_action_child.pl | 258 + AST_update.pl | 1099 ++++ AST_vm_update.pl | 202 + DTMF_sounds/0.wav | Bin 0 -> 8044 bytes DTMF_sounds/1.wav | Bin 0 -> 8044 bytes DTMF_sounds/2.wav | Bin 0 -> 8044 bytes DTMF_sounds/3.wav | Bin 0 -> 8044 bytes DTMF_sounds/4.wav | Bin 0 -> 8044 bytes DTMF_sounds/5.wav | Bin 0 -> 8044 bytes DTMF_sounds/6.wav | Bin 0 -> 8044 bytes DTMF_sounds/7.wav | Bin 0 -> 8044 bytes DTMF_sounds/8.wav | Bin 0 -> 8044 bytes DTMF_sounds/9.wav | Bin 0 -> 8044 bytes DTMF_sounds/US_pol_survey_hello.gsm | Bin 0 -> 55737 bytes DTMF_sounds/US_pol_survey_transfer.gsm | Bin 0 -> 10461 bytes DTMF_sounds/US_reminder_callback.gsm | Bin 0 -> 5610 bytes DTMF_sounds/US_reminder_goodbye.gsm | Bin 0 -> 1980 bytes DTMF_sounds/US_reminder_message.gsm | Bin 0 -> 14058 bytes DTMF_sounds/US_reminder_options.gsm | Bin 0 -> 11748 bytes DTMF_sounds/US_thanks_no_contact.gsm | Bin 0 -> 3894 bytes DTMF_sounds/four_digit_id.gsm | Bin 0 -> 2211 bytes DTMF_sounds/generic_hold.gsm | Bin 0 -> 8250 bytes DTMF_sounds/hash.wav | Bin 0 -> 8044 bytes DTMF_sounds/hold_tone.gsm | Bin 0 -> 759 bytes DTMF_sounds/silence.wav | Bin 0 -> 8044 bytes DTMF_sounds/star.wav | Bin 0 -> 8044 bytes LANG_admin.zip | Bin 0 -> 598320 bytes .../astguiclient/AST_inboundEXTstats.php | 369 ++ LANG_admin/LANG_admin/astguiclient/admin.php | 1627 +++++ .../LANG_admin/astguiclient/dbconnect.php | 15 + LANG_admin/LANG_admin/astguiclient/help.gif | Bin 0 -> 492 bytes .../LANG_admin/astguiclient/htglobalize.php | 23 + .../LANG_admin/astguiclient/inbound_popup.php | 194 + .../LANG_admin/astguiclient/phone_stats.php | 228 + .../astguiclient/remote_inbound.php | 165 + .../astguiclient_el/AST_inboundEXTstats.php | 369 ++ .../LANG_admin/astguiclient_el/admin.php | 1627 +++++ .../LANG_admin/astguiclient_el/dbconnect.php | 15 + .../LANG_admin/astguiclient_el/help.gif | Bin 0 -> 492 bytes .../astguiclient_el/htglobalize.php | 23 + .../astguiclient_el/inbound_popup.php | 194 + .../astguiclient_el/phone_stats.php | 228 + .../astguiclient_el/remote_inbound.php | 165 + .../astguiclient_en/AST_inboundEXTstats.php | 369 ++ .../LANG_admin/astguiclient_en/admin.php | 1627 +++++ .../LANG_admin/astguiclient_en/dbconnect.php | 16 + .../LANG_admin/astguiclient_en/help.gif | Bin 0 -> 492 bytes .../astguiclient_en/htglobalize.php | 23 + .../astguiclient_en/inbound_popup.php | 194 + .../astguiclient_en/phone_stats.php | 228 + .../astguiclient_en/remote_inbound.php | 165 + .../astguiclient_es/AST_inboundEXTstats.php | 369 ++ .../LANG_admin/astguiclient_es/admin.php | 1627 +++++ .../LANG_admin/astguiclient_es/dbconnect.php | 15 + .../LANG_admin/astguiclient_es/help.gif | Bin 0 -> 492 bytes .../astguiclient_es/htglobalize.php | 23 + .../astguiclient_es/inbound_popup.php | 194 + .../astguiclient_es/phone_stats.php | 228 + .../astguiclient_es/remote_inbound.php | 165 + LANG_admin/LANG_admin/install_server_files.pl | 426 ++ LANG_admin/LANG_admin/languages.txt | 358 ++ LANG_admin/LANG_admin/languages_admin.txt | 709 +++ .../LANG_admin/vicidial/AST_CLOSERstats.php | 370 ++ .../LANG_admin/vicidial/AST_VDADstats.php | 390 ++ .../vicidial/AST_VICIDIAL_hopperlist.php | 135 + .../vicidial/AST_admin_log_display.php | 105 + .../vicidial/AST_agent_performance.php | 205 + .../vicidial/AST_agent_performance_detail.php | 379 ++ .../vicidial/AST_agent_time_sheet.php | 256 + .../LANG_admin/vicidial/AST_parkstats.php | 369 ++ .../vicidial/AST_server_performance.php | 283 + .../LANG_admin/vicidial/AST_timeonVDAD.php | 231 + .../vicidial/AST_timeonVDAD_closer.php | 289 + .../LANG_admin/vicidial/AST_timeonVDADall.php | 272 + .../LANG_admin/vicidial/AST_timeoncall.php | 171 + .../LANG_admin/vicidial/AST_timeonpark.php | 193 + LANG_admin/LANG_admin/vicidial/admin-old.php | 2289 +++++++ LANG_admin/LANG_admin/vicidial/admin.php | 4156 +++++++++++++ .../LANG_admin/vicidial/admin_modify_lead.php | 321 + .../LANG_admin/vicidial/admin_search_lead.php | 185 + .../vicidial/closer-fronter_popup.php | 470 ++ .../vicidial/closer-fronter_popup2.php | 385 ++ LANG_admin/LANG_admin/vicidial/closer.php | 334 ++ .../LANG_admin/vicidial/closer_dispo.php | 289 + .../LANG_admin/vicidial/closer_popup.php | 431 ++ LANG_admin/LANG_admin/vicidial/count.htm | 1 + LANG_admin/LANG_admin/vicidial/dbconnect.php | 15 + .../vicidial/group_hourly_stats.php | 227 + LANG_admin/LANG_admin/vicidial/help.gif | Bin 0 -> 492 bytes .../LANG_admin/vicidial/htglobalize.php | 23 + LANG_admin/LANG_admin/vicidial/listloader.php | 187 + LANG_admin/LANG_admin/vicidial/listloader.pl | 126 + .../LANG_admin/vicidial/listloaderMAIN.php | 69 + .../vicidial/listloader_rowdisplay.pl | 56 + .../LANG_admin/vicidial/listloader_super.pl | 128 + LANG_admin/LANG_admin/vicidial/log_test.php | 33 + .../vicidial/new_listloader_superL.php | 747 +++ .../vicidial/record_conf_1_hour.php | 124 + .../LANG_admin/vicidial/remote_dispo.php | 279 + .../LANG_admin/vicidial/server_stats.php | 70 + LANG_admin/LANG_admin/vicidial/user_stats.php | 302 + .../LANG_admin/vicidial/user_status.php | 223 + LANG_admin/LANG_admin/vicidial/vdremote.php | 584 ++ .../vicidial_el/AST_CLOSERstats.php | 360 ++ .../LANG_admin/vicidial_el/AST_VDADstats.php | 390 ++ .../vicidial_el/AST_VICIDIAL_hopperlist.php | 135 + .../vicidial_el/AST_admin_log_display.php | 105 + .../vicidial_el/AST_agent_performance.php | 205 + .../AST_agent_performance_detail.php | 379 ++ .../vicidial_el/AST_agent_time_sheet.php | 256 + .../vicidial_el/AST_server_performance.php | 283 + .../LANG_admin/vicidial_el/AST_timeonVDAD.php | 231 + .../vicidial_el/AST_timeonVDAD_closer.php | 289 + LANG_admin/LANG_admin/vicidial_el/admin.php | 4156 +++++++++++++ .../vicidial_el/admin_modify_lead.php | 321 + .../vicidial_el/admin_search_lead.php | 185 + LANG_admin/LANG_admin/vicidial_el/count.htm | 1 + .../LANG_admin/vicidial_el/dbconnect.php | 15 + .../vicidial_el/group_hourly_stats.php | 227 + LANG_admin/LANG_admin/vicidial_el/help.gif | Bin 0 -> 492 bytes .../LANG_admin/vicidial_el/htglobalize.php | 23 + .../LANG_admin/vicidial_el/listloader.php | 187 + .../LANG_admin/vicidial_el/listloaderMAIN.php | 69 + .../vicidial_el/new_listloader_superL.php | 747 +++ .../LANG_admin/vicidial_el/remote_dispo.php | 279 + .../LANG_admin/vicidial_el/server_stats.php | 70 + .../LANG_admin/vicidial_el/user_stats.php | 302 + .../LANG_admin/vicidial_el/user_status.php | 223 + .../LANG_admin/vicidial_el/vdremote.php | 584 ++ .../vicidial_en/AST_CLOSERstats.php | 360 ++ .../LANG_admin/vicidial_en/AST_VDADstats.php | 390 ++ .../vicidial_en/AST_VICIDIAL_hopperlist.php | 135 + .../vicidial_en/AST_admin_log_display.php | 105 + .../vicidial_en/AST_agent_performance.php | 205 + .../AST_agent_performance_detail.php | 379 ++ .../vicidial_en/AST_agent_time_sheet.php | 256 + .../vicidial_en/AST_server_performance.php | 283 + .../LANG_admin/vicidial_en/AST_timeonVDAD.php | 231 + .../vicidial_en/AST_timeonVDAD_closer.php | 289 + LANG_admin/LANG_admin/vicidial_en/admin.php | 4156 +++++++++++++ .../vicidial_en/admin_modify_lead.php | 321 + .../vicidial_en/admin_search_lead.php | 185 + LANG_admin/LANG_admin/vicidial_en/count.htm | 1 + .../LANG_admin/vicidial_en/dbconnect.php | 15 + .../vicidial_en/group_hourly_stats.php | 227 + LANG_admin/LANG_admin/vicidial_en/help.gif | Bin 0 -> 492 bytes .../LANG_admin/vicidial_en/htglobalize.php | 23 + .../LANG_admin/vicidial_en/listloader.php | 187 + .../LANG_admin/vicidial_en/listloaderMAIN.php | 69 + .../vicidial_en/new_listloader_superL.php | 747 +++ .../LANG_admin/vicidial_en/remote_dispo.php | 279 + .../LANG_admin/vicidial_en/server_stats.php | 70 + .../LANG_admin/vicidial_en/user_stats.php | 302 + .../LANG_admin/vicidial_en/user_status.php | 223 + .../LANG_admin/vicidial_en/vdremote.php | 584 ++ .../vicidial_es/AST_CLOSERstats.php | 360 ++ .../LANG_admin/vicidial_es/AST_VDADstats.php | 390 ++ .../vicidial_es/AST_VICIDIAL_hopperlist.php | 135 + .../vicidial_es/AST_admin_log_display.php | 105 + .../vicidial_es/AST_agent_performance.php | 205 + .../AST_agent_performance_detail.php | 379 ++ .../vicidial_es/AST_agent_time_sheet.php | 256 + .../vicidial_es/AST_server_performance.php | 283 + .../LANG_admin/vicidial_es/AST_timeonVDAD.php | 231 + .../vicidial_es/AST_timeonVDAD_closer.php | 289 + LANG_admin/LANG_admin/vicidial_es/admin.php | 4156 +++++++++++++ .../vicidial_es/admin_modify_lead.php | 321 + .../vicidial_es/admin_search_lead.php | 185 + LANG_admin/LANG_admin/vicidial_es/count.htm | 1 + .../LANG_admin/vicidial_es/dbconnect.php | 15 + .../vicidial_es/group_hourly_stats.php | 227 + LANG_admin/LANG_admin/vicidial_es/help.gif | Bin 0 -> 492 bytes .../LANG_admin/vicidial_es/htglobalize.php | 23 + .../LANG_admin/vicidial_es/listloader.php | 187 + .../LANG_admin/vicidial_es/listloaderMAIN.php | 69 + .../vicidial_es/new_listloader_superL.php | 747 +++ .../LANG_admin/vicidial_es/remote_dispo.php | 279 + .../LANG_admin/vicidial_es/server_stats.php | 70 + .../LANG_admin/vicidial_es/user_stats.php | 302 + .../LANG_admin/vicidial_es/user_status.php | 223 + .../LANG_admin/vicidial_es/vdremote.php | 584 ++ LANG_agc.zip | Bin 0 -> 836571 bytes LANG_agc/LANG_agc/agc/active_list_refresh.php | 446 ++ LANG_agc/LANG_agc/agc/astguiclient.php | 2874 +++++++++ LANG_agc/LANG_agc/agc/call_log_display.php | 181 + LANG_agc/LANG_agc/agc/conf_exten_check.php | 213 + LANG_agc/LANG_agc/agc/dbconnect.php | 15 + .../agc/images/agc_check_voicemail_BLINK.gif | Bin 0 -> 2627 bytes .../images/agc_check_voicemail_BLINK_e.gif | Bin 0 -> 780 bytes .../agc/images/agc_check_voicemail_OFF.gif | Bin 0 -> 919 bytes .../agc/images/agc_check_voicemail_OFF_es.gif | Bin 0 -> 785 bytes .../agc/images/agc_check_voicemail_ON.gif | Bin 0 -> 919 bytes .../agc/images/agc_check_voicemail_ON_es.gif | Bin 0 -> 781 bytes .../LANG_agc/agc/images/agc_live_call_OFF.gif | Bin 0 -> 541 bytes .../agc/images/agc_live_call_OFF_es.gif | Bin 0 -> 513 bytes .../LANG_agc/agc/images/agc_live_call_ON.gif | Bin 0 -> 793 bytes .../agc/images/agc_live_call_ON_es.gif | Bin 0 -> 871 bytes .../agc/images/agc_tab_active_lines.gif | Bin 0 -> 886 bytes .../agc/images/agc_tab_active_lines_es.gif | Bin 0 -> 704 bytes .../agc/images/agc_tab_astguiclient.gif | Bin 0 -> 1092 bytes .../agc/images/agc_tab_conferences.gif | Bin 0 -> 928 bytes .../agc/images/agc_tab_conferences_es.gif | Bin 0 -> 718 bytes LANG_agc/LANG_agc/agc/images/agc_tab_main.gif | Bin 0 -> 596 bytes .../LANG_agc/agc/images/agc_tab_main_es.gif | Bin 0 -> 541 bytes LANG_agc/LANG_agc/agc/images/blank.gif | Bin 0 -> 43 bytes LANG_agc/LANG_agc/agc/images/de.gif | Bin 0 -> 162 bytes LANG_agc/LANG_agc/agc/images/down.gif | Bin 0 -> 169 bytes LANG_agc/LANG_agc/agc/images/el.gif | Bin 0 -> 245 bytes LANG_agc/LANG_agc/agc/images/en.gif | Bin 0 -> 322 bytes LANG_agc/LANG_agc/agc/images/es.gif | Bin 0 -> 222 bytes LANG_agc/LANG_agc/agc/images/fr.gif | Bin 0 -> 217 bytes LANG_agc/LANG_agc/agc/images/it.gif | Bin 0 -> 198 bytes LANG_agc/LANG_agc/agc/images/pt.gif | Bin 0 -> 208 bytes LANG_agc/LANG_agc/agc/images/remove.gif | Bin 0 -> 176 bytes LANG_agc/LANG_agc/agc/images/up.gif | Bin 0 -> 169 bytes .../agc/images/vdc_LB_dialnextnumber.gif | Bin 0 -> 831 bytes .../agc/images/vdc_LB_dialnextnumber_OFF.gif | Bin 0 -> 827 bytes .../images/vdc_LB_dialnextnumber_OFF_es.gif | Bin 0 -> 914 bytes .../agc/images/vdc_LB_dialnextnumber_es.gif | Bin 0 -> 914 bytes .../agc/images/vdc_LB_grabparkedcall.gif | Bin 0 -> 804 bytes .../agc/images/vdc_LB_grabparkedcall_OFF.gif | Bin 0 -> 802 bytes .../images/vdc_LB_grabparkedcall_OFF_es.gif | Bin 0 -> 827 bytes .../agc/images/vdc_LB_grabparkedcall_es.gif | Bin 0 -> 826 bytes .../agc/images/vdc_LB_hangupcustomer.gif | Bin 0 -> 803 bytes .../agc/images/vdc_LB_hangupcustomer_OFF.gif | Bin 0 -> 803 bytes .../images/vdc_LB_hangupcustomer_OFF_es.gif | Bin 0 -> 693 bytes .../agc/images/vdc_LB_hangupcustomer_es.gif | Bin 0 -> 697 bytes .../LANG_agc/agc/images/vdc_LB_parkcall.gif | Bin 0 -> 601 bytes .../agc/images/vdc_LB_parkcall_OFF.gif | Bin 0 -> 607 bytes .../agc/images/vdc_LB_parkcall_OFF_es.gif | Bin 0 -> 644 bytes .../agc/images/vdc_LB_parkcall_es.gif | Bin 0 -> 646 bytes LANG_agc/LANG_agc/agc/images/vdc_LB_pause.gif | Bin 0 -> 483 bytes .../LANG_agc/agc/images/vdc_LB_pause_OFF.gif | Bin 0 -> 483 bytes .../agc/images/vdc_LB_pause_OFF_es.gif | Bin 0 -> 404 bytes .../LANG_agc/agc/images/vdc_LB_pause_es.gif | Bin 0 -> 410 bytes .../LANG_agc/agc/images/vdc_LB_resume.gif | Bin 0 -> 535 bytes .../LANG_agc/agc/images/vdc_LB_resume_OFF.gif | Bin 0 -> 533 bytes .../agc/images/vdc_LB_resume_OFF_es.gif | Bin 0 -> 495 bytes .../LANG_agc/agc/images/vdc_LB_resume_es.gif | Bin 0 -> 500 bytes .../LANG_agc/agc/images/vdc_LB_senddtmf.gif | Bin 0 -> 567 bytes .../agc/images/vdc_LB_senddtmf_OFF.gif | Bin 0 -> 563 bytes .../agc/images/vdc_LB_senddtmf_OFF_es.gif | Bin 0 -> 563 bytes .../agc/images/vdc_LB_senddtmf_es.gif | Bin 0 -> 564 bytes .../LANG_agc/agc/images/vdc_LB_spacer.gif | Bin 0 -> 90 bytes .../agc/images/vdc_LB_startrecording.gif | Bin 0 -> 778 bytes .../agc/images/vdc_LB_startrecording_OFF.gif | Bin 0 -> 778 bytes .../images/vdc_LB_startrecording_OFF_es.gif | Bin 0 -> 788 bytes .../agc/images/vdc_LB_startrecording_es.gif | Bin 0 -> 788 bytes .../agc/images/vdc_LB_stoprecording.gif | Bin 0 -> 758 bytes .../agc/images/vdc_LB_stoprecording_OFF.gif | Bin 0 -> 757 bytes .../images/vdc_LB_stoprecording_OFF_es.gif | Bin 0 -> 701 bytes .../agc/images/vdc_LB_stoprecording_es.gif | Bin 0 -> 699 bytes .../agc/images/vdc_LB_transferconf.gif | Bin 0 -> 732 bytes .../agc/images/vdc_LB_transferconf_OFF.gif | Bin 0 -> 732 bytes .../agc/images/vdc_LB_transferconf_OFF_es.gif | Bin 0 -> 712 bytes .../agc/images/vdc_LB_transferconf_es.gif | Bin 0 -> 714 bytes .../LANG_agc/agc/images/vdc_LB_webform.gif | Bin 0 -> 542 bytes .../agc/images/vdc_LB_webform_OFF.gif | Bin 0 -> 615 bytes .../agc/images/vdc_LB_webform_OFF_es.gif | Bin 0 -> 661 bytes .../LANG_agc/agc/images/vdc_LB_webform_es.gif | Bin 0 -> 583 bytes .../LANG_agc/agc/images/vdc_XB_ammessage.gif | Bin 0 -> 396 bytes .../agc/images/vdc_XB_ammessage_OFF.gif | Bin 0 -> 403 bytes .../agc/images/vdc_XB_blindtransfer.gif | Bin 0 -> 774 bytes .../agc/images/vdc_XB_blindtransfer_OFF.gif | Bin 0 -> 774 bytes .../images/vdc_XB_blindtransfer_OFF_es.gif | Bin 0 -> 758 bytes .../agc/images/vdc_XB_blindtransfer_es.gif | Bin 0 -> 758 bytes .../LANG_agc/agc/images/vdc_XB_channel.gif | Bin 0 -> 314 bytes .../LANG_agc/agc/images/vdc_XB_channel_es.gif | Bin 0 -> 242 bytes LANG_agc/LANG_agc/agc/images/vdc_XB_code.gif | Bin 0 -> 247 bytes .../agc/images/vdc_XB_dialwithcustomer.gif | Bin 0 -> 881 bytes .../images/vdc_XB_dialwithcustomer_OFF.gif | Bin 0 -> 881 bytes .../images/vdc_XB_dialwithcustomer_OFF_es.gif | Bin 0 -> 776 bytes .../agc/images/vdc_XB_dialwithcustomer_es.gif | Bin 0 -> 776 bytes .../agc/images/vdc_XB_hangupbothlines.gif | Bin 0 -> 849 bytes .../agc/images/vdc_XB_hangupbothlines_OFF.gif | Bin 0 -> 849 bytes .../images/vdc_XB_hangupbothlines_OFF_es.gif | Bin 0 -> 809 bytes .../agc/images/vdc_XB_hangupbothlines_es.gif | Bin 0 -> 809 bytes .../agc/images/vdc_XB_hangupxferline.gif | Bin 0 -> 803 bytes .../agc/images/vdc_XB_hangupxferline_OFF.gif | Bin 0 -> 803 bytes .../images/vdc_XB_hangupxferline_OFF_es.gif | Bin 0 -> 702 bytes .../agc/images/vdc_XB_hangupxferline_es.gif | Bin 0 -> 702 bytes .../LANG_agc/agc/images/vdc_XB_header.gif | Bin 0 -> 884 bytes .../LANG_agc/agc/images/vdc_XB_header_es.gif | Bin 0 -> 639 bytes .../agc/images/vdc_XB_hotkeysactive.gif | Bin 0 -> 791 bytes .../agc/images/vdc_XB_hotkeysactive_OFF.gif | Bin 0 -> 786 bytes .../vdc_XB_hotkeysactive_OFF_es-orig.gif | Bin 0 -> 1231 bytes .../images/vdc_XB_hotkeysactive_OFF_es.gif | Bin 0 -> 1256 bytes .../agc/images/vdc_XB_hotkeysactive_es.gif | Bin 0 -> 1231 bytes .../agc/images/vdc_XB_hotkeysinactive_es.gif | Bin 0 -> 1256 bytes .../agc/images/vdc_XB_internalcloser.gif | Bin 0 -> 801 bytes .../agc/images/vdc_XB_internalcloser_OFF.gif | Bin 0 -> 801 bytes .../images/vdc_XB_internalcloser_OFF_es.gif | Bin 0 -> 640 bytes .../agc/images/vdc_XB_internalcloser_es.gif | Bin 0 -> 640 bytes .../agc/images/vdc_XB_leave3waycall.gif | Bin 0 -> 816 bytes .../agc/images/vdc_XB_leave3waycall_OFF.gif | Bin 0 -> 816 bytes .../images/vdc_XB_leave3waycall_OFF_es.gif | Bin 0 -> 834 bytes .../agc/images/vdc_XB_leave3waycall_es.gif | Bin 0 -> 834 bytes .../agc/images/vdc_XB_localcloser.gif | Bin 0 -> 728 bytes .../agc/images/vdc_XB_localcloser_OFF.gif | Bin 0 -> 728 bytes .../agc/images/vdc_XB_localcloser_OFF_es.gif | Bin 0 -> 607 bytes .../agc/images/vdc_XB_localcloser_es.gif | Bin 0 -> 606 bytes .../LANG_agc/agc/images/vdc_XB_number.gif | Bin 0 -> 480 bytes .../LANG_agc/agc/images/vdc_XB_number_es.gif | Bin 0 -> 416 bytes .../agc/images/vdc_XB_parkcustomerdial.gif | Bin 0 -> 896 bytes .../images/vdc_XB_parkcustomerdial_OFF.gif | Bin 0 -> 896 bytes .../images/vdc_XB_parkcustomerdial_OFF_es.gif | Bin 0 -> 661 bytes .../agc/images/vdc_XB_parkcustomerdial_es.gif | Bin 0 -> 659 bytes .../LANG_agc/agc/images/vdc_XB_seconds.gif | Bin 0 -> 352 bytes .../LANG_agc/agc/images/vdc_XB_seconds_es.gif | Bin 0 -> 333 bytes .../LANG_agc/agc/images/vdc_tab_script.gif | Bin 0 -> 825 bytes .../LANG_agc/agc/images/vdc_tab_script_es.gif | Bin 0 -> 916 bytes .../LANG_agc/agc/images/vdc_tab_vicidial.gif | Bin 0 -> 866 bytes LANG_agc/LANG_agc/agc/inbound_popup.php | 335 ++ LANG_agc/LANG_agc/agc/live_exten_check.php | 263 + LANG_agc/LANG_agc/agc/manager_send.php | 882 +++ LANG_agc/LANG_agc/agc/park_calls_display.php | 136 + LANG_agc/LANG_agc/agc/vdc_db_query.php | 1289 ++++ LANG_agc/LANG_agc/agc/vicidial.php | 4633 +++++++++++++++ LANG_agc/LANG_agc/agc/voicemail_check.php | 130 + .../LANG_agc/agc_de/active_list_refresh.php | 446 ++ LANG_agc/LANG_agc/agc_de/astguiclient.php | 2874 +++++++++ LANG_agc/LANG_agc/agc_de/call_log_display.php | 181 + LANG_agc/LANG_agc/agc_de/conf_exten_check.php | 213 + LANG_agc/LANG_agc/agc_de/dbconnect.php | 15 + LANG_agc/LANG_agc/agc_de/inbound_popup.php | 335 ++ LANG_agc/LANG_agc/agc_de/live_exten_check.php | 263 + LANG_agc/LANG_agc/agc_de/manager_send.php | 882 +++ .../LANG_agc/agc_de/park_calls_display.php | 136 + LANG_agc/LANG_agc/agc_de/vdc_db_query.php | 1289 ++++ LANG_agc/LANG_agc/agc_de/vicidial.php | 4633 +++++++++++++++ LANG_agc/LANG_agc/agc_de/voicemail_check.php | 130 + .../LANG_agc/agc_el/active_list_refresh.php | 446 ++ LANG_agc/LANG_agc/agc_el/astguiclient.php | 2874 +++++++++ LANG_agc/LANG_agc/agc_el/call_log_display.php | 181 + LANG_agc/LANG_agc/agc_el/conf_exten_check.php | 213 + LANG_agc/LANG_agc/agc_el/dbconnect.php | 15 + LANG_agc/LANG_agc/agc_el/inbound_popup.php | 335 ++ LANG_agc/LANG_agc/agc_el/live_exten_check.php | 263 + LANG_agc/LANG_agc/agc_el/manager_send.php | 882 +++ .../LANG_agc/agc_el/park_calls_display.php | 136 + LANG_agc/LANG_agc/agc_el/vdc_db_query.php | 1289 ++++ LANG_agc/LANG_agc/agc_el/vicidial.php | 4633 +++++++++++++++ LANG_agc/LANG_agc/agc_el/voicemail_check.php | 130 + .../LANG_agc/agc_en/active_list_refresh.php | 446 ++ LANG_agc/LANG_agc/agc_en/astguiclient.php | 2874 +++++++++ LANG_agc/LANG_agc/agc_en/call_log_display.php | 181 + LANG_agc/LANG_agc/agc_en/conf_exten_check.php | 213 + LANG_agc/LANG_agc/agc_en/dbconnect.php | 15 + LANG_agc/LANG_agc/agc_en/inbound_popup.php | 335 ++ LANG_agc/LANG_agc/agc_en/live_exten_check.php | 263 + LANG_agc/LANG_agc/agc_en/manager_send.php | 882 +++ .../LANG_agc/agc_en/park_calls_display.php | 136 + LANG_agc/LANG_agc/agc_en/vdc_db_query.php | 1289 ++++ LANG_agc/LANG_agc/agc_en/vicidial.php | 4633 +++++++++++++++ LANG_agc/LANG_agc/agc_en/voicemail_check.php | 130 + .../LANG_agc/agc_es/active_list_refresh.php | 446 ++ LANG_agc/LANG_agc/agc_es/astguiclient.php | 2874 +++++++++ LANG_agc/LANG_agc/agc_es/call_log_display.php | 181 + LANG_agc/LANG_agc/agc_es/conf_exten_check.php | 213 + LANG_agc/LANG_agc/agc_es/dbconnect.php | 15 + LANG_agc/LANG_agc/agc_es/inbound_popup.php | 335 ++ LANG_agc/LANG_agc/agc_es/live_exten_check.php | 263 + LANG_agc/LANG_agc/agc_es/manager_send.php | 882 +++ .../LANG_agc/agc_es/park_calls_display.php | 136 + LANG_agc/LANG_agc/agc_es/vdc_db_query.php | 1289 ++++ LANG_agc/LANG_agc/agc_es/vicidial.php | 4633 +++++++++++++++ LANG_agc/LANG_agc/agc_es/voicemail_check.php | 130 + .../LANG_agc/agc_fr/active_list_refresh.php | 446 ++ LANG_agc/LANG_agc/agc_fr/astguiclient.php | 2874 +++++++++ LANG_agc/LANG_agc/agc_fr/call_log_display.php | 181 + LANG_agc/LANG_agc/agc_fr/conf_exten_check.php | 213 + LANG_agc/LANG_agc/agc_fr/dbconnect.php | 15 + LANG_agc/LANG_agc/agc_fr/htglobalize.php | 23 + LANG_agc/LANG_agc/agc_fr/inbound_popup.php | 335 ++ LANG_agc/LANG_agc/agc_fr/live_exten_check.php | 263 + LANG_agc/LANG_agc/agc_fr/manager_send.php | 882 +++ .../LANG_agc/agc_fr/park_calls_display.php | 136 + LANG_agc/LANG_agc/agc_fr/vdc_db_query.php | 1289 ++++ LANG_agc/LANG_agc/agc_fr/vicidial.php | 4633 +++++++++++++++ LANG_agc/LANG_agc/agc_fr/voicemail_check.php | 130 + .../LANG_agc/agc_it/active_list_refresh.php | 446 ++ LANG_agc/LANG_agc/agc_it/astguiclient.php | 2874 +++++++++ LANG_agc/LANG_agc/agc_it/call_log_display.php | 181 + LANG_agc/LANG_agc/agc_it/conf_exten_check.php | 213 + LANG_agc/LANG_agc/agc_it/dbconnect.php | 15 + LANG_agc/LANG_agc/agc_it/inbound_popup.php | 335 ++ LANG_agc/LANG_agc/agc_it/live_exten_check.php | 263 + LANG_agc/LANG_agc/agc_it/manager_send.php | 882 +++ .../LANG_agc/agc_it/park_calls_display.php | 136 + LANG_agc/LANG_agc/agc_it/vdc_db_query.php | 1289 ++++ LANG_agc/LANG_agc/agc_it/vicidial.php | 4633 +++++++++++++++ LANG_agc/LANG_agc/agc_it/voicemail_check.php | 130 + .../LANG_agc/agc_pt/active_list_refresh.php | 446 ++ LANG_agc/LANG_agc/agc_pt/astguiclient.php | 2874 +++++++++ LANG_agc/LANG_agc/agc_pt/call_log_display.php | 181 + LANG_agc/LANG_agc/agc_pt/conf_exten_check.php | 213 + LANG_agc/LANG_agc/agc_pt/dbconnect.php | 15 + LANG_agc/LANG_agc/agc_pt/inbound_popup.php | 335 ++ LANG_agc/LANG_agc/agc_pt/live_exten_check.php | 263 + LANG_agc/LANG_agc/agc_pt/manager_send.php | 882 +++ .../LANG_agc/agc_pt/park_calls_display.php | 136 + LANG_agc/LANG_agc/agc_pt/vdc_db_query.php | 1289 ++++ LANG_agc/LANG_agc/agc_pt/vicidial.php | 4633 +++++++++++++++ LANG_agc/LANG_agc/agc_pt/voicemail_check.php | 130 + MySQL_AST_CREATE_tables.sql | 633 ++ VD_amd.agi | 368 ++ VD_amd_post.agi | 239 + VD_hangup.agi | 306 + VICIDIAL_IN_new_leads_file.pl | 309 + VICIDIAL_web/AST_CLOSERstats.php | 369 ++ VICIDIAL_web/AST_VDADstats.php | 390 ++ VICIDIAL_web/AST_VICIDIAL_hopperlist.php | 135 + VICIDIAL_web/AST_admin_log_display.php | 107 + VICIDIAL_web/AST_agent_performance.php | 205 + VICIDIAL_web/AST_agent_performance_detail.php | 379 ++ VICIDIAL_web/AST_agent_time_sheet.php | 256 + VICIDIAL_web/AST_parkstats.php | 369 ++ VICIDIAL_web/AST_server_performance.php | 283 + VICIDIAL_web/AST_timeonVDAD.php | 232 + VICIDIAL_web/AST_timeonVDAD_closer.php | 289 + VICIDIAL_web/AST_timeonVDADall.php | 272 + VICIDIAL_web/AST_timeoncall.php | 176 + VICIDIAL_web/AST_timeonpark.php | 193 + VICIDIAL_web/admin.php | 4156 +++++++++++++ VICIDIAL_web/admin_modify_lead.php | 321 + VICIDIAL_web/admin_search_lead.php | 185 + VICIDIAL_web/closer-fronter_popup.php | 468 ++ VICIDIAL_web/closer-fronter_popup2.php | 383 ++ VICIDIAL_web/closer.php | 329 ++ VICIDIAL_web/closer_dispo.php | 284 + VICIDIAL_web/closer_popup.php | 429 ++ VICIDIAL_web/count.htm | 1 + VICIDIAL_web/dbconnect.php | 20 + VICIDIAL_web/group_hourly_stats.php | 222 + VICIDIAL_web/help.gif | Bin 0 -> 492 bytes VICIDIAL_web/listloader.php | 187 + VICIDIAL_web/listloader.pl | 132 + VICIDIAL_web/listloaderMAIN.php | 72 + VICIDIAL_web/listloader_rowdisplay.pl | 62 + VICIDIAL_web/listloader_super.pl | 134 + VICIDIAL_web/log_test.php | 37 + VICIDIAL_web/new_listloader_superL.php | 747 +++ VICIDIAL_web/record_conf_1_hour.php | 124 + VICIDIAL_web/remote_dispo.php | 277 + VICIDIAL_web/server_stats.php | 69 + VICIDIAL_web/test.txt | 1 + VICIDIAL_web/user_stats.php | 302 + VICIDIAL_web/user_status.php | 223 + VICIDIAL_web/vdremote.php | 581 ++ agc/active_list_refresh.php | 448 ++ agc/astguiclient.php | 2876 +++++++++ agc/call_log_display.php | 183 + agc/conf_exten_check.php | 215 + agc/dbconnect.php | 18 + agc/images/agc_check_voicemail_BLINK.gif | Bin 0 -> 2627 bytes agc/images/agc_check_voicemail_BLINK_es.gif | Bin 0 -> 780 bytes agc/images/agc_check_voicemail_OFF.gif | Bin 0 -> 919 bytes agc/images/agc_check_voicemail_OFF_es.gif | Bin 0 -> 785 bytes agc/images/agc_check_voicemail_ON.gif | Bin 0 -> 919 bytes agc/images/agc_check_voicemail_ON_es.gif | Bin 0 -> 781 bytes agc/images/agc_live_call_OFF.gif | Bin 0 -> 541 bytes agc/images/agc_live_call_OFF_es.gif | Bin 0 -> 513 bytes agc/images/agc_live_call_ON.gif | Bin 0 -> 793 bytes agc/images/agc_live_call_ON_es.gif | Bin 0 -> 871 bytes agc/images/agc_tab_active_lines.gif | Bin 0 -> 886 bytes agc/images/agc_tab_active_lines_es.gif | Bin 0 -> 704 bytes agc/images/agc_tab_astguiclient.gif | Bin 0 -> 1092 bytes agc/images/agc_tab_conferences.gif | Bin 0 -> 928 bytes agc/images/agc_tab_conferences_es.gif | Bin 0 -> 718 bytes agc/images/agc_tab_main.gif | Bin 0 -> 596 bytes agc/images/agc_tab_main_es.gif | Bin 0 -> 541 bytes agc/images/blank.gif | Bin 0 -> 43 bytes agc/images/de.gif | Bin 0 -> 162 bytes agc/images/down.gif | Bin 0 -> 169 bytes agc/images/el.gif | Bin 0 -> 245 bytes agc/images/en.gif | Bin 0 -> 322 bytes agc/images/es.gif | Bin 0 -> 222 bytes agc/images/fr.gif | Bin 0 -> 217 bytes agc/images/it.gif | Bin 0 -> 198 bytes agc/images/pt.gif | Bin 0 -> 208 bytes agc/images/remove.gif | Bin 0 -> 176 bytes agc/images/up.gif | Bin 0 -> 169 bytes agc/images/vdc_LB_dialnextnumber.gif | Bin 0 -> 831 bytes agc/images/vdc_LB_dialnextnumber_OFF.gif | Bin 0 -> 827 bytes agc/images/vdc_LB_dialnextnumber_OFF_es.gif | Bin 0 -> 914 bytes agc/images/vdc_LB_dialnextnumber_es.gif | Bin 0 -> 914 bytes agc/images/vdc_LB_grabparkedcall.gif | Bin 0 -> 804 bytes agc/images/vdc_LB_grabparkedcall_OFF.gif | Bin 0 -> 802 bytes agc/images/vdc_LB_grabparkedcall_OFF_es.gif | Bin 0 -> 827 bytes agc/images/vdc_LB_grabparkedcall_es.gif | Bin 0 -> 826 bytes agc/images/vdc_LB_hangupcustomer.gif | Bin 0 -> 803 bytes agc/images/vdc_LB_hangupcustomer_OFF.gif | Bin 0 -> 803 bytes agc/images/vdc_LB_hangupcustomer_OFF_es.gif | Bin 0 -> 693 bytes agc/images/vdc_LB_hangupcustomer_es.gif | Bin 0 -> 697 bytes agc/images/vdc_LB_parkcall.gif | Bin 0 -> 601 bytes agc/images/vdc_LB_parkcall_OFF.gif | Bin 0 -> 607 bytes agc/images/vdc_LB_parkcall_OFF_es.gif | Bin 0 -> 644 bytes agc/images/vdc_LB_parkcall_es.gif | Bin 0 -> 646 bytes agc/images/vdc_LB_pause.gif | Bin 0 -> 483 bytes agc/images/vdc_LB_pause_OFF.gif | Bin 0 -> 483 bytes agc/images/vdc_LB_pause_OFF_es.gif | Bin 0 -> 404 bytes agc/images/vdc_LB_pause_es.gif | Bin 0 -> 410 bytes agc/images/vdc_LB_resume.gif | Bin 0 -> 535 bytes agc/images/vdc_LB_resume_OFF.gif | Bin 0 -> 533 bytes agc/images/vdc_LB_resume_OFF_es.gif | Bin 0 -> 495 bytes agc/images/vdc_LB_resume_es.gif | Bin 0 -> 500 bytes agc/images/vdc_LB_senddtmf.gif | Bin 0 -> 567 bytes agc/images/vdc_LB_senddtmf_OFF.gif | Bin 0 -> 563 bytes agc/images/vdc_LB_senddtmf_OFF_es.gif | Bin 0 -> 563 bytes agc/images/vdc_LB_senddtmf_es.gif | Bin 0 -> 564 bytes agc/images/vdc_LB_spacer.gif | Bin 0 -> 90 bytes agc/images/vdc_LB_startrecording.gif | Bin 0 -> 778 bytes agc/images/vdc_LB_startrecording_OFF.gif | Bin 0 -> 778 bytes agc/images/vdc_LB_startrecording_OFF_es.gif | Bin 0 -> 788 bytes agc/images/vdc_LB_startrecording_es.gif | Bin 0 -> 788 bytes agc/images/vdc_LB_stoprecording.gif | Bin 0 -> 758 bytes agc/images/vdc_LB_stoprecording_OFF.gif | Bin 0 -> 757 bytes agc/images/vdc_LB_stoprecording_OFF_es.gif | Bin 0 -> 701 bytes agc/images/vdc_LB_stoprecording_es.gif | Bin 0 -> 699 bytes agc/images/vdc_LB_transferconf.gif | Bin 0 -> 732 bytes agc/images/vdc_LB_transferconf_OFF.gif | Bin 0 -> 732 bytes agc/images/vdc_LB_transferconf_OFF_es.gif | Bin 0 -> 712 bytes agc/images/vdc_LB_transferconf_es.gif | Bin 0 -> 714 bytes agc/images/vdc_LB_webform.gif | Bin 0 -> 542 bytes agc/images/vdc_LB_webform_OFF.gif | Bin 0 -> 615 bytes agc/images/vdc_LB_webform_OFF_es.gif | Bin 0 -> 661 bytes agc/images/vdc_LB_webform_es.gif | Bin 0 -> 583 bytes agc/images/vdc_XB_ammessage.gif | Bin 0 -> 396 bytes agc/images/vdc_XB_ammessage_OFF.gif | Bin 0 -> 403 bytes agc/images/vdc_XB_blindtransfer.gif | Bin 0 -> 774 bytes agc/images/vdc_XB_blindtransfer_OFF.gif | Bin 0 -> 774 bytes agc/images/vdc_XB_blindtransfer_OFF_es.gif | Bin 0 -> 758 bytes agc/images/vdc_XB_blindtransfer_es.gif | Bin 0 -> 758 bytes agc/images/vdc_XB_channel.gif | Bin 0 -> 314 bytes agc/images/vdc_XB_channel_es.gif | Bin 0 -> 242 bytes agc/images/vdc_XB_code.gif | Bin 0 -> 247 bytes agc/images/vdc_XB_dialwithcustomer.gif | Bin 0 -> 881 bytes agc/images/vdc_XB_dialwithcustomer_OFF.gif | Bin 0 -> 881 bytes agc/images/vdc_XB_dialwithcustomer_OFF_es.gif | Bin 0 -> 776 bytes agc/images/vdc_XB_dialwithcustomer_es.gif | Bin 0 -> 776 bytes agc/images/vdc_XB_hangupbothlines.gif | Bin 0 -> 849 bytes agc/images/vdc_XB_hangupbothlines_OFF.gif | Bin 0 -> 849 bytes agc/images/vdc_XB_hangupbothlines_OFF_es.gif | Bin 0 -> 809 bytes agc/images/vdc_XB_hangupbothlines_es.gif | Bin 0 -> 809 bytes agc/images/vdc_XB_hangupxferline.gif | Bin 0 -> 803 bytes agc/images/vdc_XB_hangupxferline_OFF.gif | Bin 0 -> 803 bytes agc/images/vdc_XB_hangupxferline_OFF_es.gif | Bin 0 -> 702 bytes agc/images/vdc_XB_hangupxferline_es.gif | Bin 0 -> 702 bytes agc/images/vdc_XB_header.gif | Bin 0 -> 884 bytes agc/images/vdc_XB_header_es.gif | Bin 0 -> 639 bytes agc/images/vdc_XB_hotkeysactive.gif | Bin 0 -> 791 bytes agc/images/vdc_XB_hotkeysactive_OFF.gif | Bin 0 -> 786 bytes .../vdc_XB_hotkeysactive_OFF_es-orig.gif | Bin 0 -> 1231 bytes agc/images/vdc_XB_hotkeysactive_OFF_es.gif | Bin 0 -> 1256 bytes agc/images/vdc_XB_hotkeysactive_es.gif | Bin 0 -> 1231 bytes agc/images/vdc_XB_hotkeysinactive_es.gif | Bin 0 -> 1256 bytes agc/images/vdc_XB_internalcloser.gif | Bin 0 -> 801 bytes agc/images/vdc_XB_internalcloser_OFF.gif | Bin 0 -> 801 bytes agc/images/vdc_XB_internalcloser_OFF_es.gif | Bin 0 -> 640 bytes agc/images/vdc_XB_internalcloser_es.gif | Bin 0 -> 640 bytes agc/images/vdc_XB_leave3waycall.gif | Bin 0 -> 816 bytes agc/images/vdc_XB_leave3waycall_OFF.gif | Bin 0 -> 816 bytes agc/images/vdc_XB_leave3waycall_OFF_es.gif | Bin 0 -> 834 bytes agc/images/vdc_XB_leave3waycall_es.gif | Bin 0 -> 834 bytes agc/images/vdc_XB_localcloser.gif | Bin 0 -> 728 bytes agc/images/vdc_XB_localcloser_OFF.gif | Bin 0 -> 728 bytes agc/images/vdc_XB_localcloser_OFF_es.gif | Bin 0 -> 607 bytes agc/images/vdc_XB_localcloser_es.gif | Bin 0 -> 606 bytes agc/images/vdc_XB_number.gif | Bin 0 -> 480 bytes agc/images/vdc_XB_number_es.gif | Bin 0 -> 416 bytes agc/images/vdc_XB_parkcustomerdial.gif | Bin 0 -> 896 bytes agc/images/vdc_XB_parkcustomerdial_OFF.gif | Bin 0 -> 896 bytes agc/images/vdc_XB_parkcustomerdial_OFF_es.gif | Bin 0 -> 661 bytes agc/images/vdc_XB_parkcustomerdial_es.gif | Bin 0 -> 659 bytes agc/images/vdc_XB_seconds.gif | Bin 0 -> 352 bytes agc/images/vdc_XB_seconds_es.gif | Bin 0 -> 333 bytes agc/images/vdc_tab_script.gif | Bin 0 -> 825 bytes agc/images/vdc_tab_script_es.gif | Bin 0 -> 916 bytes agc/images/vdc_tab_vicidial.gif | Bin 0 -> 866 bytes agc/inbound_popup.php | 337 ++ agc/live_exten_check.php | 265 + agc/manager_send.php | 882 +++ agc/park_calls_display.php | 138 + agc/vdc_db_query.php | 1289 ++++ agc/vicidial.php | 4633 +++++++++++++++ agc/voicemail_check.php | 132 + agi-CIDtest.agi | 175 + agi-VDAD_LB_closer.agi | 525 ++ agi-VDAD_LB_closer_inbound.agi | 649 ++ agi-VDAD_LB_transfer.agi | 505 ++ agi-VDAD_LO_closer.agi | 610 ++ agi-VDAD_LO_closer_inbound.agi | 739 +++ agi-VDAD_LO_transfer.agi | 579 ++ agi-VDADautoREMINDER.agi | 610 ++ agi-VDADcloser.agi | 500 ++ agi-VDADcloser_PHONE.agi | 535 ++ agi-VDADcloser_inbound.agi | 633 ++ agi-VDADcloser_inboundANI.agi | 610 ++ agi-VDADcloser_inboundCID.agi | 626 ++ agi-VDADcloser_inboundCIDlookup.agi | 639 ++ agi-VDADcloser_inbound_5ID.agi | 633 ++ agi-VDADcloser_inbound_NOCID.agi | 560 ++ agi-VDADtransfer.agi | 474 ++ agi-VDADtransferSURVEY.agi | 623 ++ agi-dtmf.agi | 98 + astGUIclient_1.1.0.pl | 4793 +++++++++++++++ astLEADLOADER.pl | 1654 ++++++ astVICIDIAL_1.1.pl | 4710 +++++++++++++++ astguiclient_web/AST_inboundEXTstats.php | 370 ++ astguiclient_web/admin.php | 1626 +++++ astguiclient_web/dbconnect.php | 14 + astguiclient_web/help.gif | Bin 0 -> 492 bytes astguiclient_web/inbound_popup.php | 191 + astguiclient_web/phone_stats.php | 227 + astguiclient_web/remote_inbound.php | 162 + astguiclient_web/test.php | 30 + call_inbound.agi | 235 + call_log.agi | 323 + call_logCID.agi | 302 + call_park.agi | 224 + call_park_EXT.agi | 223 + call_park_I.agi | 230 + call_park_L.agi | 307 + call_park_W.agi | 307 + debug_speak.agi | 123 + docs/ACQS.txt | 94 + docs/CONF_Asterisk.txt | 9 + docs/CONF_MySQL.txt | 17 + docs/CONF_VICIDIAL_MySQL.txt | 12 + docs/LICENSE.txt | 278 + docs/LOAD_BALANCING.txt | 117 + docs/PROCESS_IDEA_CALLBACKS.txt | 87 + docs/README.txt | 667 +++ docs/REQUIREMENTS.txt | 30 + docs/SCRATCH_INSTALL.txt | 2545 ++++++++ docs/TODO.txt | 137 + docs/VICIDIAL.txt | 162 + install_server_files.pl | 449 ++ invalid_speak.agi | 139 + languages.txt | 373 ++ languages_admin.txt | 839 +++ libs/AST_VICI_conf.pl | 146 + libs/AST_VICI_conf.pl.bak | 146 + libs/AUS_zipcode_ranges.txt | 14 + libs/Asterisk.pm | 20 + libs/Asterisk/AGI.pm | 685 +++ libs/Asterisk/Manager.pm | 412 ++ libs/Asterisk/Outgoing.pm | 110 + libs/Asterisk/QCall.pm | 61 + libs/Asterisk/Voicemail.pm | 261 + libs/MySQL.pm | 1015 ++++ libs/Net/HTTP.pm | 266 + libs/Net/HTTP/Methods.pm | 520 ++ libs/Net/HTTP/NB.pm | 106 + libs/Net/HTTPS.pm | 55 + libs/Net/MySQL.pm | 1015 ++++ libs/Net/SSH.pm | 255 + libs/Net/Telnet.pm | 5252 +++++++++++++++++ libs/Telnet.pm | 5252 +++++++++++++++++ libs/Time/HiRes.1.59.pm | 359 ++ libs/Time/HiRes.1.65.pm | 369 ++ libs/Time/HiRes.pm | 351 ++ libs/Time/Local.pm | 289 + libs/Time/Other_HiRes_versions/HiRes.1.55.pm | 351 ++ libs/Time/Other_HiRes_versions/HiRes.1.59.pm | 359 ++ libs/Time/Other_HiRes_versions/HiRes.1.65.pm | 369 ++ libs/Time/Other_HiRes_versions/HiRes.pm | 359 ++ libs/Time/gmtime.pm | 90 + libs/Time/localtime.pm | 86 + libs/Time/tm.pm | 33 + libs/US_zipcode_ranges.txt | 51 + libs/buttons.gif | Bin 0 -> 10160 bytes libs/vm_indicator_anim.gif | Bin 0 -> 311 bytes libs/voicemail_digits.gif | Bin 0 -> 1812 bytes libs/voicemail_new.gif | Bin 0 -> 240 bytes libs/voicemail_old.gif | Bin 0 -> 230 bytes libs/voicemail_up.gif | Bin 0 -> 520 bytes park_CID.agi | 164 + phone_codes_GMT.txt | 1011 ++++ sphinxNA.zip | Bin 0 -> 27727 bytes start_asterisk_boot.pl | 14 + test_VICIDIAL_lead_file.txt | 13 + upgrade_1.1.10.sql | 49 + 706 files changed, 235239 insertions(+) create mode 100644 ADMIN_adjust_GMTnow_on_leads.pl create mode 100644 ADMIN_area_code_populate.pl create mode 100644 ADMIN_keepalive_AST_VDautodial.pl create mode 100644 ADMIN_keepalive_AST_VDremote_agents.pl create mode 100644 ADMIN_keepalive_AST_send_listen.pl create mode 100644 ADMIN_keepalive_AST_update.pl create mode 100644 ADMIN_keepalive_send_listen.at create mode 100644 ADMIN_restart_roll_logs.pl create mode 100644 AST_CRON_mix_recordings.pl create mode 100644 AST_CRON_mix_recordings_BASIC.pl create mode 100644 AST_CRON_mix_recordings_GSM.pl create mode 100644 AST_DB_optimize.pl create mode 100644 AST_SERVER_conf.pl create mode 100644 AST_VDauto_dial.pl create mode 100644 AST_VDhopper.pl create mode 100644 AST_VDremote_agents.pl create mode 100644 AST_cleanup_agent_log.pl create mode 100644 AST_conf_update.pl create mode 100644 AST_flush_DBqueue.pl create mode 100644 AST_manager_kill_hung_congested.pl create mode 100644 AST_manager_listen.pl create mode 100644 AST_manager_send.pl create mode 100644 AST_reset_mysql_vars.pl create mode 100644 AST_send_action_child.pl create mode 100644 AST_update.pl create mode 100644 AST_vm_update.pl create mode 100644 DTMF_sounds/0.wav create mode 100644 DTMF_sounds/1.wav create mode 100644 DTMF_sounds/2.wav create mode 100644 DTMF_sounds/3.wav create mode 100644 DTMF_sounds/4.wav create mode 100644 DTMF_sounds/5.wav create mode 100644 DTMF_sounds/6.wav create mode 100644 DTMF_sounds/7.wav create mode 100644 DTMF_sounds/8.wav create mode 100644 DTMF_sounds/9.wav create mode 100644 DTMF_sounds/US_pol_survey_hello.gsm create mode 100644 DTMF_sounds/US_pol_survey_transfer.gsm create mode 100644 DTMF_sounds/US_reminder_callback.gsm create mode 100644 DTMF_sounds/US_reminder_goodbye.gsm create mode 100644 DTMF_sounds/US_reminder_message.gsm create mode 100644 DTMF_sounds/US_reminder_options.gsm create mode 100644 DTMF_sounds/US_thanks_no_contact.gsm create mode 100644 DTMF_sounds/four_digit_id.gsm create mode 100644 DTMF_sounds/generic_hold.gsm create mode 100644 DTMF_sounds/hash.wav create mode 100644 DTMF_sounds/hold_tone.gsm create mode 100644 DTMF_sounds/silence.wav create mode 100644 DTMF_sounds/star.wav create mode 100644 LANG_admin.zip create mode 100644 LANG_admin/LANG_admin/astguiclient/AST_inboundEXTstats.php create mode 100644 LANG_admin/LANG_admin/astguiclient/admin.php create mode 100644 LANG_admin/LANG_admin/astguiclient/dbconnect.php create mode 100644 LANG_admin/LANG_admin/astguiclient/help.gif create mode 100644 LANG_admin/LANG_admin/astguiclient/htglobalize.php create mode 100644 LANG_admin/LANG_admin/astguiclient/inbound_popup.php create mode 100644 LANG_admin/LANG_admin/astguiclient/phone_stats.php create mode 100644 LANG_admin/LANG_admin/astguiclient/remote_inbound.php create mode 100644 LANG_admin/LANG_admin/astguiclient_el/AST_inboundEXTstats.php create mode 100644 LANG_admin/LANG_admin/astguiclient_el/admin.php create mode 100644 LANG_admin/LANG_admin/astguiclient_el/dbconnect.php create mode 100644 LANG_admin/LANG_admin/astguiclient_el/help.gif create mode 100644 LANG_admin/LANG_admin/astguiclient_el/htglobalize.php create mode 100644 LANG_admin/LANG_admin/astguiclient_el/inbound_popup.php create mode 100644 LANG_admin/LANG_admin/astguiclient_el/phone_stats.php create mode 100644 LANG_admin/LANG_admin/astguiclient_el/remote_inbound.php create mode 100644 LANG_admin/LANG_admin/astguiclient_en/AST_inboundEXTstats.php create mode 100644 LANG_admin/LANG_admin/astguiclient_en/admin.php create mode 100644 LANG_admin/LANG_admin/astguiclient_en/dbconnect.php create mode 100644 LANG_admin/LANG_admin/astguiclient_en/help.gif create mode 100644 LANG_admin/LANG_admin/astguiclient_en/htglobalize.php create mode 100644 LANG_admin/LANG_admin/astguiclient_en/inbound_popup.php create mode 100644 LANG_admin/LANG_admin/astguiclient_en/phone_stats.php create mode 100644 LANG_admin/LANG_admin/astguiclient_en/remote_inbound.php create mode 100644 LANG_admin/LANG_admin/astguiclient_es/AST_inboundEXTstats.php create mode 100644 LANG_admin/LANG_admin/astguiclient_es/admin.php create mode 100644 LANG_admin/LANG_admin/astguiclient_es/dbconnect.php create mode 100644 LANG_admin/LANG_admin/astguiclient_es/help.gif create mode 100644 LANG_admin/LANG_admin/astguiclient_es/htglobalize.php create mode 100644 LANG_admin/LANG_admin/astguiclient_es/inbound_popup.php create mode 100644 LANG_admin/LANG_admin/astguiclient_es/phone_stats.php create mode 100644 LANG_admin/LANG_admin/astguiclient_es/remote_inbound.php create mode 100644 LANG_admin/LANG_admin/install_server_files.pl create mode 100644 LANG_admin/LANG_admin/languages.txt create mode 100644 LANG_admin/LANG_admin/languages_admin.txt create mode 100644 LANG_admin/LANG_admin/vicidial/AST_CLOSERstats.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_VDADstats.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_VICIDIAL_hopperlist.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_admin_log_display.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_agent_performance.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_agent_performance_detail.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_agent_time_sheet.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_parkstats.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_server_performance.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_timeonVDAD.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_timeonVDAD_closer.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_timeonVDADall.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_timeoncall.php create mode 100644 LANG_admin/LANG_admin/vicidial/AST_timeonpark.php create mode 100644 LANG_admin/LANG_admin/vicidial/admin-old.php create mode 100644 LANG_admin/LANG_admin/vicidial/admin.php create mode 100644 LANG_admin/LANG_admin/vicidial/admin_modify_lead.php create mode 100644 LANG_admin/LANG_admin/vicidial/admin_search_lead.php create mode 100644 LANG_admin/LANG_admin/vicidial/closer-fronter_popup.php create mode 100644 LANG_admin/LANG_admin/vicidial/closer-fronter_popup2.php create mode 100644 LANG_admin/LANG_admin/vicidial/closer.php create mode 100644 LANG_admin/LANG_admin/vicidial/closer_dispo.php create mode 100644 LANG_admin/LANG_admin/vicidial/closer_popup.php create mode 100644 LANG_admin/LANG_admin/vicidial/count.htm create mode 100644 LANG_admin/LANG_admin/vicidial/dbconnect.php create mode 100644 LANG_admin/LANG_admin/vicidial/group_hourly_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial/help.gif create mode 100644 LANG_admin/LANG_admin/vicidial/htglobalize.php create mode 100644 LANG_admin/LANG_admin/vicidial/listloader.php create mode 100644 LANG_admin/LANG_admin/vicidial/listloader.pl create mode 100644 LANG_admin/LANG_admin/vicidial/listloaderMAIN.php create mode 100644 LANG_admin/LANG_admin/vicidial/listloader_rowdisplay.pl create mode 100644 LANG_admin/LANG_admin/vicidial/listloader_super.pl create mode 100644 LANG_admin/LANG_admin/vicidial/log_test.php create mode 100644 LANG_admin/LANG_admin/vicidial/new_listloader_superL.php create mode 100644 LANG_admin/LANG_admin/vicidial/record_conf_1_hour.php create mode 100644 LANG_admin/LANG_admin/vicidial/remote_dispo.php create mode 100644 LANG_admin/LANG_admin/vicidial/server_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial/user_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial/user_status.php create mode 100644 LANG_admin/LANG_admin/vicidial/vdremote.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_CLOSERstats.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_VDADstats.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_VICIDIAL_hopperlist.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_admin_log_display.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_agent_performance.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_agent_performance_detail.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_agent_time_sheet.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_server_performance.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_timeonVDAD.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/AST_timeonVDAD_closer.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/admin.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/admin_modify_lead.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/admin_search_lead.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/count.htm create mode 100644 LANG_admin/LANG_admin/vicidial_el/dbconnect.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/group_hourly_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/help.gif create mode 100644 LANG_admin/LANG_admin/vicidial_el/htglobalize.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/listloader.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/listloaderMAIN.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/new_listloader_superL.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/remote_dispo.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/server_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/user_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/user_status.php create mode 100644 LANG_admin/LANG_admin/vicidial_el/vdremote.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_CLOSERstats.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_VDADstats.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_VICIDIAL_hopperlist.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_admin_log_display.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_agent_performance.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_agent_performance_detail.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_agent_time_sheet.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_server_performance.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_timeonVDAD.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/AST_timeonVDAD_closer.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/admin.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/admin_modify_lead.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/admin_search_lead.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/count.htm create mode 100644 LANG_admin/LANG_admin/vicidial_en/dbconnect.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/group_hourly_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/help.gif create mode 100644 LANG_admin/LANG_admin/vicidial_en/htglobalize.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/listloader.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/listloaderMAIN.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/new_listloader_superL.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/remote_dispo.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/server_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/user_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/user_status.php create mode 100644 LANG_admin/LANG_admin/vicidial_en/vdremote.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_CLOSERstats.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_VDADstats.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_VICIDIAL_hopperlist.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_admin_log_display.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_agent_performance.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_agent_performance_detail.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_agent_time_sheet.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_server_performance.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_timeonVDAD.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/AST_timeonVDAD_closer.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/admin.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/admin_modify_lead.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/admin_search_lead.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/count.htm create mode 100644 LANG_admin/LANG_admin/vicidial_es/dbconnect.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/group_hourly_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/help.gif create mode 100644 LANG_admin/LANG_admin/vicidial_es/htglobalize.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/listloader.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/listloaderMAIN.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/new_listloader_superL.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/remote_dispo.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/server_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/user_stats.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/user_status.php create mode 100644 LANG_admin/LANG_admin/vicidial_es/vdremote.php create mode 100644 LANG_agc.zip create mode 100644 LANG_agc/LANG_agc/agc/active_list_refresh.php create mode 100644 LANG_agc/LANG_agc/agc/astguiclient.php create mode 100644 LANG_agc/LANG_agc/agc/call_log_display.php create mode 100644 LANG_agc/LANG_agc/agc/conf_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc/dbconnect.php create mode 100644 LANG_agc/LANG_agc/agc/images/agc_check_voicemail_BLINK.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_check_voicemail_BLINK_e.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_check_voicemail_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_check_voicemail_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_check_voicemail_ON.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_check_voicemail_ON_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_live_call_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_live_call_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_live_call_ON.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_live_call_ON_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_tab_active_lines.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_tab_active_lines_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_tab_astguiclient.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_tab_conferences.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_tab_conferences_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_tab_main.gif create mode 100644 LANG_agc/LANG_agc/agc/images/agc_tab_main_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/blank.gif create mode 100644 LANG_agc/LANG_agc/agc/images/de.gif create mode 100644 LANG_agc/LANG_agc/agc/images/down.gif create mode 100644 LANG_agc/LANG_agc/agc/images/el.gif create mode 100644 LANG_agc/LANG_agc/agc/images/en.gif create mode 100644 LANG_agc/LANG_agc/agc/images/es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/fr.gif create mode 100644 LANG_agc/LANG_agc/agc/images/it.gif create mode 100644 LANG_agc/LANG_agc/agc/images/pt.gif create mode 100644 LANG_agc/LANG_agc/agc/images/remove.gif create mode 100644 LANG_agc/LANG_agc/agc/images/up.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_dialnextnumber.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_dialnextnumber_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_dialnextnumber_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_dialnextnumber_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_grabparkedcall.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_grabparkedcall_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_grabparkedcall_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_grabparkedcall_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_hangupcustomer.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_hangupcustomer_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_hangupcustomer_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_hangupcustomer_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_parkcall.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_parkcall_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_parkcall_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_parkcall_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_pause.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_pause_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_pause_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_pause_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_resume.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_resume_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_resume_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_resume_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_senddtmf.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_senddtmf_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_senddtmf_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_senddtmf_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_spacer.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_startrecording.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_startrecording_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_startrecording_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_startrecording_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_stoprecording.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_stoprecording_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_stoprecording_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_stoprecording_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_transferconf.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_transferconf_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_transferconf_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_transferconf_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_webform.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_webform_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_webform_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_LB_webform_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_ammessage.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_ammessage_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_blindtransfer.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_blindtransfer_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_blindtransfer_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_blindtransfer_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_channel.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_channel_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_code.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_dialwithcustomer.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_dialwithcustomer_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_dialwithcustomer_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_dialwithcustomer_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hangupbothlines.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hangupbothlines_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hangupbothlines_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hangupbothlines_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hangupxferline.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hangupxferline_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hangupxferline_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hangupxferline_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_header.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_header_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hotkeysactive.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hotkeysactive_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hotkeysactive_OFF_es-orig.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hotkeysactive_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hotkeysactive_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_hotkeysinactive_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_internalcloser.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_internalcloser_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_internalcloser_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_internalcloser_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_leave3waycall.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_leave3waycall_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_leave3waycall_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_leave3waycall_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_localcloser.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_localcloser_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_localcloser_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_localcloser_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_number.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_number_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_parkcustomerdial.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_parkcustomerdial_OFF.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_parkcustomerdial_OFF_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_parkcustomerdial_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_seconds.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_XB_seconds_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_tab_script.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_tab_script_es.gif create mode 100644 LANG_agc/LANG_agc/agc/images/vdc_tab_vicidial.gif create mode 100644 LANG_agc/LANG_agc/agc/inbound_popup.php create mode 100644 LANG_agc/LANG_agc/agc/live_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc/manager_send.php create mode 100644 LANG_agc/LANG_agc/agc/park_calls_display.php create mode 100644 LANG_agc/LANG_agc/agc/vdc_db_query.php create mode 100644 LANG_agc/LANG_agc/agc/vicidial.php create mode 100644 LANG_agc/LANG_agc/agc/voicemail_check.php create mode 100644 LANG_agc/LANG_agc/agc_de/active_list_refresh.php create mode 100644 LANG_agc/LANG_agc/agc_de/astguiclient.php create mode 100644 LANG_agc/LANG_agc/agc_de/call_log_display.php create mode 100644 LANG_agc/LANG_agc/agc_de/conf_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_de/dbconnect.php create mode 100644 LANG_agc/LANG_agc/agc_de/inbound_popup.php create mode 100644 LANG_agc/LANG_agc/agc_de/live_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_de/manager_send.php create mode 100644 LANG_agc/LANG_agc/agc_de/park_calls_display.php create mode 100644 LANG_agc/LANG_agc/agc_de/vdc_db_query.php create mode 100644 LANG_agc/LANG_agc/agc_de/vicidial.php create mode 100644 LANG_agc/LANG_agc/agc_de/voicemail_check.php create mode 100644 LANG_agc/LANG_agc/agc_el/active_list_refresh.php create mode 100644 LANG_agc/LANG_agc/agc_el/astguiclient.php create mode 100644 LANG_agc/LANG_agc/agc_el/call_log_display.php create mode 100644 LANG_agc/LANG_agc/agc_el/conf_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_el/dbconnect.php create mode 100644 LANG_agc/LANG_agc/agc_el/inbound_popup.php create mode 100644 LANG_agc/LANG_agc/agc_el/live_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_el/manager_send.php create mode 100644 LANG_agc/LANG_agc/agc_el/park_calls_display.php create mode 100644 LANG_agc/LANG_agc/agc_el/vdc_db_query.php create mode 100644 LANG_agc/LANG_agc/agc_el/vicidial.php create mode 100644 LANG_agc/LANG_agc/agc_el/voicemail_check.php create mode 100644 LANG_agc/LANG_agc/agc_en/active_list_refresh.php create mode 100644 LANG_agc/LANG_agc/agc_en/astguiclient.php create mode 100644 LANG_agc/LANG_agc/agc_en/call_log_display.php create mode 100644 LANG_agc/LANG_agc/agc_en/conf_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_en/dbconnect.php create mode 100644 LANG_agc/LANG_agc/agc_en/inbound_popup.php create mode 100644 LANG_agc/LANG_agc/agc_en/live_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_en/manager_send.php create mode 100644 LANG_agc/LANG_agc/agc_en/park_calls_display.php create mode 100644 LANG_agc/LANG_agc/agc_en/vdc_db_query.php create mode 100644 LANG_agc/LANG_agc/agc_en/vicidial.php create mode 100644 LANG_agc/LANG_agc/agc_en/voicemail_check.php create mode 100644 LANG_agc/LANG_agc/agc_es/active_list_refresh.php create mode 100644 LANG_agc/LANG_agc/agc_es/astguiclient.php create mode 100644 LANG_agc/LANG_agc/agc_es/call_log_display.php create mode 100644 LANG_agc/LANG_agc/agc_es/conf_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_es/dbconnect.php create mode 100644 LANG_agc/LANG_agc/agc_es/inbound_popup.php create mode 100644 LANG_agc/LANG_agc/agc_es/live_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_es/manager_send.php create mode 100644 LANG_agc/LANG_agc/agc_es/park_calls_display.php create mode 100644 LANG_agc/LANG_agc/agc_es/vdc_db_query.php create mode 100644 LANG_agc/LANG_agc/agc_es/vicidial.php create mode 100644 LANG_agc/LANG_agc/agc_es/voicemail_check.php create mode 100644 LANG_agc/LANG_agc/agc_fr/active_list_refresh.php create mode 100644 LANG_agc/LANG_agc/agc_fr/astguiclient.php create mode 100644 LANG_agc/LANG_agc/agc_fr/call_log_display.php create mode 100644 LANG_agc/LANG_agc/agc_fr/conf_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_fr/dbconnect.php create mode 100644 LANG_agc/LANG_agc/agc_fr/htglobalize.php create mode 100644 LANG_agc/LANG_agc/agc_fr/inbound_popup.php create mode 100644 LANG_agc/LANG_agc/agc_fr/live_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_fr/manager_send.php create mode 100644 LANG_agc/LANG_agc/agc_fr/park_calls_display.php create mode 100644 LANG_agc/LANG_agc/agc_fr/vdc_db_query.php create mode 100644 LANG_agc/LANG_agc/agc_fr/vicidial.php create mode 100644 LANG_agc/LANG_agc/agc_fr/voicemail_check.php create mode 100644 LANG_agc/LANG_agc/agc_it/active_list_refresh.php create mode 100644 LANG_agc/LANG_agc/agc_it/astguiclient.php create mode 100644 LANG_agc/LANG_agc/agc_it/call_log_display.php create mode 100644 LANG_agc/LANG_agc/agc_it/conf_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_it/dbconnect.php create mode 100644 LANG_agc/LANG_agc/agc_it/inbound_popup.php create mode 100644 LANG_agc/LANG_agc/agc_it/live_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_it/manager_send.php create mode 100644 LANG_agc/LANG_agc/agc_it/park_calls_display.php create mode 100644 LANG_agc/LANG_agc/agc_it/vdc_db_query.php create mode 100644 LANG_agc/LANG_agc/agc_it/vicidial.php create mode 100644 LANG_agc/LANG_agc/agc_it/voicemail_check.php create mode 100644 LANG_agc/LANG_agc/agc_pt/active_list_refresh.php create mode 100644 LANG_agc/LANG_agc/agc_pt/astguiclient.php create mode 100644 LANG_agc/LANG_agc/agc_pt/call_log_display.php create mode 100644 LANG_agc/LANG_agc/agc_pt/conf_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_pt/dbconnect.php create mode 100644 LANG_agc/LANG_agc/agc_pt/inbound_popup.php create mode 100644 LANG_agc/LANG_agc/agc_pt/live_exten_check.php create mode 100644 LANG_agc/LANG_agc/agc_pt/manager_send.php create mode 100644 LANG_agc/LANG_agc/agc_pt/park_calls_display.php create mode 100644 LANG_agc/LANG_agc/agc_pt/vdc_db_query.php create mode 100644 LANG_agc/LANG_agc/agc_pt/vicidial.php create mode 100644 LANG_agc/LANG_agc/agc_pt/voicemail_check.php create mode 100644 MySQL_AST_CREATE_tables.sql create mode 100644 VD_amd.agi create mode 100644 VD_amd_post.agi create mode 100644 VD_hangup.agi create mode 100644 VICIDIAL_IN_new_leads_file.pl create mode 100644 VICIDIAL_web/AST_CLOSERstats.php create mode 100644 VICIDIAL_web/AST_VDADstats.php create mode 100644 VICIDIAL_web/AST_VICIDIAL_hopperlist.php create mode 100644 VICIDIAL_web/AST_admin_log_display.php create mode 100644 VICIDIAL_web/AST_agent_performance.php create mode 100644 VICIDIAL_web/AST_agent_performance_detail.php create mode 100644 VICIDIAL_web/AST_agent_time_sheet.php create mode 100644 VICIDIAL_web/AST_parkstats.php create mode 100644 VICIDIAL_web/AST_server_performance.php create mode 100644 VICIDIAL_web/AST_timeonVDAD.php create mode 100644 VICIDIAL_web/AST_timeonVDAD_closer.php create mode 100644 VICIDIAL_web/AST_timeonVDADall.php create mode 100644 VICIDIAL_web/AST_timeoncall.php create mode 100644 VICIDIAL_web/AST_timeonpark.php create mode 100644 VICIDIAL_web/admin.php create mode 100644 VICIDIAL_web/admin_modify_lead.php create mode 100644 VICIDIAL_web/admin_search_lead.php create mode 100644 VICIDIAL_web/closer-fronter_popup.php create mode 100644 VICIDIAL_web/closer-fronter_popup2.php create mode 100644 VICIDIAL_web/closer.php create mode 100644 VICIDIAL_web/closer_dispo.php create mode 100644 VICIDIAL_web/closer_popup.php create mode 100644 VICIDIAL_web/count.htm create mode 100644 VICIDIAL_web/dbconnect.php create mode 100644 VICIDIAL_web/group_hourly_stats.php create mode 100644 VICIDIAL_web/help.gif create mode 100644 VICIDIAL_web/listloader.php create mode 100644 VICIDIAL_web/listloader.pl create mode 100644 VICIDIAL_web/listloaderMAIN.php create mode 100644 VICIDIAL_web/listloader_rowdisplay.pl create mode 100644 VICIDIAL_web/listloader_super.pl create mode 100644 VICIDIAL_web/log_test.php create mode 100644 VICIDIAL_web/new_listloader_superL.php create mode 100644 VICIDIAL_web/record_conf_1_hour.php create mode 100644 VICIDIAL_web/remote_dispo.php create mode 100644 VICIDIAL_web/server_stats.php create mode 100644 VICIDIAL_web/test.txt create mode 100644 VICIDIAL_web/user_stats.php create mode 100644 VICIDIAL_web/user_status.php create mode 100644 VICIDIAL_web/vdremote.php create mode 100644 agc/active_list_refresh.php create mode 100644 agc/astguiclient.php create mode 100644 agc/call_log_display.php create mode 100644 agc/conf_exten_check.php create mode 100644 agc/dbconnect.php create mode 100644 agc/images/agc_check_voicemail_BLINK.gif create mode 100644 agc/images/agc_check_voicemail_BLINK_es.gif create mode 100644 agc/images/agc_check_voicemail_OFF.gif create mode 100644 agc/images/agc_check_voicemail_OFF_es.gif create mode 100644 agc/images/agc_check_voicemail_ON.gif create mode 100644 agc/images/agc_check_voicemail_ON_es.gif create mode 100644 agc/images/agc_live_call_OFF.gif create mode 100644 agc/images/agc_live_call_OFF_es.gif create mode 100644 agc/images/agc_live_call_ON.gif create mode 100644 agc/images/agc_live_call_ON_es.gif create mode 100644 agc/images/agc_tab_active_lines.gif create mode 100644 agc/images/agc_tab_active_lines_es.gif create mode 100644 agc/images/agc_tab_astguiclient.gif create mode 100644 agc/images/agc_tab_conferences.gif create mode 100644 agc/images/agc_tab_conferences_es.gif create mode 100644 agc/images/agc_tab_main.gif create mode 100644 agc/images/agc_tab_main_es.gif create mode 100644 agc/images/blank.gif create mode 100644 agc/images/de.gif create mode 100644 agc/images/down.gif create mode 100644 agc/images/el.gif create mode 100644 agc/images/en.gif create mode 100644 agc/images/es.gif create mode 100644 agc/images/fr.gif create mode 100644 agc/images/it.gif create mode 100644 agc/images/pt.gif create mode 100644 agc/images/remove.gif create mode 100644 agc/images/up.gif create mode 100644 agc/images/vdc_LB_dialnextnumber.gif create mode 100644 agc/images/vdc_LB_dialnextnumber_OFF.gif create mode 100644 agc/images/vdc_LB_dialnextnumber_OFF_es.gif create mode 100644 agc/images/vdc_LB_dialnextnumber_es.gif create mode 100644 agc/images/vdc_LB_grabparkedcall.gif create mode 100644 agc/images/vdc_LB_grabparkedcall_OFF.gif create mode 100644 agc/images/vdc_LB_grabparkedcall_OFF_es.gif create mode 100644 agc/images/vdc_LB_grabparkedcall_es.gif create mode 100644 agc/images/vdc_LB_hangupcustomer.gif create mode 100644 agc/images/vdc_LB_hangupcustomer_OFF.gif create mode 100644 agc/images/vdc_LB_hangupcustomer_OFF_es.gif create mode 100644 agc/images/vdc_LB_hangupcustomer_es.gif create mode 100644 agc/images/vdc_LB_parkcall.gif create mode 100644 agc/images/vdc_LB_parkcall_OFF.gif create mode 100644 agc/images/vdc_LB_parkcall_OFF_es.gif create mode 100644 agc/images/vdc_LB_parkcall_es.gif create mode 100644 agc/images/vdc_LB_pause.gif create mode 100644 agc/images/vdc_LB_pause_OFF.gif create mode 100644 agc/images/vdc_LB_pause_OFF_es.gif create mode 100644 agc/images/vdc_LB_pause_es.gif create mode 100644 agc/images/vdc_LB_resume.gif create mode 100644 agc/images/vdc_LB_resume_OFF.gif create mode 100644 agc/images/vdc_LB_resume_OFF_es.gif create mode 100644 agc/images/vdc_LB_resume_es.gif create mode 100644 agc/images/vdc_LB_senddtmf.gif create mode 100644 agc/images/vdc_LB_senddtmf_OFF.gif create mode 100644 agc/images/vdc_LB_senddtmf_OFF_es.gif create mode 100644 agc/images/vdc_LB_senddtmf_es.gif create mode 100644 agc/images/vdc_LB_spacer.gif create mode 100644 agc/images/vdc_LB_startrecording.gif create mode 100644 agc/images/vdc_LB_startrecording_OFF.gif create mode 100644 agc/images/vdc_LB_startrecording_OFF_es.gif create mode 100644 agc/images/vdc_LB_startrecording_es.gif create mode 100644 agc/images/vdc_LB_stoprecording.gif create mode 100644 agc/images/vdc_LB_stoprecording_OFF.gif create mode 100644 agc/images/vdc_LB_stoprecording_OFF_es.gif create mode 100644 agc/images/vdc_LB_stoprecording_es.gif create mode 100644 agc/images/vdc_LB_transferconf.gif create mode 100644 agc/images/vdc_LB_transferconf_OFF.gif create mode 100644 agc/images/vdc_LB_transferconf_OFF_es.gif create mode 100644 agc/images/vdc_LB_transferconf_es.gif create mode 100644 agc/images/vdc_LB_webform.gif create mode 100644 agc/images/vdc_LB_webform_OFF.gif create mode 100644 agc/images/vdc_LB_webform_OFF_es.gif create mode 100644 agc/images/vdc_LB_webform_es.gif create mode 100644 agc/images/vdc_XB_ammessage.gif create mode 100644 agc/images/vdc_XB_ammessage_OFF.gif create mode 100644 agc/images/vdc_XB_blindtransfer.gif create mode 100644 agc/images/vdc_XB_blindtransfer_OFF.gif create mode 100644 agc/images/vdc_XB_blindtransfer_OFF_es.gif create mode 100644 agc/images/vdc_XB_blindtransfer_es.gif create mode 100644 agc/images/vdc_XB_channel.gif create mode 100644 agc/images/vdc_XB_channel_es.gif create mode 100644 agc/images/vdc_XB_code.gif create mode 100644 agc/images/vdc_XB_dialwithcustomer.gif create mode 100644 agc/images/vdc_XB_dialwithcustomer_OFF.gif create mode 100644 agc/images/vdc_XB_dialwithcustomer_OFF_es.gif create mode 100644 agc/images/vdc_XB_dialwithcustomer_es.gif create mode 100644 agc/images/vdc_XB_hangupbothlines.gif create mode 100644 agc/images/vdc_XB_hangupbothlines_OFF.gif create mode 100644 agc/images/vdc_XB_hangupbothlines_OFF_es.gif create mode 100644 agc/images/vdc_XB_hangupbothlines_es.gif create mode 100644 agc/images/vdc_XB_hangupxferline.gif create mode 100644 agc/images/vdc_XB_hangupxferline_OFF.gif create mode 100644 agc/images/vdc_XB_hangupxferline_OFF_es.gif create mode 100644 agc/images/vdc_XB_hangupxferline_es.gif create mode 100644 agc/images/vdc_XB_header.gif create mode 100644 agc/images/vdc_XB_header_es.gif create mode 100644 agc/images/vdc_XB_hotkeysactive.gif create mode 100644 agc/images/vdc_XB_hotkeysactive_OFF.gif create mode 100644 agc/images/vdc_XB_hotkeysactive_OFF_es-orig.gif create mode 100644 agc/images/vdc_XB_hotkeysactive_OFF_es.gif create mode 100644 agc/images/vdc_XB_hotkeysactive_es.gif create mode 100644 agc/images/vdc_XB_hotkeysinactive_es.gif create mode 100644 agc/images/vdc_XB_internalcloser.gif create mode 100644 agc/images/vdc_XB_internalcloser_OFF.gif create mode 100644 agc/images/vdc_XB_internalcloser_OFF_es.gif create mode 100644 agc/images/vdc_XB_internalcloser_es.gif create mode 100644 agc/images/vdc_XB_leave3waycall.gif create mode 100644 agc/images/vdc_XB_leave3waycall_OFF.gif create mode 100644 agc/images/vdc_XB_leave3waycall_OFF_es.gif create mode 100644 agc/images/vdc_XB_leave3waycall_es.gif create mode 100644 agc/images/vdc_XB_localcloser.gif create mode 100644 agc/images/vdc_XB_localcloser_OFF.gif create mode 100644 agc/images/vdc_XB_localcloser_OFF_es.gif create mode 100644 agc/images/vdc_XB_localcloser_es.gif create mode 100644 agc/images/vdc_XB_number.gif create mode 100644 agc/images/vdc_XB_number_es.gif create mode 100644 agc/images/vdc_XB_parkcustomerdial.gif create mode 100644 agc/images/vdc_XB_parkcustomerdial_OFF.gif create mode 100644 agc/images/vdc_XB_parkcustomerdial_OFF_es.gif create mode 100644 agc/images/vdc_XB_parkcustomerdial_es.gif create mode 100644 agc/images/vdc_XB_seconds.gif create mode 100644 agc/images/vdc_XB_seconds_es.gif create mode 100644 agc/images/vdc_tab_script.gif create mode 100644 agc/images/vdc_tab_script_es.gif create mode 100644 agc/images/vdc_tab_vicidial.gif create mode 100644 agc/inbound_popup.php create mode 100644 agc/live_exten_check.php create mode 100644 agc/manager_send.php create mode 100644 agc/park_calls_display.php create mode 100644 agc/vdc_db_query.php create mode 100644 agc/vicidial.php create mode 100644 agc/voicemail_check.php create mode 100644 agi-CIDtest.agi create mode 100644 agi-VDAD_LB_closer.agi create mode 100644 agi-VDAD_LB_closer_inbound.agi create mode 100644 agi-VDAD_LB_transfer.agi create mode 100644 agi-VDAD_LO_closer.agi create mode 100644 agi-VDAD_LO_closer_inbound.agi create mode 100644 agi-VDAD_LO_transfer.agi create mode 100644 agi-VDADautoREMINDER.agi create mode 100644 agi-VDADcloser.agi create mode 100644 agi-VDADcloser_PHONE.agi create mode 100644 agi-VDADcloser_inbound.agi create mode 100644 agi-VDADcloser_inboundANI.agi create mode 100644 agi-VDADcloser_inboundCID.agi create mode 100644 agi-VDADcloser_inboundCIDlookup.agi create mode 100644 agi-VDADcloser_inbound_5ID.agi create mode 100644 agi-VDADcloser_inbound_NOCID.agi create mode 100644 agi-VDADtransfer.agi create mode 100644 agi-VDADtransferSURVEY.agi create mode 100644 agi-dtmf.agi create mode 100644 astGUIclient_1.1.0.pl create mode 100644 astLEADLOADER.pl create mode 100644 astVICIDIAL_1.1.pl create mode 100644 astguiclient_web/AST_inboundEXTstats.php create mode 100644 astguiclient_web/admin.php create mode 100644 astguiclient_web/dbconnect.php create mode 100644 astguiclient_web/help.gif create mode 100644 astguiclient_web/inbound_popup.php create mode 100644 astguiclient_web/phone_stats.php create mode 100644 astguiclient_web/remote_inbound.php create mode 100644 astguiclient_web/test.php create mode 100644 call_inbound.agi create mode 100644 call_log.agi create mode 100644 call_logCID.agi create mode 100644 call_park.agi create mode 100644 call_park_EXT.agi create mode 100644 call_park_I.agi create mode 100644 call_park_L.agi create mode 100644 call_park_W.agi create mode 100644 debug_speak.agi create mode 100644 docs/ACQS.txt create mode 100644 docs/CONF_Asterisk.txt create mode 100644 docs/CONF_MySQL.txt create mode 100644 docs/CONF_VICIDIAL_MySQL.txt create mode 100644 docs/LICENSE.txt create mode 100644 docs/LOAD_BALANCING.txt create mode 100644 docs/PROCESS_IDEA_CALLBACKS.txt create mode 100644 docs/README.txt create mode 100644 docs/REQUIREMENTS.txt create mode 100644 docs/SCRATCH_INSTALL.txt create mode 100644 docs/TODO.txt create mode 100644 docs/VICIDIAL.txt create mode 100644 install_server_files.pl create mode 100644 invalid_speak.agi create mode 100644 languages.txt create mode 100644 languages_admin.txt create mode 100644 libs/AST_VICI_conf.pl create mode 100644 libs/AST_VICI_conf.pl.bak create mode 100644 libs/AUS_zipcode_ranges.txt create mode 100644 libs/Asterisk.pm create mode 100644 libs/Asterisk/AGI.pm create mode 100644 libs/Asterisk/Manager.pm create mode 100644 libs/Asterisk/Outgoing.pm create mode 100644 libs/Asterisk/QCall.pm create mode 100644 libs/Asterisk/Voicemail.pm create mode 100644 libs/MySQL.pm create mode 100644 libs/Net/HTTP.pm create mode 100644 libs/Net/HTTP/Methods.pm create mode 100644 libs/Net/HTTP/NB.pm create mode 100644 libs/Net/HTTPS.pm create mode 100644 libs/Net/MySQL.pm create mode 100644 libs/Net/SSH.pm create mode 100644 libs/Net/Telnet.pm create mode 100644 libs/Telnet.pm create mode 100644 libs/Time/HiRes.1.59.pm create mode 100644 libs/Time/HiRes.1.65.pm create mode 100644 libs/Time/HiRes.pm create mode 100644 libs/Time/Local.pm create mode 100644 libs/Time/Other_HiRes_versions/HiRes.1.55.pm create mode 100644 libs/Time/Other_HiRes_versions/HiRes.1.59.pm create mode 100644 libs/Time/Other_HiRes_versions/HiRes.1.65.pm create mode 100644 libs/Time/Other_HiRes_versions/HiRes.pm create mode 100644 libs/Time/gmtime.pm create mode 100644 libs/Time/localtime.pm create mode 100644 libs/Time/tm.pm create mode 100644 libs/US_zipcode_ranges.txt create mode 100644 libs/buttons.gif create mode 100644 libs/vm_indicator_anim.gif create mode 100644 libs/voicemail_digits.gif create mode 100644 libs/voicemail_new.gif create mode 100644 libs/voicemail_old.gif create mode 100644 libs/voicemail_up.gif create mode 100644 park_CID.agi create mode 100644 phone_codes_GMT.txt create mode 100644 sphinxNA.zip create mode 100644 start_asterisk_boot.pl create mode 100644 test_VICIDIAL_lead_file.txt create mode 100644 upgrade_1.1.10.sql diff --git a/ADMIN_adjust_GMTnow_on_leads.pl b/ADMIN_adjust_GMTnow_on_leads.pl new file mode 100644 index 0000000..8779cae --- /dev/null +++ b/ADMIN_adjust_GMTnow_on_leads.pl @@ -0,0 +1,670 @@ +#!/usr/bin/perl + +# ADMIN_adjust_GMTnow_on_leads.pl +# +# program goes throught the vicidial_list table and adjusts the gmt_offset_now +# field to change it to today's offset if needed because of Daylight Saving Time +# +# run every time you load leads into the vicidial_list table +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# +# 50810-1540 - Added database server variable definitions lookup +# + +$MT[0]=''; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-q] = quiet\n [-t] = test\n [--debug] = debugging messages\n\n"; + } + else + { + if ($args =~ /-q/i) + { + $q=1; $Q=1; + } + if ($args =~ /--debug/i) + { + $DB=1; + print "\n-----DEBUGGING -----\n\n"; + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-t|--test/i) + { + $T=1; $TEST=1; + print "\n-----TESTING -----\n\n"; + } + } +} +else +{ +print "no command line options set\n"; +} + +if ($DB) {print "STARTING TIME ZONE DAYLIGHT SAVING TIME CALCULATION SCRIPT\n\n\n\n";} + +### end parsing run-time options ### + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +$secX = time(); +# $secX = '1079989363'; # epoch for March 22, 2993 +# $secX = '1097989363'; # epoch for October 17, 2004 + + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($secX); + $mon++; + $year = ($year + 1900); + if ($mon < 10) {$mon = "0$mon";} + if ($mday < 10) {$mday = "0$mday";} + if ($hour < 10) {$hour = "0$hour";} + if ($min < 10) {$min = "0$min";} + if ($sec < 10) {$sec = "0$sec";} + $dsec = ( ( ($hour * 3600) + ($min * 60) ) + $sec ); + +use Net::MySQL; + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + + ### Grab Server values from the database + $stmtA = "SELECT telnet_host,telnet_port,ASTmgrUSERNAME,ASTmgrSECRET,ASTmgrUSERNAMEupdate,ASTmgrUSERNAMElisten,ASTmgrUSERNAMEsend,max_vicidial_trunks,answer_transfer_agent,local_gmt,ext_context FROM servers where server_ip = '$server_ip';"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBtelnet_host = "$record->[0]"; + $DBtelnet_port = "$record->[1]"; + $DBASTmgrUSERNAME = "$record->[2]"; + $DBASTmgrSECRET = "$record->[3]"; + $DBASTmgrUSERNAMEupdate = "$record->[4]"; + $DBASTmgrUSERNAMElisten = "$record->[5]"; + $DBASTmgrUSERNAMEsend = "$record->[6]"; + $DBmax_vicidial_trunks = "$record->[7]"; + $DBanswer_transfer_agent= "$record->[8]"; + $DBSERVER_GMT = "$record->[9]"; + $DBext_context = "$record->[10]"; + if ($DBtelnet_host) {$telnet_host = $DBtelnet_host;} + if ($DBtelnet_port) {$telnet_port = $DBtelnet_port;} + if ($DBASTmgrUSERNAME) {$ASTmgrUSERNAME = $DBASTmgrUSERNAME;} + if ($DBASTmgrSECRET) {$ASTmgrSECRET = $DBASTmgrSECRET;} + if ($DBASTmgrUSERNAMEupdate) {$ASTmgrUSERNAMEupdate = $DBASTmgrUSERNAMEupdate;} + if ($DBASTmgrUSERNAMElisten) {$ASTmgrUSERNAMElisten = $DBASTmgrUSERNAMElisten;} + if ($DBASTmgrUSERNAMEsend) {$ASTmgrUSERNAMEsend = $DBASTmgrUSERNAMEsend;} + if ($DBmax_vicidial_trunks) {$max_vicidial_trunks = $DBmax_vicidial_trunks;} + if ($DBanswer_transfer_agent) {$answer_transfer_agent = $DBanswer_transfer_agent;} + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + if ($DBext_context) {$ext_context = $DBext_context;} + } + } + + $LOCAL_GMT_OFF = $SERVER_GMT; + $LOCAL_GMT_OFF_STD = $SERVER_GMT; + +if ($isdst) {$LOCAL_GMT_OFF++;} +if ($DB) {print "SEED TIME $secX : $year-$mon-$mday $hour:$min:$sec LOCAL GMT OFFSET NOW: $LOCAL_GMT_OFF\n";} + + + $stmtA = "select distinct phone_code from vicidial_list;"; + if($DBX){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + $rec_countY=0; + @phone_codes=@MT; + $phone_codes_list='|'; + while ($recordA = $iterA->each) + { + $phone_codes[$rec_countY] = $recordA->[0]; + $phone_codes_ORIG[$rec_countY] = $recordA->[0]; + $phone_codes[$rec_countY] =~ s/011|0011| |\t|\r|\n//gi; + + # if ( ($phone_codes_list !~ /\|$phone_codes[$rec_countY]\|/) && (length($phone_codes[$rec_countY]) > 0) ) + # { + # $phone_codes_list .= "$phone_codes[$rec_countY]|"; + # } + + if ($DBX) {print "|",$recordA->[0],"|","\n";} + $rec_countY++; + } + } + + if ($DB) {print " - Unique Country dial codes found: $rec_countY\n";} + + + open(codefile, "/home/cron/phone_codes_GMT.txt") || die "can't open /home/cron/phone_codes_GMT.txt: $!\n"; + @codefile = ; + close(codefile); + +$ep=0; $ei=0; $ee=0; +$d=0; +foreach (@phone_codes) + { + $match_code = $phone_codes[$d]; + $match_code_ORIG = $phone_codes_ORIG[$d]; + + if ($DB) {print "\nRUNNING LOOP FOR COUNTRY CODE: $match_code\n";} + + $e=0; + foreach (@codefile) + { + chomp($codefile[$e]); + if ($codefile[$e] =~ /^$match_code\t/) + { + @m = split(/\t/, $codefile[$e]); + $area_code = $m[2]; + $area_state = $m[3]; + $area_GMT = $m[4]; $area_GMT =~ s/\+//gi; $area_GMT = ($area_GMT + 0); + $area_GMT_method = $m[6]; + if ($area_code =~ /S/) # look for state override flag in areacode field + { + $area_code =~ s/\D//gi; + $AC_match = " and phone_number LIKE \"$area_code%\" and state='$area_state'"; + if ($DB) {print " AREA CODE STATE OVERRIDE: $codefile[$e]\n";} + } + else + { + if ($area_code =~ /\*/) {$AC_match = '';} + else {$AC_match = " and phone_number LIKE \"$area_code%\"";} + } + if ($DBX) {print "PROCESSING THIS LINE: $codefile[$e]\n";} + $stmtA = "select count(*) from vicidial_list where phone_code='$match_code_ORIG' $AC_match;"; + if($DBX){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); + $rec_countZ=0; + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + $rec_countZ = $recordA->[0]; + } + } + if (!$rec_countZ) + { + if ($DB) {print " IGNORING: $codefile[$e]\n";} + $ei++; + } + else + { + $AC_GMT_diff = ($area_GMT - $LOCAL_GMT_OFF_STD); + $AC_localtime = ($secX + (3600 * $AC_GMT_diff)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($AC_localtime); + $year = ($year + 1900); + $mon++; + if ($mon < 10) {$mon = "0$mon";} + if ($mday < 10) {$mday = "0$mday";} + if ($hour < 10) {$hour = "0$hour";} + if ($min < 10) {$min = "0$min";} + if ($sec < 10) {$sec = "0$sec";} + $dsec = ( ( ($hour * 3600) + ($min * 60) ) + $sec ); + + $AC_processed=0; + if ( (!$AC_processed) && ($area_GMT_method =~ /FSA-LSO/) ) + { + if ($DBX) {print " First Sunday April to Last Sunday October\n";} + &USA_dstcalc; + if ($DBX) {print " DST: $USA_DST\n";} + if ($USA_DST) {$area_GMT++;} + $AC_processed++; + } + if ( (!$AC_processed) && ($area_GMT_method =~ /LSM-LSO/) ) + { + if ($DBX) {print " Last Sunday March to Last Sunday October\n";} + &GBR_dstcalc; + if ($DBX) {print " DST: $GBR_DST\n";} + if ($GBR_DST) {$area_GMT++;} + $AC_processed++; + } + if ( (!$AC_processed) && ($area_GMT_method =~ /LSO-LSM/) ) + { + if ($DBX) {print " Last Sunday October to Last Sunday March\n";} + &AUS_dstcalc; + if ($DBX) {print " DST: $AUS_DST\n";} + if ($AUS_DST) {$area_GMT++;} + $AC_processed++; + } + if ( (!$AC_processed) && ($area_GMT_method =~ /FSO-LSM/) ) + { + if ($DBX) {print " First Sunday October to Last Sunday March\n";} + &AUST_dstcalc; + if ($DBX) {print " DST: $AUST_DST\n";} + if ($AUST_DST) {$area_GMT++;} + $AC_processed++; + } + if ( (!$AC_processed) && ($area_GMT_method =~ /FSO-TSM/) ) + { + if ($DBX) {print " First Sunday October to Third Sunday March\n";} + &NZL_dstcalc; + if ($DBX) {print " DST: $NZL_DST\n";} + if ($NZL_DST) {$area_GMT++;} + $AC_processed++; + } + if (!$AC_processed) + { + if ($DBX) {print " No DST Method Found\n";} + if ($DBX) {print " DST: 0\n";} + $AC_processed++; + } + + + if ($AC_processed) + { + $stmtA = "select count(*) from vicidial_list where phone_code='$match_code_ORIG' $AC_match and gmt_offset_now != '$area_GMT';"; + if($DBX){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); + $rec_countW=0; + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + $rec_countW = $recordA->[0]; + } + } + if (!$rec_countW) + { + if ($DB) {print " ALL GMT ALREADY CORRECT FOR : $match_code_ORIG $area_code $area_GMT\n";} + $ei++; + } + else + { + $stmtA = "update vicidial_list set gmt_offset_now='$area_GMT' where phone_code='$match_code_ORIG' $AC_match and gmt_offset_now != '$area_GMT';"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + $Prec_countW = sprintf("%8s", $rec_countW); + if ($DB) {print " $Prec_countW records in $match_code_ORIG $area_code updated to $area_GMT\n";} + $ee++; + # sleep(1); + } + } + } + + $ep++; + } + else + { + if ($DBX) {print " IGNORING: $codefile[$e]\n";} + $ei++; + } + $e++; + } + + $d++; + } + + + + $dbhA->close; + +if($DB){print "\nDONE\n";} + +exit; + + + + +sub USA_dstcalc { +#********************************************************************** +# FSA-LSO +# This is returns 1 if Daylight Savings Time is in effect and 0 if +# Standard time is in effect. +# Based on first Sunday in April and last Sunday in October at 2 am. +# INPUTS: +# mm INTEGER Month. +# dd INTEGER Day of the month. +# ns INTEGER Seconds into the day. +# dow INTEGER Day of week (0=Sunday, to 6=Saturday) +# OPTIONAL INPUT: +# timezone INTEGER hour difference UTC - local standard time +# (DEFAULT is blank) +# make calculations based on UTC time, +# which means shift at 10:00 UTC in April +# and 9:00 UTC in October +# OUTPUT: +# INTEGER 1 = DST, 0 = not DST +#********************************************************************** + + $USA_DST=0; + $mm = $mon; + $dd = $mday; + $ns = $dsec; + $dow= $wday; + + if ($mm < 4 || $mm > 10) { + $USA_DST=0; return 0; + } elsif ($mm >= 5 && $mm <= 9) { + $USA_DST=1; return 1; + } elsif ($mm == 4) { + if ($dd > 7) { + $USA_DST=1; return 1; + } elsif ($dd >= ($dow+1)) { + if ($timezone) { + if ($dow == 0 && $ns < (7200+$timezone*3600)) { + $USA_DST=0; return 0; + } else { + $USA_DST=1; return 1; + } + } else { + if ($dow == 0 && $ns < 7200) { + $USA_DST=0; return 0; + } else { + $USA_DST=1; return 1; + } + } + } else { + $USA_DST=0; return 0; + } + } elsif ($mm == 10) { + if ($dd < 25) { + $USA_DST=1; return 1; + } elsif ($dd < ($dow+25)) { + $USA_DST=1; return 1; + } elsif ($dow == 0) { + if ($timezone) { # UTC calculations + if ($ns < (7200+($timezone-1)*3600)) { + $USA_DST=1; return 1; + } else { + $USA_DST=0; return 0; + } + } else { # local time calculations + if ($ns < 7200) { + $USA_DST=1; return 1; + } else { + $USA_DST=0; return 0; + } + } + } else { + $USA_DST=0; return 0; + } + } # end of month checks +} # end of subroutine dstcalc + + + + +sub GBR_dstcalc { +#********************************************************************** +# This is returns 1 if Daylight Savings Time is in effect and 0 if +# Standard time is in effect. +# Based on last Sunday in March and last Sunday in October at 1 am. +#********************************************************************** + + $GBR_DST=0; + $mm = $mon; + $dd = $mday; + $ns = $dsec; + $dow= $wday; + + if ($mm < 3 || $mm > 10) { + $GBR_DST=0; return 0; + } elsif ($mm >= 4 && $mm <= 9) { + $GBR_DST=1; return 1; + } elsif ($mm == 3) { + if ($dd < 25) { + $GBR_DST=0; return 0; + } elsif ($dd < ($dow+25)) { + $GBR_DST=0; return 0; + } elsif ($dow == 0) { + if ($timezone) { # UTC calculations + if ($ns < (3600+($timezone-1)*3600)) { + $GBR_DST=0; return 0; + } else { + $GBR_DST=1; return 1; + } + } else { # local time calculations + if ($ns < 3600) { + $GBR_DST=0; return 0; + } else { + $GBR_DST=1; return 1; + } + } + } else { + $GBR_DST=1; return 1; + } + } elsif ($mm == 10) { + if ($dd < 25) { + $GBR_DST=1; return 1; + } elsif ($dd < ($dow+25)) { + $GBR_DST=1; return 1; + } elsif ($dow == 0) { + if ($timezone) { # UTC calculations + if ($ns < (3600+($timezone-1)*3600)) { + $GBR_DST=1; return 1; + } else { + $GBR_DST=0; return 0; + } + } else { # local time calculations + if ($ns < 3600) { + $GBR_DST=1; return 1; + } else { + $GBR_DST=0; return 0; + } + } + } else { + $GBR_DST=0; return 0; + } + } # end of month checks +} # end of subroutine dstcalc + + + + +sub AUS_dstcalc { +#********************************************************************** +# This is returns 1 if Daylight Savings Time is in effect and 0 if +# Standard time is in effect. +# Based on last Sunday in October and last Sunday in March at 1 am. +#********************************************************************** + + $AUS_DST=0; + $mm = $mon; + $dd = $mday; + $ns = $dsec; + $dow= $wday; + + if ($mm < 3 || $mm > 10) { + $AUS_DST=1; return 1; + } elsif ($mm >= 4 && $mm <= 9) { + $AUS_DST=0; return 0; + } elsif ($mm == 3) { + if ($dd < 25) { + $AUS_DST=1; return 1; + } elsif ($dd < ($dow+25)) { + $AUS_DST=1; return 1; + } elsif ($dow == 0) { + if ($timezone) { # UTC calculations + if ($ns < (3600+($timezone-1)*3600)) { + $AUS_DST=1; return 1; + } else { + $AUS_DST=0; return 0; + } + } else { # local time calculations + if ($ns < 3600) { + $AUS_DST=1; return 1; + } else { + $AUS_DST=0; return 0; + } + } + } else { + $AUS_DST=0; return 0; + } + } elsif ($mm == 10) { + if ($dd < 25) { + $AUS_DST=0; return 0; + } elsif ($dd < ($dow+25)) { + $AUS_DST=0; return 0; + } elsif ($dow == 0) { + if ($timezone) { # UTC calculations + if ($ns < (3600+($timezone-1)*3600)) { + $AUS_DST=0; return 0; + } else { + $AUS_DST=1; return 1; + } + } else { # local time calculations + if ($ns < 3600) { + $AUS_DST=0; return 0; + } else { + $AUS_DST=1; return 1; + } + } + } else { + $AUS_DST=1; return 1; + } + } # end of month checks +} # end of subroutine dstcalc + + + + + +sub AUST_dstcalc { +#********************************************************************** +# TASMANIA ONLY +# This is returns 1 if Daylight Savings Time is in effect and 0 if +# Standard time is in effect. +# Based on first Sunday in October and last Sunday in March at 1 am. +#********************************************************************** + + $AUST_DST=0; + $mm = $mon; + $dd = $mday; + $ns = $dsec; + $dow= $wday; + + if ($mm < 3 || $mm > 10) { + $AUST_DST=1; return 1; + } elsif ($mm >= 4 && $mm <= 9) { + $AUST_DST=0; return 0; + } elsif ($mm == 3) { + if ($dd < 25) { + $AUST_DST=1; return 1; + } elsif ($dd < ($dow+25)) { + $AUST_DST=1; return 1; + } elsif ($dow == 0) { + if ($timezone) { # UTC calculations + if ($ns < (3600+($timezone-1)*3600)) { + $AUST_DST=1; return 1; + } else { + $AUST_DST=0; return 0; + } + } else { # local time calculations + if ($ns < 3600) { + $AUST_DST=1; return 1; + } else { + $AUST_DST=0; return 0; + } + } + } else { + $AUST_DST=0; return 0; + } + } elsif ($mm == 10) { + if ($dd > 7) { + $AUST_DST=1; return 1; + } elsif ($dd >= ($dow+1)) { + if ($timezone) { + if ($dow == 0 && $ns < (7200+$timezone*3600)) { + $AUST_DST=0; return 0; + } else { + $AUST_DST=1; return 1; + } + } else { + if ($dow == 0 && $ns < 3600) { + $AUST_DST=0; return 0; + } else { + $AUST_DST=1; return 1; + } + } + } else { + $AUST_DST=0; return 0; + } + } # end of month checks +} # end of subroutine dstcalc + + + + + +sub NZL_dstcalc { +#********************************************************************** +# This is returns 1 if Daylight Savings Time is in effect and 0 if +# Standard time is in effect. +# Based on first Sunday in October and third Sunday in March at 1 am. +#********************************************************************** + + $NZL_DST=0; + $mm = $mon; + $dd = $mday; + $ns = $dsec; + $dow= $wday; + + if ($mm < 3 || $mm > 10) { + $NZL_DST=1; return 1; + } elsif ($mm >= 4 && $mm <= 9) { + $NZL_DST=0; return 0; + } elsif ($mm == 3) { + if ($dd < 14) { + $NZL_DST=1; return 1; + } elsif ($dd < ($dow+14)) { + $NZL_DST=1; return 1; + } elsif ($dow == 0) { + if ($timezone) { # UTC calculations + if ($ns < (3600+($timezone-1)*3600)) { + $NZL_DST=1; return 1; + } else { + $NZL_DST=0; return 0; + } + } else { # local time calculations + if ($ns < 3600) { + $NZL_DST=1; return 1; + } else { + $NZL_DST=0; return 0; + } + } + } else { + $NZL_DST=0; return 0; + } + } elsif ($mm == 10) { + if ($dd > 7) { + $NZL_DST=1; return 1; + } elsif ($dd >= ($dow+1)) { + if ($timezone) { + if ($dow == 0 && $ns < (7200+$timezone*3600)) { + $NZL_DST=0; return 0; + } else { + $NZL_DST=1; return 1; + } + } else { + if ($dow == 0 && $ns < 3600) { + $NZL_DST=0; return 0; + } else { + $NZL_DST=1; return 1; + } + } + } else { + $NZL_DST=0; return 0; + } + } # end of month checks +} # end of subroutine dstcalc diff --git a/ADMIN_area_code_populate.pl b/ADMIN_area_code_populate.pl new file mode 100644 index 0000000..d47cdea --- /dev/null +++ b/ADMIN_area_code_populate.pl @@ -0,0 +1,57 @@ +#!/usr/bin/perl +# ADMIN_area_code_populate.pl version 1.1 for Perl +# +# Copyright (C) 2006 Joe Johnson,Matt Florell LICENSE: GPLv2 +# +# Description: +# server application that allows load areacodes into to asterisk list database +# + +use lib ".\\",".\\libs", './', './libs', '../libs', '/usr/local/perl_TK/libs', 'C:\\AST_VICI\\libs'; +use Time::HiRes ('gettimeofday','usleep','sleep'); # needed to have perl sleep in increments of less than one second +use Net::MySQL; + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +$dbh = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + +$slash_star = '\*'; + + open(codefile, "/home/cron/phone_codes_GMT.txt") || die "can't open /home/cron/phone_codes_GMT.txt: $!\n"; + @codefile = ; + close(codefile); + $pc=0; + $ins_stmt="insert into vicidial_phone_codes VALUES "; + foreach (@codefile) { + @row=split(/\t/, $codefile[$pc]); + $row[7] =~ s/\r|\n|\t| $//gi; + $row[6] =~ s/\r|\n|\t| $//gi; + $row[5] =~ s/\r|\n|\t| $//gi; + $row[4] =~ s/\r|\n|\t| $//gi; + $row[3] =~ s/\r|\n|\t| $//gi; + $row[2] =~ s/\r|\n|\t| $//gi; + $row[1] =~ s/\r|\n|\t| $//gi; + $row[0] =~ s/\r|\n|\t| $//gi; + $pc++; + $ins_stmt.="('$row[0]', '$row[1]', '$row[2]', '$row[3]', '$row[4]', '$row[5]', '$row[6]', '$row[7]'), "; + if ($pc =~ /00$/) { + chop($ins_stmt); + chop($ins_stmt); + $dbh->query($ins_stmt) || die "can't execute query: |$ins_stmt| $!\n"; + $ins_stmt="insert into vicidial_phone_codes VALUES "; + print STDERR "$pc\n"; + } + } + + chop($ins_stmt); + chop($ins_stmt); + $dbh->query($ins_stmt); + $ins_stmt="insert into vicidial_phone_codes VALUES "; + print STDERR "$pc\n"; + +exit; \ No newline at end of file diff --git a/ADMIN_keepalive_AST_VDautodial.pl b/ADMIN_keepalive_AST_VDautodial.pl new file mode 100644 index 0000000..b40027f --- /dev/null +++ b/ADMIN_keepalive_AST_VDautodial.pl @@ -0,0 +1,120 @@ +#!/usr/bin/perl +# +# ADMIN_keepalive_AST_VDauto_dial.pl version 0.3 +# +# designed to keep the AST_update processes aline and check every minute +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# +# 50810-1452 - removed '-L' flag for screen logging of output (optimization) +# 50823-1454 - Added commandline debug options with debug printouts +# + +$DB=0; # Debug flag +$MT[0]=''; $MT[1]=''; +@psline=@MT; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n[-debugX] = Extra-verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + + +#@psoutput = `ps -f -C AST_update --no-headers`; +#@psoutput = `ps -f -C AST_updat* --no-headers`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput = `/bin/ps -o "%p %a" --no-headers -A`; + +$running_VDAD = 0; + +$i=0; +foreach (@psoutput) +{ + chomp($psoutput[$i]); +if ($DBX) {print "$i|$psoutput[$i]| \n";} +@psline = split(/\/usr\/bin\/perl /,$psoutput[$i]); + +if ($psline[1] =~ /\/home\/cron\/AST_VDauto_dial\.pl/) + { + $running_VDAD++; + if ($DB) {print "VDAD RUNNING: |$psline[1]|\n";} + } + +$i++; +} + + +if (!$running_VDAD) +{ +# print "double check that VDAD is not running\n"; + + sleep(4); + +#@psoutput = `ps -f -C AST_update --no-headers`; +#@psoutput = `ps -f -C AST_updat* --no-headers`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput = `/bin/ps -o "%p %a" --no-headers -A`; +$i=0; +foreach (@psoutput2) + { + chomp($psoutput2[$i]); + if ($DBX) {print "$i|$psoutput[$i]| \n";} + @psline = split(/\/usr\/bin\/perl /,$psoutput2[$i]); + + if ($psline[1] =~ /\/home\/cron\/AST_VDauto_dial\.pl/) + { + $running_VDAD++; + if ($DB) {print "VDAD RUNNING: |$psline[1]|\n";} + } + + $i++; + } + +if (!$running_VDAD) + { + if ($DB) {print "starting AST_VDauto_dial...\n";} + # add a '-L' to the command below to activate logging + `/usr/bin/screen -d -m -S ASTVDauto /home/cron/AST_VDauto_dial.pl`; + } +} + + + + + if ($DB) {print "DONE\n";} + +exit; diff --git a/ADMIN_keepalive_AST_VDremote_agents.pl b/ADMIN_keepalive_AST_VDremote_agents.pl new file mode 100644 index 0000000..5b83422 --- /dev/null +++ b/ADMIN_keepalive_AST_VDremote_agents.pl @@ -0,0 +1,119 @@ +#!/usr/bin/perl +# +# ADMIN_keepalive_AST_VDremote_agents.pl version 0.3 +# +# designed to keep the AST_update processes aline and check every minute +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# +# 50810-1453 - removed '-L' flag for screen logging of output (optimization) +# 50823-1459 - Added commandline debug options with debug printouts +# + +$DB=0; # Debug flag +$MT[0]=''; $MT[1]=''; +@psline=@MT; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n[-debugX] = Extra-verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + +#@psoutput = `ps -f -C AST_update --no-headers`; +#@psoutput = `ps -f -C AST_updat* --no-headers`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput = `/bin/ps -o "%p %a" --no-headers -A`; + +$running_VDra = 0; + +$i=0; +foreach (@psoutput) +{ + chomp($psoutput[$i]); +if ($DBX) {print "$i|$psoutput[$i]| \n";} +@psline = split(/\/usr\/bin\/perl /,$psoutput[$i]); + +if ($psline[1] =~ /\/home\/cron\/AST_VDremote_agents\.pl/) + { + $running_VDra++; + if ($DB) {print "VDra RUNNING: |$psline[1]|\n";} + } + +$i++; +} + + +if (!$running_VDra) +{ +# print "double check that update is not running\n"; + + sleep(5); + +#@psoutput = `ps -f -C AST_update --no-headers`; +#@psoutput = `ps -f -C AST_updat* --no-headers`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput = `/bin/ps -o "%p %a" --no-headers -A`; +$i=0; +foreach (@psoutput2) + { + chomp($psoutput2[$i]); + if ($DBX) {print "$i|$psoutput[$i]| \n";} + @psline = split(/\/usr\/bin\/perl /,$psoutput2[$i]); + + if ($psline[1] =~ /\/home\/cron\/AST_VDremote_agents\.pl/) + { + $running_VDra++; + if ($DB) {print "VDra RUNNING: |$psline[1]|\n";} + } + + $i++; + } + +if (!$running_VDra) + { + if ($DB) {print "starting AST_VDremote_agents...\n";} + # add a '-L' to the command below to activate logging + `/usr/bin/screen -d -m -S ASTVDremote /home/cron/AST_VDremote_agents.pl`; + } +} + + + + + if ($DB) {print "DONE\n";} + +exit; diff --git a/ADMIN_keepalive_AST_send_listen.pl b/ADMIN_keepalive_AST_send_listen.pl new file mode 100644 index 0000000..bdf94ea --- /dev/null +++ b/ADMIN_keepalive_AST_send_listen.pl @@ -0,0 +1,169 @@ +#!/usr/bin/perl +# +# ADMIN_keepalive_AST_send_listen.pl version 0.3 +# +# designed to keep the AST_manager_listen and manager_send processes alive and check every minute +# +# if you are on an older system you may need to change the @psoutput lines to fit your ps version +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# +# 50810-1450 - removed '-L' flag for screen logging of output (optimization) +# 50823-1440 - Added commandline debug options with debug printouts +# + +$DB=0; # Debug flag +$MT[0]=''; $MT[1]=''; +@psline=@MT; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n[-debugX] = Extra-verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + + sleep(1); + + +#@psoutput = `ps -f -C AST_manager --no-headers`; +#@psoutput = `ps -f --no-headers -C AST_manager_s*; ps -f --no-headers -C AST_manager_l*`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput = `/bin/ps -o "%p %a" --no-headers -A`; + +$running_listen = 0; +$running_send = 0; + +$i=0; +foreach (@psoutput) +{ + chomp($psoutput[$i]); +if ($DBX) {print "$i|$psoutput[$i]| \n";} +@psline = split(/\/usr\/bin\/perl /,$psoutput[$i]); + +if ($psline[1] =~ /AST_manager_li/) + { + $running_listen++; + if ($DB) {print "LISTEN RUNNING: |$psline[1]|\n";} + } +if ($psline[1] =~ /AST_manager_se/) + { + $running_send++; + if ($DB) {print "SEND RUNNING: |$psline[1]|\n";} + } + +$i++; +} + + + +if (!$running_listen) +{ + # print "double check that update_11 is not running\n"; + + sleep(1); + +#@psoutput = `ps -f -C AST_manager --no-headers`; +#@psoutput = `ps -f --no-headers -C AST_manager_s*; ps -f --no-headers -C AST_manager_l*`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput = `/bin/ps -o "%p %a" --no-headers -A`; + +$i=0; +foreach (@psoutput2) + { + chomp($psoutput2[$i]); + if ($DBX) {print "$i|$psoutput[$i]| \n";} + @psline = split(/\/usr\/bin\/perl /,$psoutput2[$i]); + + if ($psline[1] =~ /AST_manager_li/) + { + $running_listen++; + if ($DB) {print "LISTEN RUNNING: |$psline[1]|\n";} + } + + $i++; + } + +if (!$running_listen) + { + if ($DB) {print "starting AST_manager_listen...\n";} + # add a '-L' to the command below to activate logging + `/usr/bin/screen -d -m -S ASTlisten /home/cron/AST_manager_listen.pl`; + } +} + + + + +if (!$running_send) +{ + # print "double check that update_12 is not running\n"; + + sleep(1); + +#@psoutput = `ps -f -C AST_manager --no-headers`; +#@psoutput = `ps -f --no-headers -C AST_manager_s*; ps -f --no-headers -C AST_manager_l*`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput = `/bin/ps -o "%p %a" --no-headers -A`; +$i=0; +foreach (@psoutput) + { + chomp($psoutput[$i]); + if ($DBX) {print "$i|$psoutput[$i]| \n";} + @psline = split(/\/usr\/bin\/perl /,$psoutput[$i]); + + if ($psline[1] =~ /AST_manager_se/) + { + $running_send++; + if ($DB) {print "SEND RUNNING: |$psline[1]|";} + } + + $i++; + } + +if (!$running_send) + { + if ($DB) {print "starting AST_manager_send...\n";} + # add a '-L' to the command below to activate logging + `/usr/bin/screen -d -m -S ASTsend /home/cron/AST_manager_send.pl`; + } +} + + + + + if ($DB) {print "DONE\n";} + +exit; diff --git a/ADMIN_keepalive_AST_update.pl b/ADMIN_keepalive_AST_update.pl new file mode 100644 index 0000000..4a713e3 --- /dev/null +++ b/ADMIN_keepalive_AST_update.pl @@ -0,0 +1,121 @@ +#!/usr/bin/perl +# +# ADMIN_keepalive_AST_update.pl version 0.4 +# +# designed to keep the AST_update processes aline and check every minute +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# +# 50810-1451 - removed '-L' flag for screen logging of output (optimization) +# 50823-1446 - Added commandline debug options with debug printouts +# 51229-1027 - fixed typo of array name +# + +$DB=0; # Debug flag +$MT[0]=''; $MT[1]=''; +@psline=@MT; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n[-debugX] = Extra-verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + + +#@psoutput = `ps -f -C AST_update --no-headers`; +#@psoutput = `ps -f -C AST_updat* --no-headers`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput = `/bin/ps -o "%p %a" --no-headers -A`; + +$running_update = 0; + +$i=0; +foreach (@psoutput) +{ + chomp($psoutput[$i]); +if ($DBX) {print "$i|$psoutput[$i]| \n";} +@psline = split(/\/usr\/bin\/perl /,$psoutput[$i]); + +if ($psline[1] =~ /\/home\/cron\/AST_update\.pl/) + { + $running_update++; + if ($DB) {print "UPDATE RUNNING: |$psline[1]|\n";} + } + +$i++; +} + + +if (!$running_update) +{ +# print "double check that update is not running\n"; + + sleep(5); + +#@psoutput = `ps -f -C AST_update --no-headers`; +#@psoutput = `ps -f -C AST_updat* --no-headers`; +#@psoutput = `/bin/ps -f --no-headers -A`; +@psoutput2 = `/bin/ps -o "%p %a" --no-headers -A`; +$i=0; +foreach (@psoutput2) + { + chomp($psoutput2[$i]); + if ($DBX) {print "$i|$psoutput[$i]| \n";} + @psline = split(/\/usr\/bin\/perl /,$psoutput2[$i]); + + if ($psline[1] =~ /\/home\/cron\/AST_update\.pl/) + { + $running_update++; + if ($DB) {print "UPDATE RUNNING: |$psline[1]|\n";} + } + + $i++; + } + +if (!$running_update) + { + if ($DB) {print "starting AST_update...\n";} + # add a '-L' to the command below to activate logging + `/usr/bin/screen -d -m -S ASTupdate /home/cron/AST_update.pl`; + } +} + + + + + if ($DB) {print "DONE\n";} + +exit; diff --git a/ADMIN_keepalive_send_listen.at b/ADMIN_keepalive_send_listen.at new file mode 100644 index 0000000..652c1bc --- /dev/null +++ b/ADMIN_keepalive_send_listen.at @@ -0,0 +1,6 @@ +#!/bin/sh + +/home/cron/ADMIN_keepalive_AST_send_listen.pl + +exit + diff --git a/ADMIN_restart_roll_logs.pl b/ADMIN_restart_roll_logs.pl new file mode 100644 index 0000000..a617e3d --- /dev/null +++ b/ADMIN_restart_roll_logs.pl @@ -0,0 +1,52 @@ +#!/usr/bin/perl +# ADMIN_restart_roll_logs.pl - script to roll the Asterisk logs on machine restart +# have this run on the astersik server +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# + +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$Fhour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday---$hour$min$sec"; + + +print "rolling AST_update log...\n"; +`mv -f /home/cron/LOG_AST_update.log /home/cron/LOGS/LOG_AST_update.log.$now_date`; + +print "rolling MSAST_update log...\n"; +`mv -f /home/cron/MSLOG_AST_update.log /home/cron/LOGS/MSLOG_AST_update.log.$now_date`; + +print "rolling LIAST_update log...\n"; +`mv -f /home/cron/LILOG_AST_update.log /home/cron/LOGS/LILOG_AST_update.log.$now_date`; + +print "rolling VDAST_update log...\n"; +`mv -f /home/cron/VDLOG_AST_update.log /home/cron/LOGS/VDLOG_AST_update.log.$now_date`; + +print "rolling Asterisk messages log...\n"; +`mv -f /var/log/asterisk/messages /var/log/asterisk/messages.$now_date`; + +print "rolling Asterisk event log...\n"; +`mv -f /var/log/asterisk/event_log /var/log/asterisk/event_log.$now_date`; + +print "rolling Asterisk cdr logs...\n"; +`mv -f /var/log/asterisk/cdr-csv/Master.csv /var/log/asterisk/cdr-csv/Master.csv.$now_date`; +`mv -f /var/log/asterisk/cdr-custom/Master.csv /var/log/asterisk/cdr-custom/Master.csv.$now_date`; + +print "rolling Asterisk screen log...\n"; +`mv -f /screenlog.0 /screenlog.0.$now_date`; + +print "rolling Asterisk root screen log...\n"; +`mv -f /root/screenlog.0 /root/screenlog.0.$now_date`; + + +print "FINISHED... EXITING\n"; + +exit; diff --git a/AST_CRON_mix_recordings.pl b/AST_CRON_mix_recordings.pl new file mode 100644 index 0000000..1c7557b --- /dev/null +++ b/AST_CRON_mix_recordings.pl @@ -0,0 +1,122 @@ +#!/usr/bin/perl +# +# AST_CRON_mix_recordings.pl +# runs every 5 minutes and mixes the call recordings in the monitor together +# and puts the resulting ALL file into the DONE directory in the "monitor" dir +# +# soxmix is REQUIRED to use this script, soxmix is available only as part of +# the sox audio package, and only in newer versions. Make sure you have soxmix +# installed properly and in your path +# +# put an entry into the cron of of your asterisk machine to run this script +# every 5 minutes or however often you desire +# +# make sure that the following directories exist: +# /var/spool/asterisk/monitor # default Asterisk recording directory +# /var/spool/asterisk/monitor/DONE # where the combined files are put +# /var/spool/asterisk/monitor/ORIG # where the original in/out files are put +# +# This program assumes that recordings are saved as .wav +# should be easy to change this code if you use .gsm instead +# +# This program also sends the ALL combined file to an FTP server for archival +# purposes, you can comment out the Net::Ping and Net::FTP lines as well as the +# file transfer section of the code to deactivate remote copying of the +# recording files +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# +# 51021-1058 Added quotes around CLI executed commands +# 51122-1458 Added soxmix binary path check +# + + +#$v=1; + +# Customize variables for FTP +$FTP_host = '10.0.0.4'; +$FTP_user = 'cron'; +$FTP_pass = 'test'; +$FTP_dir = 'RECORDINGS'; + +### directory where in/out recordings are saved to by Asterisk +$dir1 = '/var/spool/asterisk/monitor'; + +$soxmixbin = ''; +if ( -e ('/usr/bin/soxmix')) {$soxmixbin = '/usr/bin/soxmix';} +else + { + if ( -e ('/usr/local/bin/soxmix')) {$soxmixbin = '/usr/local/bin/soxmix';} + else + { + print "Can't find soxmix binary! Exiting...\n"; + exit; + } + } + +use Net::Ping; +use Net::FTP; + + + opendir(FILE, "$dir1/"); + @FILES = readdir(FILE); + +$i=0; +foreach(@FILES) + { + $size1 = 0; + $size2 = 0; + + if ( (length($FILES[$i]) > 4) && (!-d $FILES[$i]) ) + { + + $size1 = (-s "$dir1/$FILES[$i]"); + if ($v) {print "$FILES[$i] $size1\n";} + sleep(1); + $size2 = (-s "$dir1/$FILES[$i]"); + if ($v) {print "$FILES[$i] $size2\n\n";} + + + if ( ($FILES[$i] !~ /out\.wav/i) && ($size1 eq $size2) && (length($FILES[$i]) > 4)) + { + $INfile = $FILES[$i]; + $OUTfile = $FILES[$i]; + $OUTfile =~ s/-in\.wav/-out.wav/gi; + $ALLfile = $FILES[$i]; + $ALLfile =~ s/-in\.wav/-all.wav/gi; + + if ($v) {print "|$INfile| |$OUTfile| |$ALLfile|\n\n";} + + `$soxmixbin "$dir1/$INfile" "$dir1/$OUTfile" "$dir1/$ALLfile"`; + if ($v) {print "|$INfile| |$OUTfile| |$ALLfile|\n\n";} + if (!$T) + { + `mv -f "$dir1/$INfile" "$dir1/ORIG/$INfile"`; + `mv -f "$dir1/$OUTfile" "$dir1/ORIG/$OUTfile"`; + `mv -f "$dir1/$ALLfile" "$dir1/DONE/$ALLfile"`; + } + + ### BEGIN Remote file transfer + $p = Net::Ping->new(); + $ping_good = $p->ping("$FTP_host"); + + if ($ping_good) + { + $ftp = Net::FTP->new("$FTP_host", Port => 21); + $ftp->login("$FTP_user","$FTP_pass"); + $ftp->cwd("$FTP_dir"); + $ftp->binary(); + $ftp->put("$dir1/DONE/$ALLfile", "$ALLfile"); + $ftp->quit; + } + ### END Remote file transfer + + } + } + $i++; + } + +if ($v) {print "DONE... EXITING\n\n";} + +exit; diff --git a/AST_CRON_mix_recordings_BASIC.pl b/AST_CRON_mix_recordings_BASIC.pl new file mode 100644 index 0000000..4786e67 --- /dev/null +++ b/AST_CRON_mix_recordings_BASIC.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl +# +# AST_CRON_mix_recordings_BASIC.pl +# runs every 5 minutes and mixes the call recordings in the monitor together +# and puts the resulting ALL file into the DONE directory in the "monitor" dir +# +# soxmix is REQUIRED to use this script, soxmix is available only as part of +# the sox audio package, and only in newer versions. Make sure you have soxmix +# installed properly and in your path +# +# put an entry into the cron of of your asterisk machine to run this script +# every 5 minutes or however often you desire +# +# make sure that the following directories exist: +# /var/spool/asterisk/monitor # default Asterisk recording directory +# /var/spool/asterisk/monitor/DONE # where the combined files are put +# /var/spool/asterisk/monitor/ORIG # where the original in/out files are put +# +# This program assumes that recordings are saved as .wav +# should be easy to change this code if you use .gsm instead +# +# This program also sends the ALL combined file to an FTP server for archival +# purposes, you can comment out the Net::Ping and Net::FTP lines as well as the +# file transfer section of the code to deactivate remote copying of the +# recording files +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# +# 51021-1058 Added quotes around CLI executed commands +# 51122-1456 Added soxmix binary path check +# + + +#$v=1; + + +### directory where in/out recordings are saved to by Asterisk +$dir1 = '/var/spool/asterisk/monitor'; + +$soxmixbin = ''; +if ( -e ('/usr/bin/soxmix')) {$soxmixbin = '/usr/bin/soxmix';} +else + { + if ( -e ('/usr/local/bin/soxmix')) {$soxmixbin = '/usr/local/bin/soxmix';} + else + { + print "Can't find soxmix binary! Exiting...\n"; + exit; + } + } + + opendir(FILE, "$dir1/"); + @FILES = readdir(FILE); + +$i=0; +foreach(@FILES) + { + $size1 = 0; + $size2 = 0; + + if ( (length($FILES[$i]) > 4) && (!-d $FILES[$i]) ) + { + + $size1 = (-s "$dir1/$FILES[$i]"); + if ($v) {print "$FILES[$i] $size1\n";} + sleep(1); + $size2 = (-s "$dir1/$FILES[$i]"); + if ($v) {print "$FILES[$i] $size2\n\n";} + + + if ( ($FILES[$i] !~ /out\.wav/i) && ($size1 eq $size2) && (length($FILES[$i]) > 4)) + { + $INfile = $FILES[$i]; + $OUTfile = $FILES[$i]; + $OUTfile =~ s/-in\.wav/-out.wav/gi; + $ALLfile = $FILES[$i]; + $ALLfile =~ s/-in\.wav/-all.wav/gi; + + if ($v) {print "|$INfile| |$OUTfile| |$ALLfile|\n\n";} + + `$soxmixbin "$dir1/$INfile" "$dir1/$OUTfile" "$dir1/$ALLfile"`; + if ($v) {print "|$INfile| |$OUTfile| |$ALLfile|\n\n";} + if (!$T) + { + `mv -f "$dir1/$INfile" "$dir1/ORIG/$INfile"`; + `mv -f "$dir1/$OUTfile" "$dir1/ORIG/$OUTfile"`; + `mv -f "$dir1/$ALLfile" "$dir1/DONE/$ALLfile"`; + } + } + } + $i++; + } + +if ($v) {print "DONE... EXITING\n\n";} + +exit; diff --git a/AST_CRON_mix_recordings_GSM.pl b/AST_CRON_mix_recordings_GSM.pl new file mode 100644 index 0000000..02c5ef6 --- /dev/null +++ b/AST_CRON_mix_recordings_GSM.pl @@ -0,0 +1,151 @@ +#!/usr/bin/perl +# +# AST_CRON_mix_recordings_GSM.pl +# runs every 5 minutes and mixes the call recordings in the monitor together +# and puts the resulting ALL file into the DONE directory in the "monitor" dir +# and converts the ALL file to GSM format to save space. +# +# soxmix is REQUIRED to use this script, soxmix is available only as part of +# the sox audio package, and only in newer versions. Make sure you have soxmix +# installed properly and in your path +# +# put an entry into the cron of of your asterisk machine to run this script +# every 5 minutes or however often you desire +# +# make sure that the following directories exist: +# /var/spool/asterisk/monitor # default Asterisk recording directory +# /var/spool/asterisk/monitor/DONE # where the combined files are put +# /var/spool/asterisk/monitor/ORIG # where the original in/out files are put +# +# This program assumes that recordings are saved as .wav +# should be easy to change this code if you use .gsm instead +# +# This program also sends the ALL combined file to an FTP server for archival +# purposes, you can comment out the Net::Ping and Net::FTP lines as well as the +# file transfer section of the code to deactivate remote copying of the +# recording files +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# +# 51021-1058 Added quotes around CLI executed commands +# 51122-1455 Added soxmix and sox binary path check +# + + +#$v=1; + +# Customize variables for FTP +$FTP_host = '10.0.0.4'; +$FTP_user = 'cron'; +$FTP_pass = 'test'; +$FTP_dir = 'RECORDINGS'; +$FTP_port = '21'; + +### directory where in/out recordings are saved to by Asterisk +$dir1 = '/var/spool/asterisk/monitor'; + +$soxmixbin = ''; +if ( -e ('/usr/bin/soxmix')) {$soxmixbin = '/usr/bin/soxmix';} +else + { + if ( -e ('/usr/local/bin/soxmix')) {$soxmixbin = '/usr/local/bin/soxmix';} + else + { + print "Can't find soxmix binary! Exiting...\n"; + exit; + } + } +$soxbin = ''; +if ( -e ('/usr/bin/sox')) {$soxbin = '/usr/bin/sox';} +else + { + if ( -e ('/usr/local/bin/sox')) {$soxbin = '/usr/local/bin/sox';} + else + { + print "Can't find sox binary! Exiting...\n"; + exit; + } + } + +use Net::Ping; +use Net::FTP; + + + opendir(FILE, "$dir1/"); + @FILES = readdir(FILE); + +$i=0; +foreach(@FILES) + { + $size1 = 0; + $size2 = 0; + + if ( (length($FILES[$i]) > 4) && (!-d $FILES[$i]) ) + { + + $size1 = (-s "$dir1/$FILES[$i]"); + if ($v) {print "$FILES[$i] $size1\n";} + sleep(1); + $size2 = (-s "$dir1/$FILES[$i]"); + if ($v) {print "$FILES[$i] $size2\n\n";} + + + if ( ($FILES[$i] !~ /out\.wav/i) && ($size1 eq $size2) && (length($FILES[$i]) > 4)) + { + $INfile = $FILES[$i]; + $OUTfile = $FILES[$i]; + $OUTfile =~ s/-in\.wav/-out.wav/gi; + $ALLfile = $FILES[$i]; + $ALLfile =~ s/-in\.wav/-all.wav/gi; + $GSMfile = $ALLfile; + $GSMfile =~ s/-all\.wav/-all.gsm/gi; + + if ($v) {print "|$INfile| |$OUTfile| |$ALLfile|\n\n";} + + `$soxmixbin "$dir1/$INfile" "$dir1/$OUTfile" "$dir1/$ALLfile"`; + if ($v) {print "|$INfile| |$OUTfile| |$ALLfile|\n\n";} + if (!$T) + { + `mv -f "$dir1/$INfile" "$dir1/ORIG/$INfile"`; + `mv -f "$dir1/$OUTfile" "$dir1/ORIG/$OUTfile"`; + `mv -f "$dir1/$ALLfile" "$dir1/DONE/$ALLfile"`; + } + else + { + `cp -f "$dir1/$ALLfile" "$dir1/DONE/$ALLfile"`; + } + + `$soxbin "$dir1/DONE/$ALLfile" "$dir1/DONE/$GSMfile"`; + + if (!$T) + { + `rm -f "$dir1/DONE/$ALLfile"`; + } + + if($DB){print STDERR "\n|/usr/bin/sox $live_folder/$filename[$k]$WAV $arch_folder/$filename[$k]$GSM|\n";} + chmod 0755, "$dir1/DONE/$GSMfile"; + + ### BEGIN Remote file transfer + # $p = Net::Ping->new(); + # $ping_good = $p->ping("$FTP_host"); + + # if ($ping_good) + # { + $ftp = Net::FTP->new("$FTP_host", Port => $FTP_port, Debug => 0, Passive => 1); + $ftp->login("$FTP_user","$FTP_pass"); + $ftp->cwd("$FTP_dir"); + $ftp->binary(); + $ftp->put("$dir1/DONE/$GSMfile", "$GSMfile"); + $ftp->quit; + # } + ### END Remote file transfer + + } + } + $i++; + } + +if ($v) {print "DONE... EXITING\n\n";} + +exit; diff --git a/AST_DB_optimize.pl b/AST_DB_optimize.pl new file mode 100644 index 0000000..2c1aabf --- /dev/null +++ b/AST_DB_optimize.pl @@ -0,0 +1,153 @@ +#!/usr/bin/perl +# +# AST_DB_optimize.pl version 0.1 +# +# DESCRIPTION: +# optimizes the tables used in the asterisk MySQL database +# +# It is recommended that you run this program on the local Asterisk machine +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +use Net::MySQL; + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + + + + $stmtA = "optimize table call_log;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + $stmtA = "optimize table park_log;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + $stmtA = "optimize table vicidial_log;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + $stmtA = "optimize table vicidial_closer_log;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + $stmtA = "optimize table vicidial_xfer_log;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + $stmtA = "optimize table vicidial_list;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + $stmtA = "optimize table vicidial_manager;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + $stmtA = "optimize table vicidial_auto_calls;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + $stmtA = "optimize table vicidial_live_agents;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + while ($recordA = $iterA->each) + { + if ($DB) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + + + $dbhA->close; + + +exit; + + + + + + diff --git a/AST_SERVER_conf.pl b/AST_SERVER_conf.pl new file mode 100644 index 0000000..ba87cee --- /dev/null +++ b/AST_SERVER_conf.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +# AST_SERVER_conf.pl +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# + +# Customized Variables +$server_ip = '10.10.10.15'; # Asterisk server IP +$DB_server = 'localhost'; # MySQL server IP +$DB_database = 'asterisk'; # MySQL database name +$DB_user = 'cron'; # MySQL user +$DB_pass = '1234'; # MySQL pass +$DB_port = '3306'; # MySQL connection port + +$LOGfile = '/home/cron/LOG_AST_update.log'; +$telnetlog = '/home/cron/telnetlog.log'; +$MSLOGfile = '/home/cron/MSLOG_AST_update.log'; +$MStelnetlog = '/home/cron/MStelnetlog.log'; +$LILOGfile = '/home/cron/LILOG_AST_update.log'; +$LItelnetlog = '/home/cron/LItelnetlog.log'; +$KHLOGfile = '/home/cron/KHLOG_AST_update.log'; +$VDADLOGfile = '/home/cron/VDLOG_AST_update.log'; + +return 1; diff --git a/AST_VDauto_dial.pl b/AST_VDauto_dial.pl new file mode 100644 index 0000000..1352416 --- /dev/null +++ b/AST_VDauto_dial.pl @@ -0,0 +1,939 @@ +#!/usr/bin/perl +# +# AST_VDauto_dial.pl version 0.9 +# +# DESCRIPTION: +# uses Net::MySQL to place auto_dial calls on the VICIDIAL dialer system +# +# SUMMARY: +# This program was designed for people using the Asterisk PBX with VICIDIAL +# +# For the client to use VICIDIAL, this program must be in the cron constantly +# +# For this program to work you need to have the "asterisk" MySQL database +# created and create the tables listed in the CONF_MySQL.txt file, also make sure +# that the machine running this program has read/write/update/delete access +# to that database +# +# It is recommended that you run this program on the local Asterisk machine +# +# This script is to run perpetually querying every second to place new phone +# calls from the vicidial_hopper based upon how many available agents there are +# and the value of the auto_dial_level setting in the campaign screen of the +# admin web page +# +# It is good practice to keep this program running by placing the associated +# KEEPALIVE script running every minute to ensure this program is always running +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# changes: +# 50125-1201 - Changed dial timeout to 120 seconds from 180 seconds +# 50317-0954 - Added duplicate check per cycle to account for DB lockups +# 50322-1302 - Added campaign custom callerid feature +# 50324-1353 - Added optional variable to record ring time thru separate diap prefix +# 50606-2308 - Added code to ensure no calls placed out on inactive campaign +# 50617-1248 - Added code to place LOGOUT entry in auto-paused user logs +# 50620-1349 - Added custom vdad transfer AGI extension per campaign +# 50810-1610 - Added database server variable definitions lookup +# 50810-1630 - Added max_vicidial_trunks server limiter on active lines +# 50812-0957 - corrected max_trunks logic, added update of server every 25 sec +# 60120-1522 - corrected time error for hour variable, caused agi problems +# + + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n [--delay=XXX] = delay of XXX seconds per loop, default 2.5 seconds\n\n"; + } + else + { + if ($args =~ /--delay=/i) + { + @data_in = split(/--delay=/,$args); + $loop_delay = $data_in[1]; + print " LOOP DELAY OVERRIDE!!!!! = $loop_delay seconds\n\n"; + $loop_delay = ($loop_delay * 1000); + } + else + { + $loop_delay = '2500'; + } + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag, set to 0 for no debug messages, On an active system this will generate hundreds of lines of output per minute + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +print "no command line options set\n"; + $loop_delay = '2500'; + $DB=1; +} +### end parsing run-time options ### + + +# constants +$US='__'; +$MT[0]=''; +$RECcount=''; ### leave blank for no REC count +$RECprefix='7'; ### leave blank for no REC prefix + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + + &get_time_now; # update time/date variables + + $event_string='PROGRAM STARTED||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||'; + &event_logger; # writes to the log and if debug flag is set prints to STDOUT + +use lib './lib', '../lib'; +use Time::HiRes ('gettimeofday','usleep','sleep'); # necessary to have perl sleep command of less than one second +use Net::MySQL; + + ### connect to MySQL database defined in the AST_SERVER_conf.pl file + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + +### Grab Server values from the database +$stmtA = "SELECT telnet_host,telnet_port,ASTmgrUSERNAME,ASTmgrSECRET,ASTmgrUSERNAMEupdate,ASTmgrUSERNAMElisten,ASTmgrUSERNAMEsend,max_vicidial_trunks,answer_transfer_agent,local_gmt,ext_context FROM servers where server_ip = '$server_ip';"; +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBtelnet_host = "$record->[0]"; + $DBtelnet_port = "$record->[1]"; + $DBASTmgrUSERNAME = "$record->[2]"; + $DBASTmgrSECRET = "$record->[3]"; + $DBASTmgrUSERNAMEupdate = "$record->[4]"; + $DBASTmgrUSERNAMElisten = "$record->[5]"; + $DBASTmgrUSERNAMEsend = "$record->[6]"; + $DBmax_vicidial_trunks = "$record->[7]"; + $DBanswer_transfer_agent= "$record->[8]"; + $DBSERVER_GMT = "$record->[9]"; + $DBext_context = "$record->[10]"; + if ($DBtelnet_host) {$telnet_host = $DBtelnet_host;} + if ($DBtelnet_port) {$telnet_port = $DBtelnet_port;} + if ($DBASTmgrUSERNAME) {$ASTmgrUSERNAME = $DBASTmgrUSERNAME;} + if ($DBASTmgrSECRET) {$ASTmgrSECRET = $DBASTmgrSECRET;} + if ($DBASTmgrUSERNAMEupdate) {$ASTmgrUSERNAMEupdate = $DBASTmgrUSERNAMEupdate;} + if ($DBASTmgrUSERNAMElisten) {$ASTmgrUSERNAMElisten = $DBASTmgrUSERNAMElisten;} + if ($DBASTmgrUSERNAMEsend) {$ASTmgrUSERNAMEsend = $DBASTmgrUSERNAMEsend;} + if ($DBmax_vicidial_trunks) {$max_vicidial_trunks = $DBmax_vicidial_trunks;} + if ($DBanswer_transfer_agent) {$answer_transfer_agent = $DBanswer_transfer_agent;} + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + if ($DBext_context) {$ext_context = $DBext_context;} + } + } + + + $event_string='LOGGED INTO MYSQL SERVER ON 1 CONNECTION|'; + &event_logger; + +$one_day_interval = 12; # 1 month loops for one year +while($one_day_interval > 0) +{ + + $endless_loop=5760000; # 30 days minutes at XXX seconds per loop + + while($endless_loop > 0) + { + &get_time_now; + + ############################################################################### + ###### first figure out how many calls should be placed for each campaign per server + ############################################################################### + @DBlive_user=@MT; + @DBlive_server_ip=@MT; + @DBlive_campaign=@MT; + @DBlive_conf_exten=@MT; + @DBcampaigns=@MT; + @DBIPaddress=@MT; + @DBIPcampaign=@MT; + @DBIPactive=@MT; + @DBIPvdadexten=@MT; + @DBIPcount=@MT; + @DBIPadlevel=@MT; + @DBIPdialtimeout=@MT; + @DBIPdialprefix=@MT; + @DBIPcampaigncid=@MT; + @DBIPexistcalls=@MT; + @DBIPgoalcalls=@MT; + @DBIPmakecalls=@MT; + + $active_line_counter=0; + $user_counter=0; + $user_campaigns = '|'; + $user_campaigns_counter = 0; + $user_campaignIP = '|'; + $user_CIPct = 0; + + ##### Get a listing of the users that are active and ready to take calls + ##### Also get a listing of the campaigns and campaigns/serverIP that will be used + $dbhA->query("SELECT user,server_ip,campaign_id,conf_exten FROM vicidial_live_agents where status IN('READY','QUEUE','INCALL','DONE') and server_ip='$server_ip' and last_update_time > '$BDtsSQLdate' order by last_call_time"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBlive_user[$user_counter] = "$record->[0]"; + $DBlive_server_ip[$user_counter] = "$record->[1]"; + $DBlive_campaign[$user_counter] = "$record->[2]"; + $DBlive_conf_exten[$user_counter] = "$record->[3]"; + + if ($user_campaigns !~ /\|$DBlive_campaign[$user_counter]\|/i) + { + $user_campaigns .= "$DBlive_campaign[$user_counter]|"; + $DBcampaigns[$user_campaigns_counter] = $DBlive_campaign[$user_counter]; + $user_campaigns_counter++; + } + if ($user_campaignIP !~ /\|$DBlive_campaign[$user_counter]__$DBlive_server_ip[$user_counter]\|/i) + { + $user_campaignIP .= "$DBlive_campaign[$user_counter]__$DBlive_server_ip[$user_counter]|"; + $DBIPcampaign[$user_CIPct] = "$DBlive_campaign[$user_counter]"; + $DBIPaddress[$user_CIPct] = "$DBlive_server_ip[$user_counter]"; + $user_CIPct++; + } + $user_counter++; + } + } + + $event_string="LIVE AGENTS LOGGED IN: $user_counter"; + &event_logger; + + $user_CIPct = 0; + foreach(@DBIPcampaign) + { + $user_counter=0; + foreach(@DBlive_campaign) + { + if ( ($DBlive_campaign[$user_counter] =~ /$DBIPcampaign[$user_CIPct]/i) && ($DBlive_server_ip[$user_counter] =~ /$DBIPaddress[$user_CIPct]/i) ) + { + $DBIPcount[$user_CIPct]++ + } + $user_counter++; + } + + ### grab the dial_level and multiply by active agents to get your goalcalls + $DBIPadlevel[$user_CIPct]=0; + $dbhA->query("SELECT auto_dial_level,local_call_time,dial_timeout,dial_prefix,campaign_cid,active,campaign_vdad_exten FROM vicidial_campaigns where campaign_id='$DBIPcampaign[$user_CIPct]'"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBIPadlevel[$user_CIPct] = "$record->[0]"; + $DBIPcalltime[$user_CIPct] = "$record->[1]"; + $DBIPdialtimeout[$user_CIPct] = "$record->[2]"; + $DBIPdialprefix[$user_CIPct] = "$record->[3]"; + $DBIPcampaigncid[$user_CIPct] = "$record->[4]"; + $DBIPactive[$user_CIPct] = "$record->[5]"; + $DBIPvdadexten[$user_CIPct] = "$record->[6]"; + } + } + + $DBIPgoalcalls[$user_CIPct] = ($DBIPadlevel[$user_CIPct] * $DBIPcount[$user_CIPct]); + if ($DBIPactive[$user_CIPct] =~ /N/) {$DBIPgoalcalls[$user_CIPct] = 0;} + $DBIPgoalcalls[$user_CIPct] = sprintf("%.0f", $DBIPgoalcalls[$user_CIPct]); + + $event_string="$DBIPcampaign[$user_CIPct] $DBIPaddress[$user_CIPct]: agents: $DBIPcount[$user_CIPct] dial_level: $DBIPadlevel[$user_CIPct]"; + &event_logger; + + $active_line_counter=0; + $active_line_goal=0; + ### see how many total VDAD calls are going on right now for max limiter + $dbhA->query("SELECT count(*) FROM vicidial_auto_calls where server_ip='$DBIPaddress[$user_CIPct]' and status IN('SENT','RINGING','LIVE','XFER');"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $active_line_counter = "$record->[0]"; + } + } + + ### see how many calls are alrady active per campaign per server and + ### subtract that number from goalcalls to determine how many new + ### calls need to be placed in this loop + if ($DBIPcampaign[$user_CIPct] =~ /CLOSER/) + {$campaign_query = "campaign_id LIKE \"CL%\"";} + else {$campaign_query = "campaign_id='$DBIPcampaign[$user_CIPct]'";} + $dbhA->query("SELECT count(*) FROM vicidial_auto_calls where $campaign_query and server_ip='$DBIPaddress[$user_CIPct]' and status IN('SENT','RINGING','LIVE','XFER','CLOSER');"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBIPexistcalls[$user_CIPct] = "$record->[0]"; + } + } + + $DBIPmakecalls[$user_CIPct] = ($DBIPgoalcalls[$user_CIPct] - $DBIPexistcalls[$user_CIPct]); + $MVT_msg = ''; + $active_line_goal = ($active_line_counter + $DBIPmakecalls[$user_CIPct]); + if ($active_line_goal > $max_vicidial_trunks) + { + $MVT_msg = "MVT override: $max_vicidial_trunks"; + $DBIPmakecalls[$user_CIPct] = ($max_vicidial_trunks - $active_line_counter); + } + $event_string="$DBIPcampaign[$user_CIPct] $DBIPaddress[$user_CIPct]: Calls to place: $DBIPmakecalls[$user_CIPct] ($DBIPgoalcalls[$user_CIPct] - $DBIPexistcalls[$user_CIPct]) $MVT_msg"; + &event_logger; + + $user_CIPct++; + } + + ############################################################################### + ###### second lookup leads and place calls for each campaign/server_ip + ###### go one lead at a time and place the call by inserting a record into vicidial_manager + ############################################################################### + + $user_CIPct = 0; + foreach(@DBIPcampaign) + { + ##### calculate what gmt_offset_now values are within the allowed local_call_time setting + if ($DBIPcalltime[$user_CIPct] =~ /24hours/) + { + $p='13'; + $GMT_allowed = '|'; + while ($p > -13) + { + $tz = sprintf("%.2f", $p); $GMT_allowed .= "$tz|"; + $p = ($p - 0.25); + } + } + if ($DBIPcalltime[$user_CIPct] =~ /9am-9pm/) + { + $p='13'; + $GMT_allowed = '|'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 9) && ($hour <= 20) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "$tz|";} + $p = ($p - 0.25); + } + } + if ($DBIPcalltime[$user_CIPct] =~ /9am-5pm/) + { + $p='13'; + $GMT_allowed = '|'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 9) && ($hour <= 16) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "$tz|";} + $p = ($p - 0.25); + } + } + if ($DBIPcalltime[$user_CIPct] =~ /12pm-5pm/) + { + $p='13'; + $GMT_allowed = '|'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 12) && ($hour <= 16) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "$tz|";} + $p = ($p - 0.25); + } + } + if ($DBIPcalltime[$user_CIPct] =~ /12pm-9pm/) + { + $p='13'; + $GMT_allowed = '|'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 12) && ($hour <= 20) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "$tz|";} + $p = ($p - 0.25); + } + } + if ($local_call_time[$i] =~ /5pm-9pm/) + { + $p='13'; + $GMT_allowed = '|'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 17) && ($hour <= 20) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "$tz|";} + $p = ($p - 0.25); + } + } + + + + $event_string="$DBIPcampaign[$user_CIPct] $DBIPaddress[$user_CIPct]: CALLING"; + &event_logger; + $call_CMPIPct=0; + $lead_id_call_list='|'; + my $UDaffected_rows=0; + if ($call_CMPIPct < $DBIPmakecalls[$user_CIPct]) + { + $stmtA = "UPDATE vicidial_hopper set status='QUEUE', user='VDAD_$server_ip' where campaign_id='$DBIPcampaign[$user_CIPct]' and status='READY' order by hopper_id LIMIT $DBIPmakecalls[$user_CIPct]"; + print "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $UDaffected_rows = $dbhA->get_affected_rows_length; + print "hopper rows updated to QUEUE: |$UDaffected_rows|\n"; + + if ($UDaffected_rows) + { + $lead_id=''; $phone_code=''; $phone_number=''; $called_count=''; + while ($call_CMPIPct < $UDaffected_rows) + { + $stmtA = "SELECT lead_id FROM vicidial_hopper where campaign_id='$DBIPcampaign[$user_CIPct]' and status='QUEUE' and user='VDAD_$server_ip' LIMIT 1"; + print "|$stmtA|\n"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countCUSTDATA=0; + while ( $record = $iter->each) + { + $lead_id = "$record->[0]"; + } + } + + if ($lead_id_call_list =~ /\|$lead_id\|/) + { + print "!!!!!!!!!!!!!!!!duplicate lead_id for this run: |$lead_id| $lead_id_call_list\n"; + open(DUPout, ">>/home/cron/VDAD_DUPLICATE.$file_date") + || die "Can't open /home/cron/VDAD_DUPLICATE.$file_date: $!\n"; + print DUPout "$now_date-----$lead_id_call_list-----$lead_id\n"; + close(DUPout); + } + else + { + $stmtA = "UPDATE vicidial_hopper set status='INCALL' where lead_id='$lead_id'"; + print "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $UQaffected_rows = $dbhA->get_affected_rows_length; + print "hopper row updated to INCALL: |$UQaffected_rows|$lead_id|\n"; + + $stmtA = "SELECT * FROM vicidial_list where lead_id='$lead_id';"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countCUSTDATA=0; + while ( $record = $iter->each) + { + $gmt_offset_now = "$record->[8]"; + $phone_code = "$record->[10]"; + $phone_number = "$record->[11]"; + $called_count = "$record->[30]"; + + $rec_countCUSTDATA++; + } + } + if ( ($rec_countCUSTDATA) && ($GMT_allowed !~ /\|$gmt_offset_now\|/) ) + { + $rec_countCUSTDATA=0; + $stmtA = "DELETE FROM vicidial_hopper where lead_id='$lead_id'"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + # $GMT_allowed + $event_string = "| out of call time range lead DELETED $affected_rows|$lead_id|$phone_number|$gmt_offset_now|"; + &event_logger; + } + + if ($rec_countCUSTDATA) + { + ### update called_count + $called_count++; + + $stmtA = "UPDATE vicidial_list set called_since_last_reset='Y', called_count='$called_count',user='VDAD' where lead_id='$lead_id'"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $stmtA = "DELETE FROM vicidial_hopper where lead_id='$lead_id'"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $CCID_on=0; $CCID=''; + $local_DEF = 'Local/'; + $local_AMP = '@'; + $Local_out_prefix = '9'; + $Local_dial_timeout = '60'; + if ($DBIPdialtimeout[$user_CIPct] > 4) {$Local_dial_timeout = $DBIPdialtimeout[$user_CIPct];} + $Local_dial_timeout = ($Local_dial_timeout * 1000); + if (length($DBIPdialprefix[$user_CIPct]) > 0) {$Local_out_prefix = "$DBIPdialprefix[$user_CIPct]";} + if (length($DBIPvdadexten[$user_CIPct]) > 0) {$VDAD_dial_exten = "$DBIPvdadexten[$user_CIPct]";} + else {$VDAD_dial_exten = "$answer_transfer_agent";} + + if (length($DBIPcampaigncid[$user_CIPct]) > 6) {$CCID = "$DBIPcampaigncid[$user_CIPct]"; $CCID_on++;} + if ($DBIPdialprefix[$user_CIPct] =~ /x/i) {$Local_out_prefix = '';} + + if ($RECcount) + { + if ( (length($RECprefix)>0) && ($called_count < $RECcount) ) + {$Local_out_prefix .= "$RECprefix";} + } + $PADlead_id = sprintf("%09s", $lead_id); while (length($PADlead_id) > 9) {chop($PADlead_id);} + + $lead_id_call_list .= "$lead_id|"; + + ### use manager middleware-app to connect the next call to the meetme room + # VmmddhhmmssLLLLLLLLL + $VqueryCID = "V$CIDdate$PADlead_id"; + if ($CCID_on) {$CIDstring = "\"$VqueryCID\" <$CCID>";} + else {$CIDstring = "$VqueryCID";} + + ### insert a NEW record to the vicidial_manager table to be processed + $stmtA = "INSERT INTO vicidial_manager values('','','$SQLdate','NEW','N','$DBIPaddress[$user_CIPct]','','Originate','$VqueryCID','Exten: $VDAD_dial_exten','Context: $ext_context','Channel: $local_DEF$Local_out_prefix$phone_code$phone_number$local_AMP$ext_context','Priority: 1','Callerid: $CIDstring','Timeout: $Local_dial_timeout','','','','')"; + $dbhA->query($stmtA); + + # $GMT_allowed + $event_string = "| number call dialed|$DBIPcampaign[$user_CIPct]|$VqueryCID|$stmtA|$gmt_offset_now|"; + &event_logger; + + ### insert a SENT record to the vicidial_auto_calls table + $stmtA = "INSERT INTO vicidial_auto_calls values('','$DBIPaddress[$user_CIPct]','$DBIPcampaign[$user_CIPct]','SENT','$lead_id','','$VqueryCID','','$phone_code','$phone_number','$SQLdate')"; + $dbhA->query($stmtA); + + ### sleep for a tenth of a second to not flood the server with new calls + usleep(1*100*1000); + + } + } + $call_CMPIPct++; + } + } + + } + + $user_CIPct++; + } + + + + + + + + &get_time_now; + + ############################################################################### + ###### third we will grab the callerids of the vicidial_auto_calls records and check for dead calls + ###### we also check to make sure that it isn't a call that has been transferred, + ###### if it has been we need to leave the vicidial_list status alone + ############################################################################### + + @KLcallerid = @MT; + @KLserver_ip = @MT; + @KLchannel = @MT; + $kill_vac=0; + + $stmtA = "SELECT callerid,server_ip,channel,uniqueid FROM vicidial_auto_calls where server_ip='$server_ip' order by call_time;"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countCUSTDATA=0; + while ( $record = $iter->each) + { + $KLcallerid[$kill_vac] = "$record->[0]"; + $KLserver_ip[$kill_vac] = "$record->[1]"; + $KLchannel[$kill_vac] = "$record->[2]"; + $KLuniqueid[$kill_vac] = "$record->[3]"; + + $kill_vac++; + } + } + + $kill_vac=0; + foreach(@KLcallerid) + { + if (length($KLserver_ip[$kill_vac]) > 7) + { + $end_epoch=0; $CLuniqueid=''; + + $stmtA = "SELECT end_epoch,uniqueid FROM call_log where caller_code='$KLcallerid[$kill_vac]' and server_ip='$KLserver_ip[$kill_vac]' order by end_epoch, start_time desc limit 1;"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countCUSTDATA=0; + while ( $record = $iter->each) + { + $end_epoch = "$record->[0]"; + $CLuniqueid = "$record->[1]"; + } + } + + if ( (length($KLuniqueid[$kill_vac]) > 15) && (length($CLuniqueid) < 15) ) + { + $stmtA = "SELECT end_epoch,uniqueid FROM call_log where uniqueid='$KLuniqueid[$kill_vac]' and server_ip='$KLserver_ip[$kill_vac]' order by end_epoch, start_time desc limit 1;"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countCUSTDATA=0; + while ( $record = $iter->each) + { + $end_epoch = "$record->[0]"; + $CLuniqueid = "$record->[1]"; + } + } + } + if ($end_epoch > 1000) + { + $CLlead_id=''; $auto_call_id=''; $CLstatus=''; $CLcampaign_id=''; $CLphone_number=''; $CLphone_code=''; + + $stmtA = "SELECT auto_call_id,lead_id,phone_number,status,campaign_id,phone_code FROM vicidial_auto_calls where callerid='$KLcallerid[$kill_vac]'"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countCUSTDATA=0; + while ( $record = $iter->each) + { + $auto_call_id = "$record->[0]"; + $CLlead_id = "$record->[1]"; + $CLphone_number = "$record->[2]"; + $CLstatus = "$record->[3]"; + $CLcampaign_id = "$record->[4]"; + $CLphone_code = "$record->[5]"; + } + } + $stmtA = "DELETE from vicidial_auto_calls where auto_call_id='$auto_call_id'"; + # $stmtA = "UPDATE vicidial_auto_calls set status='PAUSED' where callerid='$KLcallerid[$kill_vac]'"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| dead call vac deleted|$auto_call_id|$CLlead_id|$KLcallerid[$kill_vac]|$end_epoch|$affected_rows|$KLchannel[$kill_vac]|"; + &event_logger; + + if ($CLstatus !~ /XFER|CLOSER/) + { + if ($CLstatus =~ /LIVE/) {$CLnew_status = 'DROP';} + else + { + $CLnew_status = 'NA'; + $end_epoch = ($now_date_epoch + 1); + $stmtA = "INSERT INTO vicidial_log (uniqueid,lead_id,campaign_id,call_date,start_epoch,status,phone_code,phone_number,user,processed,length_in_sec,end_epoch) values('$CLuniqueid','$CLlead_id','$CLcampaign_id','$SQLdate','$now_date_epoch','NA','$CLphone_code','$CLphone_number','VDAD','N','1','$end_epoch')"; + if($M){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); + + $event_string = "| dead NA call added to log $CLuniqueid|$CLlead_id|$CLphone_number|$CLstatus|$CLnew_status|"; + &event_logger; + + } + + $stmtA = "UPDATE vicidial_list set status='$CLnew_status' where lead_id='$CLlead_id'"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| dead call vac lead marked $CLnew_status|$CLlead_id|$CLphone_number|$CLstatus|"; + &event_logger; + + $stmtA = "UPDATE vicidial_live_agents set status='PAUSED',random_id='10' where callerid='$KLcallerid[$kill_vac]';"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| dead call vla agent PAUSED $affected_rows|$CLlead_id|$CLphone_number|$CLstatus|"; + &event_logger; + } + else + { + $event_string = "| dead call vac XFERd do nothing|$CLlead_id|$CLphone_number|$CLstatus|"; + &event_logger; + } + } + } + $kill_vac++; + } + + + + ### pause agents that have disconnected or closed their apps over 30 seconds ago + $stmtA = "UPDATE vicidial_live_agents set status='PAUSED',random_id='10' where server_ip='$server_ip' and last_update_time < '$PDtsSQLdate' and status NOT IN('PAUSED')"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| lagged call vla agent PAUSED $affected_rows|$PDtsSQLdate|$BDtsSQLdate|$tsSQLdate|"; + &event_logger; + + if ($affected_rows > 0) + { + @VALOuser=@MT; @VALOcampaign=@MT; @VALOtimelog=@MT; @VALOextension=@MT; + $logcount=0; + $stmtA = "SELECT user,campaign_id,last_update_time,extension FROM vicidial_live_agents where server_ip='$server_ip' and status = 'PAUSED' and random_id='10' order by last_update_time desc limit $affected_rows"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countCUSTDATA=0; + while ( $record = $iter->each) + { + $VALOuser[$logcount] = "$record->[0]"; + $VALOcampaign[$logcount] = "$record->[1]"; + $VALOtimelog[$logcount] = "$record->[2]"; + $VALOextension[$logcount] = "$record->[3]"; + $logcount++; + } + } + $logrun=0; + foreach(@VALOuser) + { + $stmtA = "INSERT INTO vicidial_user_log (user,event,campaign_id,event_date,event_epoch) values('$VALOuser[$logrun]','LOGOUT','$VALOcampaign[$logrun]','$SQLdate','$now_date_epoch');"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| lagged agent LOGOUT entry inserted $VALOuser[$logrun]|$VALOcampaign[$logrun]|$VALOextension[$logcount]|"; + &event_logger; + + $logrun++; + } + + } + + + ### delete call records that are SENT for over 3 minutes + $stmtA = "DELETE FROM vicidial_auto_calls where server_ip='$server_ip' and call_time < '$XDSQLdate' and status NOT IN('XFER','CLOSER','LIVE')"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| lagged call vac agent DELETED $affected_rows|$XDSQLdate|"; + &event_logger; + + + + + + ############################################################################### + ###### last, wait for a little bit and repeat the loop + ############################################################################### + + ### sleep for 2 and a half seconds before beginning the loop again + usleep(1*$loop_delay*1000); + + $endless_loop--; + if($DB){print STDERR "\nloop counter: |$endless_loop|\n";} + + ### putting a blank file called "VDAD.kill" in the directory will automatically safely kill this program + if (-e '/home/cron/VDAD.kill') + { + unlink('/home/cron/VDAD.kill'); + $endless_loop=0; + $one_day_interval=0; + print "\nPROCESS KILLED MANUALLY... EXITING\n\n" + } + if ($endless_loop =~ /0$/) # run every ten cycles (about 25 seconds) + { + ### delete call records that are LIVE for over 10 minutes + $stmtA = "DELETE FROM vicidial_auto_calls where server_ip='$server_ip' and call_time < '$TDSQLdate' and status NOT IN('XFER','CLOSER')"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| lagged call vac agent DELETED $affected_rows|$TDSQLdate|LIVE|"; + &event_logger; + + ### Grab Server values from the database in case they've changed + $stmtA = "SELECT max_vicidial_trunks,answer_transfer_agent,local_gmt,ext_context FROM servers where server_ip = '$server_ip';"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBmax_vicidial_trunks = "$record->[0]"; + $DBanswer_transfer_agent= "$record->[1]"; + $DBSERVER_GMT = "$record->[2]"; + $DBext_context = "$record->[3]"; + if ($DBmax_vicidial_trunks) {$max_vicidial_trunks = $DBmax_vicidial_trunks;} + if ($DBanswer_transfer_agent) {$answer_transfer_agent = $DBanswer_transfer_agent;} + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + if ($DBext_context) {$ext_context = $DBext_context;} + } + } + + $event_string = "| updating server parameters $max_vicidial_trunks|$answer_transfer_agent|$SERVER_GMT|$ext_context|"; + &event_logger; + + &get_time_now; + + #@psoutput = `/bin/ps -f --no-headers -A`; + @psoutput = `/bin/ps -o "%p %a" --no-headers -A`; + + $running_listen = 0; + + $i=0; + foreach (@psoutput) + { + chomp($psoutput[$i]); + + @psline = split(/\/usr\/bin\/perl /,$psoutput[$i]); + + if ($psline[1] =~ /AST_manager_li/) {$running_listen++;} + + $i++; + } + + if (!$running_listen) + { + $endless_loop=0; + $one_day_interval=0; + print "\nPROCESS KILLED NO LISTENER RUNNING... EXITING\n\n"; + } + + if($DB){print "checking to see if listener is dead |$running_listen|\n";} + } + + $bad_grabber_counter=0; + + + } + + + if($DB){print "DONE... Exiting... Goodbye... See you later... Not really, initiating next loop...\n";} + + $event_string='HANGING UP|'; + &event_logger; + + $one_day_interval--; + +} + + $event_string='CLOSING DB CONNECTION|'; + &event_logger; + + + $dbhA->close; + + + if($DB){print "DONE... Exiting... Goodbye... See you later... Really I mean it this time\n";} + + +exit; + + + + + + + + + + + + + +sub get_time_now #get the current date and time and epoch for logging call lengths and datetimes +{ + $secX = time(); +$secX = time(); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($secX); + $LOCAL_GMT_OFF = $SERVER_GMT; + $LOCAL_GMT_OFF_STD = $SERVER_GMT; + if ($isdst) {$LOCAL_GMT_OFF++;} + +$GMT_now = ($secX - ($LOCAL_GMT_OFF * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_now); + if ($hour < 10) {$hour = "0$hour";} + if ($min < 10) {$min = "0$min";} + + if ($DB) {print "TIME DEBUG: $LOCAL_GMT_OFF_STD|$LOCAL_GMT_OFF|$isdst| GMT: $hour:$min\n";} + +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$hour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday $hour:$min:$sec"; +$file_date = "$year-$mon-$mday"; + $CIDdate = "$mon$mday$hour$min$sec"; + $tsSQLdate = "$year$mon$mday$hour$min$sec"; + $SQLdate = "$year-$mon-$mday $hour:$min:$sec"; + +$BDtarget = ($secX - 10); +($Bsec,$Bmin,$Bhour,$Bmday,$Bmon,$Byear,$Bwday,$Byday,$Bisdst) = localtime($BDtarget); +$Byear = ($Byear + 1900); +$Bmon++; +if ($Bmon < 10) {$Bmon = "0$Bmon";} +if ($Bmday < 10) {$Bmday = "0$Bmday";} +if ($Bhour < 10) {$Bhour = "0$Bhour";} +if ($Bmin < 10) {$Bmin = "0$Bmin";} +if ($Bsec < 10) {$Bsec = "0$Bsec";} + $BDtsSQLdate = "$Byear$Bmon$Bmday$Bhour$Bmin$Bsec"; + +$PDtarget = ($secX - 30); +($Psec,$Pmin,$Phour,$Pmday,$Pmon,$Pyear,$Pwday,$Pyday,$Pisdst) = localtime($PDtarget); +$Pyear = ($Pyear + 1900); +$Pmon++; +if ($Pmon < 10) {$Pmon = "0$Pmon";} +if ($Pmday < 10) {$Pmday = "0$Pmday";} +if ($Phour < 10) {$Phour = "0$Phour";} +if ($Pmin < 10) {$Pmin = "0$Pmin";} +if ($Psec < 10) {$Psec = "0$Psec";} + $PDtsSQLdate = "$Pyear$Pmon$Pmday$Phour$Pmin$Psec"; + +$XDtarget = ($secX - 120); +($Xsec,$Xmin,$Xhour,$Xmday,$Xmon,$Xyear,$Xwday,$Xyday,$Xisdst) = localtime($XDtarget); +$Xyear = ($Xyear + 1900); +$Xmon++; +if ($Xmon < 10) {$Xmon = "0$Xmon";} +if ($Xmday < 10) {$Xmday = "0$Xmday";} +if ($Xhour < 10) {$Xhour = "0$Xhour";} +if ($Xmin < 10) {$Xmin = "0$Xmin";} +if ($Xsec < 10) {$Xsec = "0$Xsec";} + $XDSQLdate = "$Xyear-$Xmon-$Xmday $Xhour:$Xmin:$Xsec"; + +$TDtarget = ($secX - 600); +($Tsec,$Tmin,$Thour,$Tmday,$Tmon,$Tyear,$Twday,$Tyday,$Tisdst) = localtime($TDtarget); +$Tyear = ($Tyear + 1900); +$Tmon++; +if ($Tmon < 10) {$Tmon = "0$Tmon";} +if ($Tmday < 10) {$Tmday = "0$Tmday";} +if ($Thour < 10) {$Thour = "0$Thour";} +if ($Tmin < 10) {$Tmin = "0$Tmin";} +if ($Tsec < 10) {$Tsec = "0$Tsec";} + $TDSQLdate = "$Tyear-$Tmon-$Tmday $Thour:$Tmin:$Tsec"; + +} + + + + + +sub event_logger +{ + +if ($DB) {print "$now_date|$event_string|\n";} + ### open the log file for writing ### + open(Lout, ">>$VDADLOGfile") + || die "Can't open $VDADLOGfile: $!\n"; + + print Lout "$now_date|$event_string|\n"; + + close(Lout); + +$event_string=''; +} diff --git a/AST_VDhopper.pl b/AST_VDhopper.pl new file mode 100644 index 0000000..baa5b92 --- /dev/null +++ b/AST_VDhopper.pl @@ -0,0 +1,570 @@ +#!/usr/bin/perl +# +# AST_VDhopper.pl version 0.4 +# +# DESCRIPTION: +# uses Net::MySQL to update the VICIDIAL leads hopper for the new streamlined +# approach of allocating leads to client machines. +# +# SUMMARY: +# This program was designed for people using the Asterisk PBX with VICIDIAL +# +# For the client to use VICIDIAL, this program must be in the cron running +# every minute +# +# For this program to work you need to have the "asterisk" MySQL database +# created and create the tables listed in the CONF_MySQL.txt file, also make sure +# that the machine running this program has read/write/update/delete access +# to that database +# +# It is recommended that you run this program on the local Asterisk machine +# +# If this script is run ever minute and you are getting close to no leads after +# a minute, you may want to play with the variables below to streamline for +# your usage +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# 50810-1613 - Added database server variable definitions lookup +# 60215-1106 - Added Scheduled Callback release functionality +# 60228-1623 - Change Callback activation to set the called_since_last_reset=N +# 60228-1735 - Added hopper gmt validation to remove gmt outside of time range +# + +# constants +$DB=0; # Debug flag, set to 0 for no debug messages, On an active system this will generate lots of lines of output per minute +$US='__'; +$MT[0]=''; + +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$Fhour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} +$now_date = "$year-$mon-$mday $hour:$min:$sec"; + +if (!$VDHLOGfile) {$VDHLOGfile = "/home/cron/hopper.$year-$mon-$mday";} + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options(must stay in this order):\n [--debug] = debug\n [--dbgmt] = show GMT offset of records as they are inserted into hopper\n [-t] = test\n [--level=XXX] = force a hopper_level of XXX\n [--campaign=XXX] = run for campaign XXX only\n\n"; + } + else + { + if ($args =~ /--campaign=/i) + { + # print "\n|$ARGS|\n\n"; + @data_in = split(/--campaign=/,$args); + $CLIcampaign = $data_in[1]; + } + else + {$CLIcampaign = '';} + if ($args =~ /--level=/i) + { + @data_in = split(/--level=/,$args); + $CLIlevel = $data_in[1]; + $CLIlevel =~ s/ .*$//gi; + $CLIlevel =~ s/\D//gi; + print "\n-----HOPPER LEVEL OVERRIDE: $CLIlevel -----\n\n"; + } + else + {$CLIlevel = '';} + if ($args =~ /--debug/i) + { + $DB=1; + print "\n-----DEBUG -----\n\n"; + } + if ($args =~ /--dbgmt/i) + { + $DB_show_offset=1; + print "\n-----DEBUG GMT -----\n\n"; + } + if ($args =~ /-t/i) + { + $T=1; $TEST=1; + print "\n-----TESTING -----\n\n"; + } + if ($args =~ /--wipe-hopper-clean/i) + { + $wipe_hopper_clean=1; + } + } +} +else +{ +print "no command line options set\n"; +} + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +sleep(1); ### sleep for 5 seconds to stagger cron script load + +use lib './lib', '../lib'; +use Net::MySQL; + + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + +### Grab Server values from the database +$stmtA = "SELECT local_gmt FROM servers where server_ip = '$server_ip';"; +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBSERVER_GMT = "$record->[0]"; + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + } + } + + +$secX = time(); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($secX); + $LOCAL_GMT_OFF = $SERVER_GMT; + $LOCAL_GMT_OFF_STD = $SERVER_GMT; + if ($isdst) {$LOCAL_GMT_OFF++;} + + +$GMT_now = ($secX - ($LOCAL_GMT_OFF * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_now); + $mon++; + $year = ($year + 1900); + if ($mon < 10) {$mon = "0$mon";} + if ($mday < 10) {$mday = "0$mday";} + if ($hour < 10) {$hour = "0$hour";} + if ($min < 10) {$min = "0$min";} + if ($sec < 10) {$sec = "0$sec";} + + + if ($DB) {print "TIME DEBUG: $LOCAL_GMT_OFF_STD|$LOCAL_GMT_OFF|$isdst| GMT: $hour:$min\n";} + +if ($wipe_hopper_clean) + { + $stmtA = "DELETE from vicidial_hopper;"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if ($DB) {print "Hopper Wiped Clean: $affected_rows\n";} + $event_string = "|HOPPER WIPE CLEAN|"; + &event_logger; + + exit; + } + + + +### BEGIN Change CBHOLD status leads to CALLBK if their vicidial_callbacks time has passed +$stmtA = "SELECT count(*) FROM vicidial_callbacks where callback_time <= '$now_date' and status='ACTIVE' and recipient='ANYONE';"; +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $CBHOLD_count = "$record->[0]"; + } + } + + if ($DB) {print "CALLBACK HOLD: $CBHOLD_count|$stmtA|\n";} + +if ($CBHOLD_count > 0) + { + $update_leads=''; + $stmtA = "SELECT lead_id FROM vicidial_callbacks where callback_time <= '$now_date' and status='ACTIVE' and recipient='ANYONE';"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $cbc=0; + while ( $record = $iter->each) + { + $lead_ids[$cbc] = $record->[0]; + $update_leads .= "'$lead_ids[$cbc]',"; + $cbc++; + } + } + if ($cbc > 0) + { + chop($update_leads); + + $stmtA = "UPDATE vicidial_list set status='CALLBK', called_since_last_reset='N' where lead_id IN($update_leads);"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if ($DB) {print "Scheduled Callbacks Activated: $affected_rows\n";} + $event_string = "|CALLBACKS LISTACT|$affected_rows|"; + &event_logger; + + $stmtA = "UPDATE vicidial_callbacks set status='LIVE' where lead_id IN($update_leads);"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if ($DB) {print "Scheduled Callbacks Activated: $affected_rows\n";} + $event_string = "|CALLBACKS CB ACT |$affected_rows|"; + &event_logger; + + } + } +### END Change CBHOLD status leads to CALLBK if their vicidial_callbacks time has passed + + + +@campaign_id=@MT; + +if ($CLIcampaign) + { + $stmtA = "SELECT * from vicidial_campaigns where campaign_id='$CLIcampaign'"; + } +else + { + $stmtA = "SELECT * from vicidial_campaigns where active='Y'"; + } + +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + $campaign_id[$rec_count] = "$record->[0]"; + $dial_status_a[$rec_count] = "$record->[3]"; + $dial_status_b[$rec_count] = "$record->[4]"; + $dial_status_c[$rec_count] = "$record->[5]"; + $dial_status_d[$rec_count] = "$record->[6]"; + $dial_status_e[$rec_count] = "$record->[7]"; + $lead_order[$rec_count] = "$record->[8]"; + if (!$CLIlevel) + {$hopper_level[$rec_count] = "$record->[13]";} + else + {$hopper_level[$rec_count] = "$CLIlevel";} + $local_call_time[$rec_count] = "$record->[16]"; + $rec_count++; + } + } + +$i=0; +foreach(@campaign_id) + { + ##### calculate what gmt_offset_now values are within the allowed local_call_time setting + $GMT_allowed = ''; + + if ($local_call_time[$i] =~ /24hours/) + { + $p='13'; + while ($p > -13) + { + $tz = sprintf("%.2f", $p); $GMT_allowed .= "'$tz',"; + $p = ($p - 0.25); + } + } + if ($local_call_time[$i] =~ /9am-9pm/) + { + $p='13'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 9) && ($hour <= 20) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "'$tz',";} + $p = ($p - 0.25); + } + } + if ($local_call_time[$i] =~ /9am-5pm/) + { + $p='13'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 9) && ($hour <= 16) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "'$tz',";} + $p = ($p - 0.25); + } + } + if ($local_call_time[$i] =~ /12pm-5pm/) + { + $p='13'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 12) && ($hour <= 16) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "'$tz',";} + $p = ($p - 0.25); + } + } + if ($local_call_time[$i] =~ /12pm-9pm/) + { + $p='13'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 12) && ($hour <= 20) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "'$tz',";} + $p = ($p - 0.25); + } + } + if ($local_call_time[$i] =~ /5pm-9pm/) + { + $p='13'; + while ($p > -13) + { + $GMT_test = ($GMT_now + ($p * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_test); + if ( ($hour >= 17) && ($hour <= 20) ){$tz = sprintf("%.2f", $p); $GMT_allowed .= "'$tz',";} + $p = ($p - 0.25); + } + } + + $GMT_allowed .= "'99'"; + + + if ($DB) {print "Starting hopper run for $campaign_id[$i] campaign: GMT: $local_call_time[$1] $GMT_allowed HOPPER: $hopper_level[$i] \n";} + + ### Delete the DONE leads if there are any + $stmtA = "DELETE from vicidial_hopper where campaign_id='$campaign_id[$i]' and status IN('DONE');"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if ($DB) {print " hopper DONE cleared: $affected_rows\n";} + + ### Delete the leads that are out of GMT time range if there are any + $stmtA = "DELETE from vicidial_hopper where campaign_id='$campaign_id[$i]' and gmt_offset_now NOT IN($GMT_allowed);"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if ($DB) {print " hopper GMT BAD cleared: $affected_rows\n";} + + $hopper_ready_count=0; + $dbhA->query("SELECT count(*) from vicidial_hopper where campaign_id='$campaign_id[$i]' and status='READY';"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countA=0; + while ( $record = $iter->each) + { + $hopper_ready_count = $record->[0]; + if ($DB) {print " hopper READY count: $hopper_ready_count\n";} + $rec_countA++; + } + } + $event_string = "|$campaign_id[$i]|$hopper_level[$i]|$hopper_ready_count|$local_call_time[$i]|$GMT_allowed|"; + &event_logger; + + ##### IF hopper level is below set minimum, then try to add more leads ##### + if ($hopper_ready_count < $hopper_level[$i]) + { + if ($DB) {print " hopper too low ($hopper_ready_count|$hopper_level[$i]) starting hopper dump\n";} + + $dbhA->query("SELECT list_id FROM vicidial_lists where campaign_id='$campaign_id[$i]' and active='Y';"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countLISTS=0; + $camp_lists = ''; + while ( $record = $iter->each) + { + $camp_lists .= "'$record->[0]',"; + $rec_countLISTS++; + } + chop($camp_lists); + } + if ($DB) {print " campaign lists count: $rec_countLISTS | $camp_lists\n";} + + $dbhA->query("SELECT count(*) FROM vicidial_list where called_since_last_reset='N' and status IN('$dial_status_a[$i]','$dial_status_b[$i]','$dial_status_c[$i]','$dial_status_d[$i]','$dial_status_e[$i]') and list_id IN($camp_lists) and gmt_offset_now IN($GMT_allowed)"); + $campaign_leads_to_call=0; + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $campaign_leads_to_call = "$record->[0]"; + if ($DB) {print " leads to call count: $campaign_leads_to_call\n";} + } + } + if ( ($lead_order[$i] eq "DOWN COUNT 2nd NEW") or ($lead_order[$i] eq "DOWN COUNT 3rd NEW") or ($lead_order[$i] eq "DOWN COUNT 4th NEW") ) + { + $dbhA->query("SELECT count(*) FROM vicidial_list where called_since_last_reset='N' and status IN('NEW') and list_id IN($camp_lists) and gmt_offset_now IN($GMT_allowed)"); + $NEW_campaign_leads_to_call=0; + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $NEW_campaign_leads_to_call = "$record->[0]"; + if ($DB) {print " NEW leads to call count: $NEW_campaign_leads_to_call\n";} + } + } + } + + ##### IF no NEW leads to be called, error out of this campaign ##### + if ( ( ($lead_order[$i] eq "DOWN COUNT 2nd NEW") or ($lead_order[$i] eq "DOWN COUNT 3rd NEW") or ($lead_order[$i] eq "DOWN COUNT 4th NEW") ) && ($NEW_campaign_leads_to_call > 0) ) {$GOOD=1;} + else + { + if ($DB) {print " ERROR CANNOT ADD ANY NEW LEADS TO HOPPER\n";} + } + + ##### IF no leads to be called, error out of this campaign ##### + if ($campaign_leads_to_call < 1) + { + if ($DB) {print " ERROR CANNOT ADD ANY LEADS TO HOPPER\n";} + } + else + { + if ($DB) {print " Getting Leads to add to hopper\n";} + $dbhA->query("SELECT lead_id FROM vicidial_hopper where campaign_id='$campaign_id[$i]';"); + $rec_countLISTS=0; + $lead_id_lists = ''; + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $lead_id_lists .= "'$record->[0]',"; + $rec_countLISTS++; + } + $lead_id_lists .= "'0'"; + } + + $order_stmt=''; + $NEW_count = 0; + $NEW_level = 0; + $OTHER_level = $hopper_level[$i]; + if ($lead_order[$i] eq "DOWN") {$order_stmt = 'order by lead_id asc';} + if ($lead_order[$i] eq "UP") {$order_stmt = 'order by lead_id desc';} + if ($lead_order[$i] eq "UP LAST NAME") {$order_stmt = 'order by last_name desc, lead_id asc';} + if ($lead_order[$i] eq "DOWN LAST NAME") {$order_stmt = 'order by last_name, lead_id asc';} + if ($lead_order[$i] eq "UP PHONE") {$order_stmt = 'order by phone_number desc, lead_id asc';} + if ($lead_order[$i] eq "DOWN PHONE") {$order_stmt = 'order by phone_number, lead_id asc';} + if ($lead_order[$i] eq "UP COUNT") {$order_stmt = 'order by called_count desc, lead_id asc';} + if ($lead_order[$i] eq "DOWN COUNT") {$order_stmt = 'order by called_count, lead_id asc';} + if ($lead_order[$i] eq "DOWN COUNT 2nd NEW") {$NEW_count = 2;} + if ($lead_order[$i] eq "DOWN COUNT 3rd NEW") {$NEW_count = 3;} + if ($lead_order[$i] eq "DOWN COUNT 4th NEW") {$NEW_count = 4;} + + if ($NEW_count > 0) + { + $NEW_level = int($hopper_level[$i] / $NEW_count); + $OTHER_level = ($hopper_level[$i] - $NEW_level); + $order_stmt = 'order by called_count, lead_id asc'; + if ($DB) {print " looking for $NEW_level NEW leads mixed in with $OTHER_level other leads\n";} + + $stmtA = "SELECT lead_id,list_id,gmt_offset_now,phone_number,state FROM vicidial_list where called_since_last_reset='N' and status IN('NEW') and list_id IN($camp_lists) and lead_id NOT IN($lead_id_lists) and gmt_offset_now IN($GMT_allowed) $order_stmt limit $NEW_level;"; + # if ($DB) {print " STMT |$stmtA|\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $NEW_rec_countLEADS=0; + @NEW_leads_to_hopper=@MT; + @NEW_lists_to_hopper=@MT; + @NEW_phone_to_hopper=@MT; + while ( $record = $iter->each) + { + $NEW_leads_to_hopper[$NEW_rec_countLEADS] = "$record->[0]"; + $NEW_lists_to_hopper[$NEW_rec_countLEADS] = "$record->[1]"; + $NEW_gmt_to_hopper[$NEW_rec_countLEADS] = "$record->[2]"; + $NEW_phone_to_hopper[$NEW_rec_countLEADS] = "$record->[3]"; + if ($DB_show_offset) {print "LEAD_ADD: $record->[2] $record->[3] $record->[4]\n";} + $NEW_rec_countLEADS++; + } + } + + } + + + + if ($DB) {print " lead call order: $order_stmt\n";} + $stmtA = "SELECT lead_id,list_id,gmt_offset_now,phone_number,state FROM vicidial_list where called_since_last_reset='N' and status IN('$dial_status_a[$i]','$dial_status_b[$i]','$dial_status_c[$i]','$dial_status_d[$i]','$dial_status_e[$i]') and list_id IN($camp_lists) and lead_id NOT IN($lead_id_lists) and gmt_offset_now IN($GMT_allowed) $order_stmt limit $OTHER_level;"; + # if ($DB) {print " STMT |$stmtA|\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countLEADS=0; $NEW_dec=99; $NEW_in=0; + @leads_to_hopper=@MT; + @lists_to_hopper=@MT; + while ( $record = $iter->each) + { + if ( ($NEW_count > 0) && ($NEW_rec_countLEADS > $NEW_in) ) + { + if ($DB_show_offset) {print "NEW_COUNT: $NEW_count|$NEW_dec|$NEW_in|$NEW_rec_countLEADS\n";} + if ($NEW_count > $NEW_dec) + { + $NEW_dec++; + } + else + { + $leads_to_hopper[$rec_countLEADS] = "$NEW_leads_to_hopper[$NEW_in]"; + $lists_to_hopper[$rec_countLEADS] = "$NEW_lists_to_hopper[$NEW_in]"; + $gmt_to_hopper[$rec_countLEADS] = "$NEW_gmt_to_hopper[$NEW_in]"; + if ($DB_show_offset) {print "LEAD_ADD: $NEW_leads_to_hopper[$NEW_in] $NEW_phone_to_hopper[$NEW_in]\n";} + $rec_countLEADS++; + $NEW_in++; + $NEW_dec=2; + } + } + $leads_to_hopper[$rec_countLEADS] = "$record->[0]"; + $lists_to_hopper[$rec_countLEADS] = "$record->[1]"; + $gmt_to_hopper[$rec_countLEADS] = "$record->[2]"; + if ($DB_show_offset) {print "LEAD_ADD: $record->[2] $record->[3] $record->[4]\n";} + $rec_countLEADS++; + } + } + + if ($DB) {print " Adding to hopper: $rec_countLEADS\n";} + $event_string = "|$campaign_id[$i]|Added to hopper $rec_countLEADS|"; + &event_logger; + + $h=0; + foreach(@leads_to_hopper) + { + if ($leads_to_hopper[$h] != '0') + { + $stmtA = "INSERT INTO vicidial_hopper (lead_id,campaign_id,status,user,list_id,gmt_offset_now) values('$leads_to_hopper[$h]','$campaign_id[$i]','READY','','$lists_to_hopper[$h]','$gmt_to_hopper[$h]');"; + $dbhA->query("$stmtA"); + } + $h++; + } + if ($DB) {print " DONE with this campaign\n";} + + } + } + + + $i++; + } + + +$dbhA->close; + +if($DB){print "DONE... Exiting... Goodbye... See you later... Really, I mean it :)\n";} + +exit; + + + +sub event_logger { + ### open the log file for writing ### + open(Lout, ">>$VDHLOGfile") + || die "Can't open $VDHLOGfile: $!\n"; + + print Lout "$now_date|$event_string|\n"; + + close(Lout); + +$event_string=''; +} + + diff --git a/AST_VDremote_agents.pl b/AST_VDremote_agents.pl new file mode 100644 index 0000000..a042eff --- /dev/null +++ b/AST_VDremote_agents.pl @@ -0,0 +1,493 @@ +#!/usr/bin/perl +# +# AST_VDremote_agents.pl version 0.1 +# +# DESCRIPTION: +# uses Net::MySQL to keep remote agents logged in to the VICIDIAL system +# +# SUMMARY: +# This program was designed for people using the Asterisk PBX with VICIDIAL +# +# For the client to use VICIDIAL with remote agents, this must always be running +# +# It is recommended that you run this program on the local Asterisk machine +# +# This script is to run perpetually querying every second to update the remote +# agents that should appear to be logged in so that the calls can be transferred +# out to them properly. +# +# It is good practice to keep this program running by placing the associated +# KEEPALIVE script running every minute to ensure this program is always running +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# changes: +# 50215-0954 - First version of script +# 50810-1615 - Added database server variable definitions lookup +# + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-v] = verbose debug messages\n [--delay=XXX] = delay of XXX seconds per loop, default 2 seconds\n\n"; + } + else + { + if ($args =~ /--delay=/i) + { + @data_in = split(/--delay=/,$args); + $loop_delay = $data_in[1]; + print " LOOP DELAY OVERRIDE!!!!! = $loop_delay seconds\n\n"; + $loop_delay = ($loop_delay * 1000); + } + else + { + $loop_delay = '2000'; + } + if ($args =~ /-v/i) + { + $V=1; # Debug flag, set to 0 for no debug messages, On an active system this will generate hundreds of lines of output per minute + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +print "no command line options set\n"; + $loop_delay = '2000'; + $V=1; +} +### end parsing run-time options ### + + +# constants +$US='__'; +$MT[0]=''; + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +use lib './lib', '../lib'; +use Time::HiRes ('gettimeofday','usleep','sleep'); # necessary to have perl sleep command of less than one second +use Net::MySQL; + + ### connect to MySQL database defined in the AST_SERVER_conf.pl file + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + +### Grab Server values from the database +$stmtA = "SELECT local_gmt FROM servers where server_ip = '$server_ip';"; +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBSERVER_GMT = "$record->[0]"; + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + } + } + + + &get_time_now; # update time/date variables + + $event_string='PROGRAM STARTED||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||'; + &event_logger; # writes to the log and if debug flag is set prints to STDOUT + + + $event_string='LOGGED INTO MYSQL SERVER ON 1 CONNECTION|'; + &event_logger; + +$one_day_interval = 12; # 1 month loops for one year +while($one_day_interval > 0) +{ + + $endless_loop=5760000; # 30 days minutes at XXX seconds per loop + + while($endless_loop > 0) + { + &get_time_now; + + if ($endless_loop =~ /0$|5$/) + { + ### delete call records that are LIVE for over 10 minutes and last_update_time < '$PDtsSQLdate' + $stmtA = "DELETE FROM vicidial_live_agents where server_ip='$server_ip' and status IN('PAUSED') and extension LIKE \"R/%\""; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| lagged call vla agent DELETED $affected_rows"; + &event_logger; + + + $stmtA = "UPDATE vicidial_live_agents set status='INCALL', last_call_time='$SQLdate' where server_ip='$server_ip' and status IN('QUEUE') and extension LIKE \"R/%\""; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + + $event_string = "| QUEUEd call listing vla UPDATEd $affected_rows"; + &event_logger; + + #@psoutput = `/bin/ps -f --no-headers -A`; + @psoutput = `/bin/ps -o "%p %a" --no-headers -A`; + + $running_listen = 0; + + $i=0; + foreach (@psoutput) + { + chomp($psoutput[$i]); + + @psline = split(/\/usr\/bin\/perl /,$psoutput[$i]); + + if ($psline[1] =~ /AST_manager_li/) {$running_listen++;} + + $i++; + } + + if (!$running_listen) + { + $endless_loop=0; + $one_day_interval=0; + print "\nPROCESS KILLED NO LISTENER RUNNING... EXITING\n\n"; + } + + if($DB){print "checking to see if listener is dead |$running_listen|\n";} + } + + + + $user_counter=0; + @DBremote_user=@MT; + @DBremote_server_ip=@MT; + @DBremote_campaign=@MT; + @DBremote_conf_exten=@MT; + @DBremote_closer=@MT; + @DBremote_random=@MT; + @loginexistsRANDOM=@MT; + @loginexistsALL=@MT; + @VD_user=@MT; + @VD_extension=@MT; + @VD_status=@MT; + @VD_uniqueid=@MT; + @VD_callerid=@MT; + @VD_random=@MT; + ############################################################################### + ###### first grab all of the ACTIVE remote agents information from the database + ############################################################################### + $dbhA->query("SELECT * FROM vicidial_remote_agents where status IN('ACTIVE') and server_ip='$server_ip' order by user_start"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $user_start = "$record->[1]"; + $number_of_lines = "$record->[2]"; + $conf_exten = "$record->[4]"; + $campaign_id = "$record->[6]"; + $closer_campaigns = "$record->[7]"; + + $y=0; + while ($y < $number_of_lines) + { + $random = int( rand(9999999)) + 10000000; + $user_id = ($user_start + $y); + $DBremote_user[$user_counter] = "$user_id"; + $DBremote_server_ip[$user_counter] = "$server_ip"; + $DBremote_campaign[$user_counter] = "$campaign_id"; + $DBremote_conf_exten[$user_counter] = "$conf_exten"; + $DBremote_closer[$user_counter] = "$closer_campaigns"; + $DBremote_random[$user_counter] = "$random"; + + $y++; + $user_counter++; + } + + } + if ($V) {print STDERR "$user_counter live remote agents ACTIVE\n";} + } + + + ############################################################################### + ###### second traverse array of remote agents to be active and insert or update + ###### in vicidial_live_agents table + ############################################################################### + $h=0; + foreach(@DBremote_user) + { + if (length($DBremote_user[$h])>1) + { + + ### check to see if the record exists and only needs random number update + $dbhA->query("SELECT count(*) FROM vicidial_live_agents where user='$DBremote_user[$h]' and server_ip='$server_ip' and campaign_id='$DBremote_campaign[$h]' and conf_exten='$DBremote_conf_exten[$h]' and closer_campaigns='$DBremote_closer[$h]'"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $loginexistsRANDOM[$h] = "$record->[0]"; + } + } + if ($loginexistsRANDOM[$h] > 0) + { + $stmtA = "UPDATE vicidial_live_agents set random_id='$DBremote_random[$h]' where user='$DBremote_user[$h]' and server_ip='$server_ip' and campaign_id='$DBremote_campaign[$h]' and conf_exten='$DBremote_conf_exten[$h]' and closer_campaigns='$DBremote_closer[$h]'"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if ($V) {print STDERR "$DBremote_user[$h] $DBremote_campaign[$h] ONLY RANDOM ID UPDATE: $affected_rows\n";} + } + ### check if record for user on server exists at all in vicidial_live_agents + else + { + $dbhA->query("SELECT count(*) FROM vicidial_live_agents where user='$DBremote_user[$h]' and server_ip='$server_ip'"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $loginexistsALL[$h] = "$record->[0]"; + } + } + if ($loginexistsALL[$h] > 0) + { + $stmtA = "UPDATE vicidial_live_agents set random_id='$DBremote_random[$h]',campaign_id='$DBremote_campaign[$h]',conf_exten='$DBremote_conf_exten[$h]',closer_campaigns='$DBremote_closer[$h]', status='READY' where user='$DBremote_user[$h]' and server_ip='$server_ip'"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if ($V) {print STDERR "$DBremote_user[$h] ALL UPDATE: $affected_rows\n";} + } + ### no records exist so insert a new one + else + { + $stmtA = "INSERT INTO vicidial_live_agents (user,server_ip,conf_exten,extension,status,campaign_id,random_id,last_call_time,last_update_time,last_call_finish,closer_campaigns,channel,uniqueid,callerid) values('$DBremote_user[$h]','$server_ip','$DBremote_conf_exten[$h]','R/$DBremote_user[$h]','READY','$DBremote_campaign[$h]','$DBremote_random[$h]','$SQLdate','$tsSQLdate','$SQLdate','$DBremote_closer[$h]','','','')"; + $dbhA->query("$stmtA"); + if ($V) {print STDERR "$DBremote_user[$h] NEW INSERT\n";} + } + } + } + $h++; + } + + + ############################################################################### + ###### third validate that the calls that the vicidial_live_agents are on are not dead + ###### and if they are wipe out the values and set the agent record back to READY + ############################################################################### + $dbhA->query("SELECT user,extension,status,uniqueid,callerid FROM vicidial_live_agents where extension LIKE \"R/%\" and server_ip='$server_ip' and uniqueid > 10"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $z=0; + while ( $record = $iter->each) + { + $VDuser = "$record->[0]"; + $VDextension = "$record->[1]"; + $VDstatus = "$record->[2]"; + $VDuniqueid = "$record->[3]"; + $VDcallerid = "$record->[4]"; + $VDrandom = int( rand(9999999)) + 10000000; + + $VD_user[$z] = "$VDuser"; + $VD_extension[$z] = "$VDextension"; + $VD_status[$z] = "$VDstatus"; + $VD_uniqueid[$z] = "$VDuniqueid"; + $VD_callerid[$z] = "$VDcallerid"; + $VD_random[$z] = "$VDrandom"; + + $z++; + } + if ($V) {print STDERR "$z remote agents on calls\n";} + } + $z=0; + foreach(@VD_user) + { + $dbhA->query("SELECT count(*) FROM vicidial_auto_calls where uniqueid='$VD_uniqueid[$z]' and server_ip='$server_ip'"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $autocallexists[$z] = "$record->[0]"; + } + } + if ($autocallexists[$z] < 1) + { + $stmtA = "UPDATE vicidial_live_agents set random_id='$VD_random[$z]',status='READY', last_call_finish='$SQLdate',lead_id='',uniqueid='',callerid='',channel='' where user='$VD_user[$z]' and server_ip='$server_ip'"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if ($V) {print STDERR "$VD_user[$z] CALL WIPE UPDATE: $affected_rows|$VD_uniqueid[$z]\n";} + } + + $z++; + } + + + + + + + ############################################################################### + ###### last, wait for a little bit and repeat the loop + ############################################################################### + + ### sleep for X seconds before beginning the loop again + usleep(1*$loop_delay*1000); + + $endless_loop--; + if($DB){print STDERR "\nloop counter: |$endless_loop|\n";} + + ### putting a blank file called "VDAD.kill" in the directory will automatically safely kill this program + if (-e '/home/cron/VDAD.kill') + { + unlink('/home/cron/VDAD.kill'); + $endless_loop=0; + $one_day_interval=0; + print "\nPROCESS KILLED MANUALLY... EXITING\n\n" + } + + $bad_grabber_counter=0; + + + } + + + if($DB){print "DONE... Exiting... Goodbye... See you later... Not really, initiating next loop...\n";} + + $event_string='HANGING UP|'; + &event_logger; + + $one_day_interval--; + +} + + $event_string='CLOSING DB CONNECTION|'; + &event_logger; + + + $dbhA->close; + + + if($DB){print "DONE... Exiting... Goodbye... See you later... Really I mean it this time\n";} + + +exit; + + + + + + + + + + + + + +sub get_time_now #get the current date and time and epoch for logging call lengths and datetimes +{ + $secX = time(); +$secX = time(); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($secX); + $LOCAL_GMT_OFF = $SERVER_GMT; + $LOCAL_GMT_OFF_STD = $SERVER_GMT; + if ($isdst) {$LOCAL_GMT_OFF++;} + +$GMT_now = ($secX - ($LOCAL_GMT_OFF * 3600)); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($GMT_now); + if ($hour < 10) {$hour = "0$hour";} + if ($min < 10) {$min = "0$min";} + + if ($DB) {print "TIME DEBUG: $LOCAL_GMT_OFF_STD|$LOCAL_GMT_OFF|$isdst| GMT: $hour:$min\n";} + +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$Fhour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday $hour:$min:$sec"; + $CIDdate = "$mon$mday$hour$min$sec"; + $tsSQLdate = "$year$mon$mday$hour$min$sec"; + $SQLdate = "$year-$mon-$mday $hour:$min:$sec"; + $filedate = "$year-$mon-$mday"; + +$BDtarget = ($secX - 10); +($Bsec,$Bmin,$Bhour,$Bmday,$Bmon,$Byear,$Bwday,$Byday,$Bisdst) = localtime($BDtarget); +$Byear = ($Byear + 1900); +$Bmon++; +if ($Bmon < 10) {$Bmon = "0$Bmon";} +if ($Bmday < 10) {$Bmday = "0$Bmday";} +if ($Bhour < 10) {$Bhour = "0$Bhour";} +if ($Bmin < 10) {$Bmin = "0$Bmin";} +if ($Bsec < 10) {$Bsec = "0$Bsec";} + $BDtsSQLdate = "$Byear$Bmon$Bmday$Bhour$Bmin$Bsec"; + +$PDtarget = ($secX - 30); +($Psec,$Pmin,$Phour,$Pmday,$Pmon,$Pyear,$Pwday,$Pyday,$Pisdst) = localtime($PDtarget); +$Pyear = ($Pyear + 1900); +$Pmon++; +if ($Pmon < 10) {$Pmon = "0$Pmon";} +if ($Pmday < 10) {$Pmday = "0$Pmday";} +if ($Phour < 10) {$Phour = "0$Phour";} +if ($Pmin < 10) {$Pmin = "0$Pmin";} +if ($Psec < 10) {$Psec = "0$Psec";} + $PDtsSQLdate = "$Pyear$Pmon$Pmday$Phour$Pmin$Psec"; + +$XDtarget = ($secX - 120); +($Xsec,$Xmin,$Xhour,$Xmday,$Xmon,$Xyear,$Xwday,$Xyday,$Xisdst) = localtime($XDtarget); +$Xyear = ($Xyear + 1900); +$Xmon++; +if ($Xmon < 10) {$Xmon = "0$Xmon";} +if ($Xmday < 10) {$Xmday = "0$Xmday";} +if ($Xhour < 10) {$Xhour = "0$Xhour";} +if ($Xmin < 10) {$Xmin = "0$Xmin";} +if ($Xsec < 10) {$Xsec = "0$Xsec";} + $XDSQLdate = "$Xyear-$Xmon-$Xmday $Xhour:$Xmin:$Xsec"; + +$TDtarget = ($secX - 600); +($Tsec,$Tmin,$Thour,$Tmday,$Tmon,$Tyear,$Twday,$Tyday,$Tisdst) = localtime($TDtarget); +$Tyear = ($Tyear + 1900); +$Tmon++; +if ($Tmon < 10) {$Tmon = "0$Tmon";} +if ($Tmday < 10) {$Tmday = "0$Tmday";} +if ($Thour < 10) {$Thour = "0$Thour";} +if ($Tmin < 10) {$Tmin = "0$Tmin";} +if ($Tsec < 10) {$Tsec = "0$Tsec";} + $TDSQLdate = "$Tyear-$Tmon-$Tmday $Thour:$Tmin:$Tsec"; + +} + + + + + +sub event_logger +{ + +if ($DB) {print "$now_date|$event_string|\n";} + ### open the log file for writing ### + open(Lout, ">>/home/cron/remote.$filedate") + || die "Can't open /home/cron/remote.$filedate: $!\n"; + + print Lout "$now_date|$event_string|\n"; + + close(Lout); + +$event_string=''; +} diff --git a/AST_cleanup_agent_log.pl b/AST_cleanup_agent_log.pl new file mode 100644 index 0000000..7f405a0 --- /dev/null +++ b/AST_cleanup_agent_log.pl @@ -0,0 +1,236 @@ +#!/usr/bin/perl +# +# AST_cleanup_agent_log.pl version 0.1 +# +# DESCRIPTION: +# to be run frequently to clean up the vicidial_agent_log to fix erroneous time +# calculations due to out-of-order vicidial_agent_log updates. This happens 0.5% +# of the time in our test setups, but that leads to inaccurate time logs so we +# wrote this script to fix the miscalculations +# +# This program only needs to be run by one server +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# + + +# constants +$COUNTER_OUTPUT=1; # set to 1 to display the counter as the script runs +$US='__'; +$MT[0]=''; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n[-debugX] = Extra-verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + print "\n----- DEBUGGING -----\n\n"; + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + print "\n----- TEST RUN, NO UPDATES -----\n\n"; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +use Net::MySQL; + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + + + if ($DB) {print " - cleaning up pause time\n";} + ### Grab any pause time record greater than 50000 + $stmtA = "SELECT agent_log_id,pause_epoch,wait_epoch from vicidial_agent_log where pause_sec>50000;"; + if ($DBX) {print "$stmtA\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $i=0; + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBout = ''; + $agent_log_id[$i] = "$record->[0]"; + $pause_epoch[$i] = "$record->[1]"; + $wait_epoch[$i] = "$record->[2]"; + $pause_sec[$i] = int($wait_epoch[$i] - $pause_epoch[$i]); + if ( ($pause_sec[$i] < 0) || ($pause_sec[$i] > 50000) ) + { + $DBout = "Override output: $pause_sec[$i]"; + $pause_sec[$i] = 0; + } + if ($DBX) {print "$i - $agent_log_id[$i] |$wait_epoch[$i]|$pause_epoch[$i]|$pause_sec[$i]|$DBout|\n";} + $i++; + } + } + $h=0; + while ($h < $i) + { + $stmtA = "UPDATE vicidial_agent_log set pause_sec='$pause_sec[$h]' where agent_log_id='$agent_log_id[$h]';"; + if($DBX){print STDERR "\n|$stmtA|\n";} + if (!$TEST) {$dbhA->query($stmtA); }# or die "Couldn't execute query: |$stmtA|\n"; + $h++; + } + if ($DB) {print STDERR " Pause times fixed: $h\n";} + + + + + + @agent_log_id=@MT; + @wait_epoch=@MT; + + if ($DBX) {print "\n\n";} + if ($DB) {print " - cleaning up wait time\n";} + ### Grab any pause time record greater than 50000 + $stmtA = "SELECT agent_log_id,wait_epoch,talk_epoch from vicidial_agent_log where wait_sec>50000;"; + if ($DBX) {print "$stmtA\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $i=0; + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBout = ''; + $agent_log_id[$i] = "$record->[0]"; + $wait_epoch[$i] = "$record->[1]"; + $talk_epoch[$i] = "$record->[2]"; + $wait_sec[$i] = int($talk_epoch[$i] - $wait_epoch[$i]); + if ( ($wait_sec[$i] < 0) || ($wait_sec[$i] > 50000) ) + { + $DBout = "Override output: $wait_sec[$i]"; + $wait_sec[$i] = 0; + } + if ($DBX) {print "$i - $agent_log_id[$i] |$talk_epoch[$i]|$wait_epoch[$i]|$wait_sec[$i]|$DBout|\n";} + $i++; + } + } + $h=0; + while ($h < $i) + { + $stmtA = "UPDATE vicidial_agent_log set wait_sec='$wait_sec[$h]' where agent_log_id='$agent_log_id[$h]';"; + if($DBX){print STDERR "\n|$stmtA|\n";} + if (!$TEST) {$dbhA->query($stmtA); }# or die "Couldn't execute query: |$stmtA|\n"; + $h++; + } + if ($DB) {print STDERR " Wait times fixed: $h\n";} + + + + + + @agent_log_id=@MT; + @talk_epoch=@MT; + + if ($DBX) {print "\n\n";} + if ($DB) {print " - cleaning up talk time\n";} + ### Grab any pause time record greater than 50000 + $stmtA = "SELECT agent_log_id,talk_epoch,dispo_epoch from vicidial_agent_log where talk_sec>50000;"; + if ($DBX) {print "$stmtA\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $i=0; + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBout = ''; + $agent_log_id[$i] = "$record->[0]"; + $talk_epoch[$i] = "$record->[1]"; + $dispo_epoch[$i] = "$record->[2]"; + $talk_sec[$i] = int($dispo_epoch[$i] - $talk_epoch[$i]); + if ( ($talk_sec[$i] < 0) || ($talk_sec[$i] > 50000) ) + { + $DBout = "Override output: $talk_sec[$i]"; + $talk_sec[$i] = 0; + } + if ($DBX) {print "$i - $agent_log_id[$i] |$dispo_epoch[$i]|$talk_epoch[$i]|$talk_sec[$i]|$DBout|\n";} + $i++; + } + } + $h=0; + while ($h < $i) + { + $stmtA = "UPDATE vicidial_agent_log set talk_sec='$talk_sec[$h]' where agent_log_id='$agent_log_id[$h]';"; + if($DBX){print STDERR "|$stmtA|\n";} + if (!$TEST) {$dbhA->query($stmtA); }# or die "Couldn't execute query: |$stmtA|\n"; + $h++; + } + if ($DB) {print STDERR " Talk times fixed: $h\n";} + + + + + + @agent_log_id=@MT; + @dispo_epoch=@MT; + + if ($DBX) {print "\n\n";} + if ($DB) {print " - cleaning up dispo time\n";} + $stmtA = "UPDATE vicidial_agent_log set dispo_sec='0' where dispo_sec>50000;"; + if($DBX){print STDERR "|$stmtA|\n";} + if (!$TEST) + { + $dbhA->query($stmtA); # or die "Couldn't execute query: |$stmtA|\n"; + $affected_rows = $dbhA->get_affected_rows_length; + } + if ($DB) {print STDERR " Bad Dispo times zeroed out: $affected_rows\n";} + + + + + + + + + + + + if ($DB) {print STDERR "\nDONE\n";} + + + + $dbhA->close; + + +exit; + + + + + + diff --git a/AST_conf_update.pl b/AST_conf_update.pl new file mode 100644 index 0000000..70d874a --- /dev/null +++ b/AST_conf_update.pl @@ -0,0 +1,220 @@ +#!/usr/bin/perl +# +# AST_conf_update.pl version 0.3 +# +# DESCRIPTION: +# uses the Asterisk Manager interface and Net::MySQL to update whether a conference +# is still in use or not. If not in use 3 times in a row the extension in the +# conferences DB record is erased freeing that conference to be used again +# +# SUMMARY: +# This program was designed for people using the Asterisk PBX with conferences +# +# This program should be in the cron running every minute (like AST_vm_update.pl) +# +# For this program to work you need to have the "asterisk" MySQL database +# created with the conferences table in it, also make sure +# that the account running this program has read/write/update/delete access +# to that database +# +# It is recommended that you run this program on the local Asterisk machine +# +# If this script is run ever minute there is a theoretical limit of +# 600 conferences that it can check due to the wait interval. If you have +# more than this either change the cron when this script is run or change the +# wait interval below +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# 50810-1532 - Added database server variable definitions lookup +# 50823-1456 - Added commandline arguments for debug at runtime +# + +# constants +$DB=0; # Debug flag, set to 0 for no debug messages per minute +$US='__'; +$MT[0]=''; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +use lib './lib', '../lib'; +use Time::HiRes ('gettimeofday','usleep','sleep'); # necessary to have perl sleep command of less than one second +use Net::MySQL; +use Net::Telnet (); + + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + +### Grab Server values from the database +$stmtA = "SELECT telnet_host,telnet_port,ASTmgrUSERNAME,ASTmgrSECRET,ASTmgrUSERNAMEupdate,ASTmgrUSERNAMElisten,ASTmgrUSERNAMEsend,max_vicidial_trunks,answer_transfer_agent,local_gmt,ext_context FROM servers where server_ip = '$server_ip';"; +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBtelnet_host = "$record->[0]"; + $DBtelnet_port = "$record->[1]"; + $DBASTmgrUSERNAME = "$record->[2]"; + $DBASTmgrSECRET = "$record->[3]"; + $DBASTmgrUSERNAMEupdate = "$record->[4]"; + $DBASTmgrUSERNAMElisten = "$record->[5]"; + $DBASTmgrUSERNAMEsend = "$record->[6]"; + $DBmax_vicidial_trunks = "$record->[7]"; + $DBanswer_transfer_agent= "$record->[8]"; + $DBSERVER_GMT = "$record->[9]"; + $DBext_context = "$record->[10]"; + if ($DBtelnet_host) {$telnet_host = $DBtelnet_host;} + if ($DBtelnet_port) {$telnet_port = $DBtelnet_port;} + if ($DBASTmgrUSERNAME) {$ASTmgrUSERNAME = $DBASTmgrUSERNAME;} + if ($DBASTmgrSECRET) {$ASTmgrSECRET = $DBASTmgrSECRET;} + if ($DBASTmgrUSERNAMEupdate) {$ASTmgrUSERNAMEupdate = $DBASTmgrUSERNAMEupdate;} + if ($DBASTmgrUSERNAMElisten) {$ASTmgrUSERNAMElisten = $DBASTmgrUSERNAMElisten;} + if ($DBASTmgrUSERNAMEsend) {$ASTmgrUSERNAMEsend = $DBASTmgrUSERNAMEsend;} + if ($DBmax_vicidial_trunks) {$max_vicidial_trunks = $DBmax_vicidial_trunks;} + if ($DBanswer_transfer_agent) {$answer_transfer_agent = $DBanswer_transfer_agent;} + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + if ($DBext_context) {$ext_context = $DBext_context;} + } + } + + + +@PTextensions=@MT; @PT_conf_extens=@MT; @PTmessages=@MT; @PTold_messages=@MT; @NEW_messages=@MT; @OLD_messages=@MT; +$dbhA->query("SELECT extension,conf_exten from conferences where server_ip='$server_ip' and extension is NOT NULL and extension != '';"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + $PTextensions[$rec_count] = "$record->[0]"; + $PT_conf_extens[$rec_count] = "$record->[1]"; + $rec_count++; + } + } + +if (!$telnet_port) {$telnet_port = '5038';} + +### connect to asterisk manager through telnet +$t = new Net::Telnet (Port => $telnet_port, + Prompt => '/.*[\$%#>] $/', + Output_record_separator => '',); +#$fh = $t->dump_log("$telnetlog"); # uncomment for telnet log + if (length($ASTmgrUSERNAMEsend) > 3) {$telnet_login = $ASTmgrUSERNAMEsend;} + else {$telnet_login = $ASTmgrUSERNAME;} + +$t->open("$telnet_host"); +$t->waitfor('/0\n$/'); # print login +$t->print("Action: Login\nUsername: $telnet_login\nSecret: $ASTmgrSECRET\n\n"); +$t->waitfor('/Authentication accepted/'); # waitfor auth accepted + + +$i=0; +foreach(@PTextensions) + { + @list_channels=@MT; + $t->buffer_empty; + @list_channels = $t->cmd(String => "Action: Command\nCommand: Meetme list $PT_conf_extens[$i]\n\nAction: Ping\n\n", Prompt => '/Response: Pong.*/'); + + $j=0; + $conf_empty[$i]=0; + $conf_users[$i]=''; + foreach(@list_channels) + { + if($DB){print "|$list_channels[$j]|\n";} + if ($list_channels[$j] =~ /No active conferences|No such conference/i) + {$conf_empty[$i]++;} +# if ($list_channels[$j] =~ /^User /i) +# { +# $userx = ''; +# $userx = $list_channels[$j]; +# $userx =~ s/User \#: //gi; +# $conf_users[$i] .= "$userx|"; +# } + $j++; + } + + if($DB){print "Meetme list $PT_conf_extens[$i]- Exten:|$PTextensions[$i]| Empty:|$conf_empty[$i]| ";} + if (!$conf_empty[$i]) + { + if($DB){print "CONFERENCE STILL HAS PARTICIPANTS, DOING NOTHING FOR THIS CONFERENCE\n";} + if ($PTextensions[$i] =~ /Xtimeout\d$/i) + { + $PTextensions[$i] =~ s/Xtimeout\d$//gi; + $stmtA = "UPDATE conferences set extension='$PTextensions[$i]' where server_ip='$server_ip' and conf_exten='$PT_conf_extens[$i]';"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA) or die "Couldn't execute query:|$stmtA|\n"; + } + } + else + { + $NEWexten[$i] = $PTextensions[$i]; + if ($PTextensions[$i] =~ /Xtimeout3$/i) {$NEWexten[$i] =~ s/Xtimeout3$/Xtimeout2/gi;} + if ($PTextensions[$i] =~ /Xtimeout2$/i) {$NEWexten[$i] =~ s/Xtimeout2$/Xtimeout1/gi;} + if ($PTextensions[$i] =~ /Xtimeout1$/i) {$NEWexten[$i] = '';} + if ( ($PTextensions[$i] !~ /Xtimeout\d$/i) and (length($PTextensions[$i])> 0) ) {$NEWexten[$i] .= 'Xtimeout3';} + + + $stmtA = "UPDATE conferences set extension='$NEWexten[$i]' where server_ip='$server_ip' and conf_exten='$PT_conf_extens[$i]';"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA) or die "Couldn't execute query:|$stmtA|\n"; + } + + $i++; + ### sleep for 10 hundredths of a second + usleep(1*100*1000); + } + + +$t->buffer_empty; +@hangup = $t->cmd(String => "Action: Logoff\n\n", Prompt => "/.*/"); +$t->buffer_empty; +$ok = $t->close; + +$dbhA->close; + +if($DB){print "DONE... Exiting... Goodbye... See you later... \n";} + +exit; + + + + diff --git a/AST_flush_DBqueue.pl b/AST_flush_DBqueue.pl new file mode 100644 index 0000000..cf76ffa --- /dev/null +++ b/AST_flush_DBqueue.pl @@ -0,0 +1,189 @@ +#!/usr/bin/perl +# +# AST_flush_DBqueue.pl version 0.2 +# +# DESCRIPTION: +# - clears out mysql records for this server for the ACQS vicidial_manager table +# - optimizes tables used frequently by VICIDIAL +# +# It is recommended that you run this program on the local Asterisk machine +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# + +$secX = time(); + ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + $year = ($year + 1900); + $yy = $year; $yy =~ s/^..//gi; + $mon++; + if ($mon < 10) {$mon = "0$mon";} + if ($mday < 10) {$mday = "0$mday";} + if ($hour < 10) {$hour = "0$hour";} + if ($min < 10) {$min = "0$min";} + if ($sec < 10) {$sec = "0$sec";} +$SQLdate_NOW="$year-$mon-$mday $hour:$min:$sec"; + +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time()-3600); + $year = ($year + 1900); + $yy = $year; $yy =~ s/^..//gi; + $mon++; + if ($mon < 10) {$mon = "0$mon";} + if ($mday < 10) {$mday = "0$mday";} + if ($hour < 10) {$hour = "0$hour";} + if ($min < 10) {$min = "0$min";} + if ($sec < 10) {$sec = "0$sec";} +$SQLdate_NEG_1hour="$year-$mon-$mday $hour:$min:$sec"; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-q] = quiet\n [-t] = test\n [--debug] = debugging messages\n\n"; + } + else + { + if ($args =~ /-q/i) + { + $q=1; $Q=1; + } + if ($args =~ /--debug/i) + { + $DB=1; + print "\n-----DEBUGGING -----\n\n"; + } + if ($args =~ /-t|--test/i) + { + $T=1; $TEST=1; + print "\n-----TESTING -----\n\n"; + } + } +} +else +{ +print "no command line options set\n"; +} +### end parsing run-time options ### + + +if (!$Q) {print "TEST\n\n";} +if (!$Q) {print "NOW DATETIME: $SQLdate_NOW\n";} +if (!$Q) {print "1 HOUR AGO DATETIME: $SQLdate_NEG_1hour\n\n";} + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +use Net::MySQL; + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + + + $stmtA = "delete from vicidial_manager where server_ip='$server_ip' and entry_date < '$SQLdate_NEG_1hour';"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if (!$Q) {print " - vicidial_manager 2 hour flush\n";} + + + $stmtA = "update park_log set status='HUNGUP' where hangup_time is not null;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + $rec_countY=0; + while ($recordA = $iterA->each) + { + if (!$Q) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + if (!$Q) {print " - park_log HUNGUP flush \n";} + + + $stmtA = "optimize table vicidial_manager;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + $rec_countY=0; + while ($recordA = $iterA->each) + { + if (!$Q) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + if (!$Q) {print " - optimize vicidial_manager \n";} + + + $stmtA = "optimize table vicidial_live_agents;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + $rec_countY=0; + while ($recordA = $iterA->each) + { + if (!$Q) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + if (!$Q) {print " - optimize vicidial_live_agents \n";} + + + $stmtA = "optimize table vicidial_auto_calls;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + $rec_countY=0; + while ($recordA = $iterA->each) + { + if (!$Q) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + if (!$Q) {print " - optimize vicidial_auto_calls \n";} + + + $stmtA = "optimize table vicidial_hopper;"; + if($DB){print STDERR "\n|$stmtA|\n";} + if (!$T) {$dbhA->query($stmtA); } + if ($dbhA->has_selected_record) + { + $iterA=$dbhA->create_record_iterator; + $rec_countY=0; + while ($recordA = $iterA->each) + { + if (!$Q) {print "|",$recordA->[0],"|",$recordA->[1],"|",$recordA->[2],"|",$recordA->[3],"|","\n";} + } + } + + if (!$Q) {print " - optimize vicidial_hopper \n";} + + + + + $dbhA->close; + + +exit; + + + + + + diff --git a/AST_manager_kill_hung_congested.pl b/AST_manager_kill_hung_congested.pl new file mode 100644 index 0000000..52636af --- /dev/null +++ b/AST_manager_kill_hung_congested.pl @@ -0,0 +1,335 @@ +#!/usr/bin/perl +# +# AST_manager_kill_hung_congested.pl version 0.2 +# +# Part of the Asterisk Central Queue System (ACQS) +# +# DESCRIPTION: +# kills CONGEST local channels every 15 seconds +# +# For the client program to work in ACQS mode, this program must be running +# +# For this program to work you need to have the "asterisk" MySQL database +# created and create the tables listed in the CONF_MySQL.txt file, also make sure +# that the machine running this program has read/write/update/delete access +# to that database +# +# In your Asterisk server setup you also need to have several things activated +# and defined. See the CONF_Asterisk.txt file for details +# +# put this in the cron to run every minute +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# 50823-1525 - Added commandline debug options with debug printouts +# + +# constants +$DB=0; # Debug flag, set to 0 for no debug messages, On an active system this will generate thousands of lines of output per minute +$US='__'; +$MT[0]=''; + + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n[-debugX] = Extra-verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + + + &get_time_now; + +# $event_string='PROGRAM STARTED||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||'; +# &event_logger; + +#use lib './lib', '../lib'; +use Net::MySQL; +#use Net::Telnet (); + +my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass") +or die "Couldn't connect to database: \n"; + +#$event_string='LOGGED INTO MYSQL SERVER ON 1 CONNECTION|'; +#&event_logger; + +$stmtA = "SELECT channel FROM live_sip_channels where server_ip = '$server_ip' and extension = 'CONGEST' and channel LIKE \"Local%\" limit 99"; +# $event_string="SQL_QUERY|$stmtA|"; +# &event_logger; + +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + if($DB){print STDERR $record->[0],"\n";} + $congest_kill[$rec_count] = "$record->[0]"; + $rec_count++; + } + + } + +$i=0; +foreach(@congest_kill) + { + $i_count = $i; + if ($i_count < 10) {$i_count = "0$i_count";} + + if (length($congest_kill[$i])>0) + { + ### use manager middleware-app to zapbarge call being placed from meetme + $KCqueryCID = "KC$i_count$CIDdate"; + + ### insert a NEW record to the vicidial_manager table to be processed + $stmtA = "INSERT INTO vicidial_manager values('','','$now_date','NEW','N','$server_ip','','Hangup','$KCqueryCID','Channel: $congest_kill[$i]','','','','','','','','','')"; + + $event_string = "SUBRT|killing_congest|KC|$KCqueryCID|$congest_kill[$i]|$stmtA|"; + event_logger; + + if ($DB) {print "KILLING $congest_kill[$i]\n";} + $dbhA->query($stmtA) or die "Couldn't execute query:\n"; + } + $i++; + } + + +sleep(14); + +######## 22222 starting another loop + &get_time_now; +@congest_kill = @MT; + + + +$stmtA = "SELECT channel FROM live_sip_channels where server_ip = '$server_ip' and extension = 'CONGEST' and channel LIKE \"Local%\" limit 99"; +# $event_string="SQL_QUERY|$stmtA|"; +# &event_logger; + +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + if($DB){print STDERR $record->[0],"\n";} + $congest_kill[$rec_count] = "$record->[0]"; + $rec_count++; + } + + } + +$i=0; +foreach(@congest_kill) + { + $i_count = $i; + if ($i_count < 10) {$i_count = "0$i_count";} + + if (length($congest_kill[$i])>0) + { + ### use manager middleware-app to zapbarge call being placed from meetme + $KCqueryCID = "KC$i_count$CIDdate"; + + ### insert a NEW record to the vicidial_manager table to be processed + $stmtA = "INSERT INTO vicidial_manager values('','','$now_date','NEW','N','$server_ip','','Hangup','$KCqueryCID','Channel: $congest_kill[$i]','','','','','','','','','')"; + + $event_string = "SUBRT|killing_congest|KC|$KCqueryCID|$congest_kill[$i]|$stmtA|"; + event_logger; + + if ($DB) {print "KILLING $congest_kill[$i]\n";} + $dbhA->query($stmtA) or die "Couldn't execute query:\n"; + } + $i++; + } + + +sleep(15); + +######## 33333 starting another loop + &get_time_now; +@congest_kill = @MT; + + + + +$stmtA = "SELECT channel FROM live_sip_channels where server_ip = '$server_ip' and extension = 'CONGEST' and channel LIKE \"Local%\" limit 99"; +# $event_string="SQL_QUERY|$stmtA|"; +# &event_logger; + +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + if($DB){print STDERR $record->[0],"\n";} + $congest_kill[$rec_count] = "$record->[0]"; + $rec_count++; + } + + } + +$i=0; +foreach(@congest_kill) + { + $i_count = $i; + if ($i_count < 10) {$i_count = "0$i_count";} + + if (length($congest_kill[$i])>0) + { + ### use manager middleware-app to zapbarge call being placed from meetme + $KCqueryCID = "KC$i_count$CIDdate"; + + ### insert a NEW record to the vicidial_manager table to be processed + $stmtA = "INSERT INTO vicidial_manager values('','','$now_date','NEW','N','$server_ip','','Hangup','$KCqueryCID','Channel: $congest_kill[$i]','','','','','','','','','')"; + + $event_string = "SUBRT|killing_congest|KC|$KCqueryCID|$congest_kill[$i]|$stmtA|"; + event_logger; + + if ($DB) {print "KILLING $congest_kill[$i]\n";} + $dbhA->query($stmtA) or die "Couldn't execute query:\n"; + } + $i++; + } + + +sleep(15); + +######## 44444 starting another loop + &get_time_now; +@congest_kill = @MT; + + + + + +$stmtA = "SELECT channel FROM live_sip_channels where server_ip = '$server_ip' and extension = 'CONGEST' and channel LIKE \"Local%\" limit 99"; +# $event_string="SQL_QUERY|$stmtA|"; +# &event_logger; + +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + if($DB){print STDERR $record->[0],"\n";} + $congest_kill[$rec_count] = "$record->[0]"; + $rec_count++; + } + + } + +$i=0; +foreach(@congest_kill) + { + $i_count = $i; + if ($i_count < 10) {$i_count = "0$i_count";} + + if (length($congest_kill[$i])>0) + { + ### use manager middleware-app to zapbarge call being placed from meetme + $KCqueryCID = "KC$i_count$CIDdate"; + + ### insert a NEW record to the vicidial_manager table to be processed + $stmtA = "INSERT INTO vicidial_manager values('','','$now_date','NEW','N','$server_ip','','Hangup','$KCqueryCID','Channel: $congest_kill[$i]','','','','','','','','','')"; + + $event_string = "SUBRT|killing_congest|KC|$KCqueryCID|$congest_kill[$i]|$stmtA|"; + event_logger; + + if ($DB) {print "KILLING $congest_kill[$i]\n";} + $dbhA->query($stmtA) or die "Couldn't execute query:\n"; + } + $i++; + } + + + +# $event_string='CLOSING DB CONNECTION|'; +# &event_logger; + + $dbhA->close; + + + if($DB){print "DONE... Exiting... Goodbye... See you later... \n";} + + +exit; + + + + + + + +sub get_time_now #get the current date and time and epoch for logging call lengths and datetimes +{ +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$Fhour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday $hour:$min:$sec"; +$action_log_date = "$year-$mon-$mday"; +$CIDdate = "$year$mon$mday$hour$min$sec"; +} + + + + + +sub event_logger { + ### open the log file for writing ### + open(Lout, ">>$KHLOGfile") + || die "Can't open $KHLOGfile: $!\n"; + + print Lout "$now_date|$event_string|\n"; + + close(Lout); + +$event_string=''; +} diff --git a/AST_manager_listen.pl b/AST_manager_listen.pl new file mode 100644 index 0000000..2a8945c --- /dev/null +++ b/AST_manager_listen.pl @@ -0,0 +1,526 @@ +#!/usr/bin/perl +# +# AST_manager_listen.pl version 1.1.9 build 51222-1553 +# +# Part of the Asterisk Central Queue System (ACQS) +# +# DESCRIPTION: +# connects to the Asterisk Manager interface and updates records in the +# vicidial_manager table of the asterisk database in MySQL based upon the +# events that it receives +# +# SUMMARY: +# This program was designed as the listen-only part of the ACQS. It's job is to +# look for certain events and based upon either the uniqueid or the callerid of +# the call update the status and information of an action record in the +# vicidial_manager table of the asterisk MySQL database. +# +# Win32 - ActiveState Perl 5.8.0 +# UNIX - Gnome or KDE with Tk/Tcl and perl Tk/Tcl modules loaded +# Both - Net::MySQL, Net::Telnet and Time::HiRes perl modules loaded +# +# For the client program to work in ACQS mode, this program must be running +# +# For this program to work you need to have the "asterisk" MySQL database +# created and create the tables listed in the CONF_MySQL.txt file, also make sure +# that the machine running this program has read/write/update/delete access +# to that database +# +# In your Asterisk server setup you also need to have several things activated +# and defined. See the CONF_Asterisk.txt file for details +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# changes: +# 50322-1300 changed callerid parsing to remove quotes and number +# 50616-1559 Added NewCallerID parsing and updating +# 50621-1406 Added Asterisk server shutdown and connection dead detection +# 50810-1534 Added database server variable definitions lookup +# 50824-1606 Altered CVS/1.2 support for different output +# 50901-2359 Another CVS/1.2 output parsing fix +# 51222-1553 fixed parentheses bug in manager output +# + +# constants +$DB=1; # Debug flag, set to 0 for no debug messages, lots of output +$US='__'; +$MT[0]=''; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n[-debugX] = Extra-verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + + + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + + &get_time_now; + + $event_string='PROGRAM STARTED||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||'; + &event_logger; + +#use lib './lib', '../lib'; +use Time::HiRes ('gettimeofday','usleep','sleep'); # necessary to have perl sleep command of less than one second +use Net::MySQL; +use Net::Telnet (); + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + $event_string='LOGGED INTO MYSQL SERVER ON 1 CONNECTION|'; + &event_logger; + + +### Grab Server values from the database +$stmtA = "SELECT telnet_host,telnet_port,ASTmgrUSERNAME,ASTmgrSECRET,ASTmgrUSERNAMEupdate,ASTmgrUSERNAMElisten,ASTmgrUSERNAMEsend,max_vicidial_trunks,answer_transfer_agent,local_gmt,ext_context FROM servers where server_ip = '$server_ip';"; +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBtelnet_host = "$record->[0]"; + $DBtelnet_port = "$record->[1]"; + $DBASTmgrUSERNAME = "$record->[2]"; + $DBASTmgrSECRET = "$record->[3]"; + $DBASTmgrUSERNAMEupdate = "$record->[4]"; + $DBASTmgrUSERNAMElisten = "$record->[5]"; + $DBASTmgrUSERNAMEsend = "$record->[6]"; + $DBmax_vicidial_trunks = "$record->[7]"; + $DBanswer_transfer_agent= "$record->[8]"; + $DBSERVER_GMT = "$record->[9]"; + $DBext_context = "$record->[10]"; + if ($DBtelnet_host) {$telnet_host = $DBtelnet_host;} + if ($DBtelnet_port) {$telnet_port = $DBtelnet_port;} + if ($DBASTmgrUSERNAME) {$ASTmgrUSERNAME = $DBASTmgrUSERNAME;} + if ($DBASTmgrSECRET) {$ASTmgrSECRET = $DBASTmgrSECRET;} + if ($DBASTmgrUSERNAMEupdate) {$ASTmgrUSERNAMEupdate = $DBASTmgrUSERNAMEupdate;} + if ($DBASTmgrUSERNAMElisten) {$ASTmgrUSERNAMElisten = $DBASTmgrUSERNAMElisten;} + if ($DBASTmgrUSERNAMEsend) {$ASTmgrUSERNAMEsend = $DBASTmgrUSERNAMEsend;} + if ($DBmax_vicidial_trunks) {$max_vicidial_trunks = $DBmax_vicidial_trunks;} + if ($DBanswer_transfer_agent) {$answer_transfer_agent = $DBanswer_transfer_agent;} + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + if ($DBext_context) {$ext_context = $DBext_context;} + } + } + + +if (!$telnet_port) {$telnet_port = '5038';} + +$one_day_interval = 90; # 1 day loops for 3 months +while($one_day_interval > 0) +{ + + $event_string="STARTING NEW MANAGER TELNET CONNECTION||ATTEMPT|ONE DAY INTERVAL:$one_day_interval|"; + &event_logger; +# Errmode => Return, + + ### connect to asterisk manager through telnet + $tn = new Net::Telnet (Port => $telnet_port, + Prompt => '/.*[\$%#>] $/', + Output_record_separator => '',); + #$fh = $tn->dump_log("$LItelnetlog"); # uncomment for telnet log + if (length($ASTmgrUSERNAMElisten) > 3) {$telnet_login = $ASTmgrUSERNAMElisten;} + else {$telnet_login = $ASTmgrUSERNAME;} + $tn->open("$telnet_host"); + $tn->waitfor('/0\n$/'); # print login + $tn->print("Action: Login\nUsername: $telnet_login\nSecret: $ASTmgrSECRET\n\n"); + $tn->waitfor('/Authentication accepted/'); # waitfor auth accepted + + $tn->buffer_empty; + + $event_string="STARTING NEW MANAGER TELNET CONNECTION|$telnet_login|CONFIRMED CONNECTION|ONE DAY INTERVAL:$one_day_interval|"; + &event_logger; + + $endless_loop=864000; # 1 day at .10 seconds per loop + + while($endless_loop > 0) + { + ### sleep for 10 hundredths of a second + usleep(1*100*1000); + + $msg=''; + $read_input_buf = $tn->get(Errmode => Return, Timeout => 1,); + $input_buf_length = length($read_input_buf); + $msg = $tn->errmsg; + if ($msg =~ /filehandle isn\'t open/i) + { + $endless_loop=0; + $one_day_interval=0; + print "ERRMSG: |$msg|\n"; + print "\nAsterisk server shutting down, PROCESS KILLED... EXITING\n\n"; + $event_string="Asterisk server shutting down, PROCESS KILLED... EXITING|ONE DAY INTERVAL:$one_day_interval|$msg|"; + &event_logger; + } + + if ( ($read_input_buf !~ /\n\n/) or ($input_buf_length < 10) ) + { + # if ($read_input_buf =~ /\n/) {print "\n|||$input_buf_length|||$read_input_buf|||\n";} + $input_buf = "$input_buf$read_input_buf"; + } + else + { + $partial=0; + $partial_input_buf=''; + + if ($read_input_buf !~ /\n\n$/) + { + $read_input_buf =~ s/\(|\)/ /gi; # replace parens with space + $partial_input_buf = $read_input_buf; + $partial_input_buf =~ s/\n/-----/gi; + $partial_input_buf =~ s/\*/\\\*/gi; + $partial_input_buf =~ s/.*----------//gi; + $partial_input_buf =~ s/-----/\n/gi; + $read_input_buf =~ s/$partial_input_buf$//gi; + $partial++; + } + + +# |1 DIALINGs updated| +# Nested quantifiers in regex; marked by <-- HERE in m/Event: Newexten +# Channel: Zap/16-1 +# Context: demo +# Extension: 990009*CL_UMGCOF_L** <-- HERE 1479503*conf*** +# Priority: 2 +# Application: Dial +# AppData: Zap/g3/90009*CL_UMGCOF_L**1479503*conf***|20 +# Uniqueid: 1101224775.6783 +# $/ at /home/cron/AST_manager_listen.pl line 114. + + + $input_buf = "$input_buf$read_input_buf"; + @input_lines = split(/\n\n/, $input_buf); + + if($DB){print "input buffer: $input_buf_length lines: $#input_lines partial: $partial\n";} + if ( ($DB) && ($partial) ) {print "-----[$partial_input_buf]-----\n\n";} + if($DB){print "|$input_buf|\n";} + + $manager_string = "$input_buf"; + &manager_output_logger; + + $input_buf = "$partial_input_buf"; + + + @command_line=@MT; + $ILcount=0; + foreach(@input_lines) + { + if ( ($input_lines[$ILcount] =~ /State: Ringing|State: Up|State: Dialing|Event: Newstate|Event: Hangup|Event: Newcallerid|Event: Shutdown/) && ($input_lines[$ILcount] !~ /ZOMBIE/) ) + { + $input_lines[$ILcount] =~ s/^\n|^\n\n//gi; + @command_line=split(/\n/, $input_lines[$ILcount]); + if ($input_lines[$ILcount] =~ /Event: Shutdown/) + { + $endless_loop=0; + $one_day_interval=0; + print "\nAsterisk server shutting down, PROCESS KILLED... EXITING\n\n"; + $event_string="Asterisk server shutting down, PROCESS KILLED... EXITING|ONE DAY INTERVAL:$one_day_interval|"; + &event_logger; + } + + if ($input_lines[$ILcount] =~ /Event: Hangup/) + { + if ( ($command_line[2] =~ /^Channel: /i) && ($command_line[3] =~ /^Uniqueid: /i) ) ### post 2005-08-07 CVS + { + $channel = $command_line[2]; + $channel =~ s/Channel: |\s*$//gi; + $uniqueid = $command_line[3]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='DEAD', channel='$channel' where server_ip = '$server_ip' and uniqueid = '$uniqueid'"; + + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows HANGUPS updated|\n";} + } + else + { + $channel = $command_line[1]; + $channel =~ s/Channel: |\s*$//gi; + $uniqueid = $command_line[2]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='DEAD', channel='$channel' where server_ip = '$server_ip' and uniqueid = '$uniqueid'"; + + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows HANGUPS updated|\n";} + } + } + + if ($input_lines[$ILcount] =~ /State: Dialing/) + { + if ( ($command_line[1] =~ /^Channel: /i) && ($command_line[4] =~ /^Uniqueid: /i) ) ### pre 2004-10-07 CVS + { + $channel = $command_line[1]; + $channel =~ s/Channel: |\s*$//gi; + $callid = $command_line[3]; + $callid =~ s/Callerid: |\s*$//gi; + $callid =~ s/^\"//gi; $callid =~ s/\".*$//gi; + $uniqueid = $command_line[4]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='SENT', channel='$channel', uniqueid = '$uniqueid' where server_ip = '$server_ip' and callerid = '$callid'"; + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows DIALINGs updated|\n";} + } + if ( ($command_line[1] =~ /^Channel: /i) && ($command_line[4] =~ /^CalleridName: /i) ) ### post 2004-10-07 CVS + { + $channel = $command_line[1]; + $channel =~ s/Channel: |\s*$//gi; + $callid = $command_line[4]; + $callid =~ s/CalleridName: |\s*$//gi; + $callid =~ s/^\"//gi; $callid =~ s/\".*$//gi; + $uniqueid = $command_line[5]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='SENT', channel='$channel', uniqueid = '$uniqueid' where server_ip = '$server_ip' and callerid = '$callid'"; + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows DIALINGs updated|\n";} + } + if ( ($command_line[2] =~ /^Channel: /i) && ($command_line[5] =~ /^CalleridName: /i) ) ### post 2005-08-07 CVS + { + $channel = $command_line[2]; + $channel =~ s/Channel: |\s*$//gi; + $callid = $command_line[5]; + $callid =~ s/CalleridName: |\s*$//gi; + $callid =~ s/^\"//gi; $callid =~ s/\".*$//gi; + $uniqueid = $command_line[6]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='SENT', channel='$channel', uniqueid = '$uniqueid' where server_ip = '$server_ip' and callerid = '$callid'"; + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows DIALINGs updated|\n";} + } + } + if ($input_lines[$ILcount] =~ /State: Ringing|State: Up/) + { + if ( ($command_line[1] =~ /^Channel: /i) && ($command_line[4] =~ /^Uniqueid: /i) ) ### pre 2004-10-07 CVS + { + $channel = $command_line[1]; + $channel =~ s/Channel: |\s*$//gi; + $callid = $command_line[3]; + $callid =~ s/Callerid: |\s*$//gi; + $callid =~ s/^\"//gi; $callid =~ s/\".*$//gi; + $uniqueid = $command_line[4]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='UPDATED', channel='$channel', uniqueid = '$uniqueid' where server_ip = '$server_ip' and callerid = '$callid'"; + if ($channel !~ /local/i) + { + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows RINGINGs updated|\n";} + } + } + if ( ($command_line[1] =~ /^Channel: /i) && ($command_line[4] =~ /^CalleridName: /i) ) ### post 2004-10-07 CVS + { + $channel = $command_line[1]; + $channel =~ s/Channel: |\s*$//gi; + $callid = $command_line[4]; + $callid =~ s/CalleridName: |\s*$//gi; + $callid =~ s/^\"//gi; $callid =~ s/\".*$//gi; + $uniqueid = $command_line[5]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='UPDATED', channel='$channel', uniqueid = '$uniqueid' where server_ip = '$server_ip' and callerid = '$callid'"; + if ($channel !~ /local/i) + { + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows RINGINGs updated|\n";} + } + } + if ( ($command_line[2] =~ /^Channel: /i) && ($command_line[5] =~ /^CalleridName: /i) ) ### post 2005-08-07 CVS + { + $channel = $command_line[2]; + $channel =~ s/Channel: |\s*$//gi; + $callid = $command_line[5]; + $callid =~ s/CalleridName: |\s*$//gi; + $callid =~ s/^\"//gi; $callid =~ s/\".*$//gi; + $uniqueid = $command_line[6]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='UPDATED', channel='$channel', uniqueid = '$uniqueid' where server_ip = '$server_ip' and callerid = '$callid'"; + if ($channel !~ /local/i) + { + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows RINGINGs updated|\n";} + } + } + } + + if ($input_lines[$ILcount] =~ /Event: Newcallerid/) + { + if ( ($command_line[1] =~ /^Channel: /i) && ($command_line[3] =~ /^Uniqueid: /i) ) + { + $channel = $command_line[1]; + $channel =~ s/Channel: |\s*$//gi; + $callid = $command_line[2]; + $callid =~ s/Callerid: |\s*$//gi; + $callid =~ s/^\"//gi; $callid =~ s/\".*$//gi; + $uniqueid = $command_line[3]; + $uniqueid =~ s/Uniqueid: |\s*$//gi; + $stmtA = "UPDATE vicidial_manager set status='UPDATED', channel='$channel', uniqueid = '$uniqueid' where server_ip = '$server_ip' and callerid = '$callid'"; + if ($channel =~ /local/i) + { + print STDERR "|$stmtA|\n"; + $dbhA->query("$stmtA"); + my $affected_rows = $dbhA->get_affected_rows_length; + if($DB){print "|$affected_rows RINGINGs updated|\n";} + } + } + } + + } + $ILcount++; + } + + } + + + $endless_loop--; + $keepalive_count_loop++; + if($DB){print STDERR "loop counter: |$endless_loop|$keepalive_count_loop|\r";} + + ### putting a blank file called "sendmgr.kill" in a directory will automatically safely kill this program + if ( (-e '/home/cron/listenmgr.kill') or ($sendonlyone) ) + { + unlink('/home/cron/listenmgr.kill'); + $endless_loop=0; + $one_day_interval=0; + print "\nPROCESS KILLED MANUALLY... EXITING\n\n"; + } + + ### run a keepalive command to flush whatever is in the buffer through and to keep the connection alive + if ($endless_loop =~ /00$|50$/) + { + &get_time_now; + + @list_lines = $tn->cmd(String => "Action: Command\nCommand: show uptime\n\n", Prompt => '/--END COMMAND--.*/', Errmode => Return, Timeout => 1); + if($DB){print "input lines: $#list_lines\n";} + + if($DB){print "+++++++++++++++++++++++++++++++sending keepalive transmit line $endless_loop|$now_date|\n";} + $keepalive_count_loop=0; + } + + + } + + + if($DB){print "DONE... Exiting... Goodbye... See you later... Not really, initiating next loop...$one_day_interval left\n";} + + $event_string='HANGING UP|'; + &event_logger; + + @hangup = $tn->cmd(String => "Action: Logoff\n\n", Prompt => "/.*/", Errmode => Return, Timeout => 1); + + $ok = $tn->close; + + $one_day_interval--; + +} + + $event_string='CLOSING DB CONNECTION|'; + &event_logger; + + + $dbhA->close; + + + if($DB){print "DONE... Exiting... Goodbye... See you later... Really I mean it this time\n";} + + +exit; + + + + + + + +sub get_time_now #get the current date and time and epoch for logging call lengths and datetimes +{ +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$Fhour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday $hour:$min:$sec"; +$action_log_date = "$year-$mon-$mday"; +} + + + + + +sub event_logger { + ### open the log file for writing ### + open(Lout, ">>$LILOGfile") + || die "Can't open $LILOGfile: $!\n"; + + print Lout "$now_date|$event_string|\n"; + + close(Lout); + +$event_string=''; +} + + + + +sub manager_output_logger +{ +open(MOout, ">>/home/cron/listen.$action_log_date") + || die "Can't open /home/cron/listen.$action_log_date: $!\n"; + + print MOout "$now_date|$manager_string|\n"; + + close(MOout); +} diff --git a/AST_manager_send.pl b/AST_manager_send.pl new file mode 100644 index 0000000..222b6a9 --- /dev/null +++ b/AST_manager_send.pl @@ -0,0 +1,384 @@ +#!/usr/bin/perl +# +# AST_manager_send.pl version 0.2 +# +# Part of the Asterisk Central Queue System (ACQS) +# +# DESCRIPTION: +# spawns child processes (AST_send_action_child.pl) to execute action commands +# on the Asterisk manager interface from records in the vicidial_manager table +# of the asterisk database in MySQL that are marked as a status of NEW +# +# SUMMARY: +# This program was designed as the send-only part of the ACQS. It's job is to +# pick NEW actions from the vicidial_manager table and send them to be executed +# by separate child process. This allows for a higher degree of flexibility and +# scalability over just using a single process. Also, this means that a single +# action execution lock cannot bring the entire system down. +# +# Win32 - ActiveState Perl 5.8.0 +# UNIX - Gnome or KDE with Tk/Tcl and perl Tk/Tcl modules loaded +# Both - Net::MySQL, Net::Telnet and Time::HiRes perl modules loaded +# +# For the client program to work in ACQS mode, this program must be running +# +# For this program to work you need to have the "asterisk" MySQL database +# created and create the tables listed in the CONF_MySQL.txt file, also make sure +# that the machine running this program has read/write/update/delete access +# to that database +# +# In your Asterisk server setup you also need to have several things activated +# and defined. See the CONF_Asterisk.txt file for details +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# 50823-1514 - Added commandline debug options with debug printouts +# 50902-1051 - Added extra debug output launch sub(commented out) +# + +# constants +$COUNTER_OUTPUT=1; # set to 1 to display the counter as the script runs +$US='__'; +$MT[0]=''; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n[-debugX] = Extra-verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-sendonlyone/i) + { + $sendonlyone=1; + print "\n-----SEND ONLY ONE COMMAND -----\n\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + + &get_time_now; + + $event_string='PROGRAM STARTED||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||'; + &event_logger; + +#use lib './lib', '../lib'; +use Time::HiRes ('gettimeofday','usleep','sleep'); # necessary to have perl sleep command of less than one second +use Net::MySQL; +#use Net::Telnet (); + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + + $event_string='LOGGED INTO MYSQL SERVER ON 1 CONNECTION|'; + &event_logger; + +$one_day_interval = 90; # 1 day loops for 3 months +while($one_day_interval > 0) +{ + + + $endless_loop=864000; # 10 days at .20 seconds per loop + + while($endless_loop > 0) + { + + $affected_rows=0; + $NEW_actions=0; + + $stmtA = "SELECT count(*) from vicidial_manager where server_ip = '$server_ip' and status = 'NEW'"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + if($DB){print STDERR $record->[0]," NEW Actions to send on server $server_ip $endless_loop\n";} + $NEW_actions = "$record->[0]"; + } + } + + if (!$NEW_actions) + { + $affected_rows=0; + } + else + { + $stmtA = "UPDATE vicidial_manager set status='QUEUE' where server_ip = '$server_ip' and status = 'NEW' order by entry_date limit 1"; + $dbhA->query("$stmtA"); + $affected_rows = $dbhA->get_affected_rows_length; + if ($DB) {print STDERR "rows updated to QUEUE: |$affected_rows|\n";} + } + + + if ($affected_rows) + { + $stmtA = "SELECT * FROM vicidial_manager where server_ip = '$server_ip' and status = 'QUEUE' order by entry_date desc limit 1"; + $event_string="SQL_QUERY|$stmtA|"; + &event_logger; + + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + if($DB){print STDERR $record->[0],"|", $record->[1],"|", $record->[6],"|", $record->[7],"|", $record->[8],"\n";} + $man_id = "$record->[0]"; + $uniqueid = "$record->[1]"; + $channel = "$record->[6]"; + $action = "$record->[7]"; + $callid = "$record->[8]"; + $cmd_line_b = "$record->[9]"; + $cmd_line_c = "$record->[10]"; + $cmd_line_d = "$record->[11]"; + $cmd_line_e = "$record->[12]"; + $cmd_line_f = "$record->[13]"; + $cmd_line_g = "$record->[14]"; + $cmd_line_h = "$record->[15]"; + $cmd_line_i = "$record->[16]"; + $cmd_line_j = "$record->[17]"; + $cmd_line_k = "$record->[18]"; + + $originate_command = ''; + $originate_command .= "Action: $action\n"; + if (length($cmd_line_b)>3) {$originate_command .= "$cmd_line_b\n";} + if (length($cmd_line_c)>3) {$originate_command .= "$cmd_line_c\n";} + if (length($cmd_line_d)>3) {$originate_command .= "$cmd_line_d\n";} + if (length($cmd_line_e)>3) {$originate_command .= "$cmd_line_e\n";} + if (length($cmd_line_f)>3) {$originate_command .= "$cmd_line_f\n";} + if (length($cmd_line_g)>3) {$originate_command .= "$cmd_line_g\n";} + if (length($cmd_line_h)>3) {$originate_command .= "$cmd_line_h\n";} + if (length($cmd_line_i)>3) {$originate_command .= "$cmd_line_i\n";} + if (length($cmd_line_j)>3) {$originate_command .= "$cmd_line_j\n";} + if (length($cmd_line_k)>3) {$originate_command .= "$cmd_line_k\n";} + $originate_command .= "\n"; + + $SENDNOW=1; + if ($originate_command =~ /Action: Hangup|Action: Redirect/) + { + $SENDNOW=0; + if($DB){print STDERR "\n|checking for dead call before executing|$callid|$uniqueid|\n";} + $dbhA->query("SELECT count(*) FROM vicidial_manager where server_ip = '$server_ip' and callerid='$callid' and status = 'DEAD'"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_countH=0; + while ( $record = $iter->each) + { + $rec_countH = "$record->[0]"; + } + if (!$rec_countH){$SENDNOW=1;} + else + { + $launch_string = "|not sending command line is dead|$callid|$uniqueid|"; + # &launch_logger; + if($DB){print STDERR "\n$launch_string\n";} + + } + } + + } + + $event_string="----BEGIN NEW COMMAND----\n$originate_command----END NEW COMMAND----\n"; + &event_logger; + + if ($SENDNOW) + { + # $tn->buffer_empty; + + $launch_string = "/home/cron/AST_send_action_child.pl --data1=$man_id $callid $uniqueid $channel"; + # &launch_logger; + + system("/home/cron/AST_send_action_child.pl --data1=$man_id >> /home/cron/action.$action_log_date \&"); + + $launch_string = "SENT $man_id $callid $uniqueid $channel"; + # &launch_logger; + + $stmtA = "UPDATE vicidial_manager set status='SENT' where man_id='$man_id'"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); + + $event_string="SQL_QUERY|$stmtA|"; + &event_logger; + } + else + { + $stmtA = "UPDATE vicidial_manager set status='DEAD' where man_id='$man_id'"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); + $event_string="COMMAND NOT SENT, SQL_QUERY|$stmtA|"; + &event_logger; + } + + } + } + + } + + if ($affected_rows) + { + ### sleep for 10 hundredths of a second + usleep(1*100*1000); + } + else + { + ### sleep for 20 hundredths of a second + usleep(1*200*1000); + } + + $endless_loop--; + if( ($COUNTER_OUTPUT) or ($DB) ){print STDERR "loop counter: |$endless_loop|\r";} + + ### putting a blank file called "sendmgr.kill" in a directory will automatically safely kill this program + if ( (-e '/home/cron/sendmgr.kill') or ($sendonlyone) ) + { + unlink('/home/cron/sendmgr.kill'); + $endless_loop=0; + $one_day_interval=0; + print "\nPROCESS KILLED MANUALLY... EXITING\n\n"; + } + + if ($endless_loop =~ /0$/) + { + &get_time_now; + + if( ($COUNTER_OUTPUT) or ($DB) ){print "checking to see if listener is dead |$sendonlyone|$running_listen|\n";} + #@psoutput = `/bin/ps -f --no-headers -A`; + @psoutput = `/bin/ps -o "%p %a" --no-headers -A`; + + $running_listen = 0; + + $i=0; + foreach (@psoutput) + { + chomp($psoutput[$i]); + + if ($DBX) {print "$i|$psoutput[$i]| \n";} + @psline = split(/\/usr\/bin\/perl /,$psoutput[$i]); + + if ($psline[1] =~ /AST_manager_li/) + { + $running_listen++; + if ($DB) {print "SEND RUNNING: |$psline[1]|\n";} + } + + $i++; + } + + if (!$running_listen) + { + $sendonlyone++; + if( ($COUNTER_OUTPUT) or ($DB) ){print "LISTENER DEAD STOPPING PROGRAM... ATTEMPTING TO START keepalive SCRIPT\n";} + $event_string='LISTENER DEAD STOPPING PROGRAM... ATTEMPTING TO START keepalive SCRIPT|'; + &event_logger; + `/usr/bin/at now < /home/cron/ADMIN_keepalive_send_listen.at 2>/dev/null 1>&2`; + } + } + + + } + + + if( ($COUNTER_OUTPUT) or ($DB) ){print "DONE... Exiting... Goodbye... See you later... Not really, initiating next loop...$one_day_interval left\n";} + + $one_day_interval--; + +} + + $event_string='CLOSING DB CONNECTION|'; + &event_logger; + + + $dbhA->close; + + + if( ($COUNTER_OUTPUT) or ($DB) ){print "DONE... Exiting... Goodbye... See you later... Really I mean it this time\n";} + + +exit; + + + + + + + +sub get_time_now #get the current date and time and epoch for logging call lengths and datetimes +{ +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$Fhour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday $hour:$min:$sec"; +$action_log_date = "$year-$mon-$mday"; +} + + + + + +sub event_logger { + ### open the log file for writing ### + open(Lout, ">>$MSLOGfile") + || die "Can't open $MSLOGfile: $!\n"; + + print Lout "$now_date|$event_string|\n"; + + close(Lout); + +$event_string=''; +} + +sub launch_logger { + ### open the log file for writing ### + open(LLout, ">>/home/cron/action_launch.$action_log_date") + || die "Can't open /home/cron/action_launch.$action_log_date: $!\n"; + + print LLout "$now_date|$launch_string|\n"; + + close(LLout); + +$event_string=''; +} diff --git a/AST_reset_mysql_vars.pl b/AST_reset_mysql_vars.pl new file mode 100644 index 0000000..0409266 --- /dev/null +++ b/AST_reset_mysql_vars.pl @@ -0,0 +1,72 @@ +#!/usr/bin/perl +# +# AST_reset_mysql_vars.pl version 0.2 +# +# !!! DO NOT RUN THIS WHILE THERE ARE ACTIVE CALLS ON THE ASTERISK SERVER !!! +# +# DESCRIPTION: +# clears out mysql records for this server +# +# It is recommended that you run this program on the local Asterisk machine +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# + + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +use Net::MySQL; + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + + + $stmtA = "UPDATE conferences set extension='' where server_ip='$server_ip';"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); # or die "Couldn't execute query: |$stmtA|\n"; + print " - conferences reset\n"; + + $stmtA = "UPDATE vicidial_conferences set extension='' where server_ip='$server_ip';"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); # or die "Couldn't execute query: |$stmtA|\n"; + print " - vicidial_conferences reset\n"; + +# $stmtA = "UPDATE vicidial_manager set status='DEAD' where server_ip='$server_ip' and status='NEW';"; +# if($DB){print STDERR "\n|$stmtA|\n";} +# $dbhA->query($stmtA); # or die "Couldn't execute query: |$stmtA|\n"; +# print " - vicidial_manager queue reset\n"; + + $stmtA = "DELETE from vicidial_manager where server_ip='$server_ip';"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); # or die "Couldn't execute query: |$stmtA|\n"; + print " - vicidial_manager delete\n"; + + $stmtA = "DELETE from vicidial_auto_calls where server_ip='$server_ip';"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); # or die "Couldn't execute query: |$stmtA|\n"; + print " - vicidial_manager delete\n"; + + $stmtA = "DELETE from vicidial_live_agents where server_ip='$server_ip';"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); # or die "Couldn't execute query: |$stmtA|\n"; + print " - vicidial_manager delete\n"; + + $stmtA = "DELETE from vicidial_users where full_name='5555';"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA); # or die "Couldn't execute query: |$stmtA|\n"; + print " - vicidial_manager delete\n"; + + $dbhA->close; + + +exit; + + + + + + diff --git a/AST_send_action_child.pl b/AST_send_action_child.pl new file mode 100644 index 0000000..b15eb8b --- /dev/null +++ b/AST_send_action_child.pl @@ -0,0 +1,258 @@ +#!/usr/bin/perl +# +# AST_send_action_child.pl version 0.2 +# +# Part of the Asterisk Central Queue System (ACQS) +# +# DESCRIPTION: +# This script is spawned every time an action is to be executed by the main +# AST_manager_send.pl script and sends actions blindly to the Asterisk manager +# +# SUMMARY: +# This program was designed as the blind-send part of the ACQS. It's job is to +# be spawned by the AST_manager_send.pl script lookup the record in the MySQL DB +# to be executed. connect to the manager interface, send the action and logoff +# then exit. +# +# Win32 - ActiveState Perl 5.8.0 +# UNIX - Gnome or KDE with Tk/Tcl and perl Tk/Tcl modules loaded +# Both - Net::MySQL, Net::Telnet and Time::HiRes perl modules loaded +# +# For the client program to work in ACQS mode, this program must be running +# +# For this program to work you need to have the "asterisk" MySQL database +# created and create the tables listed in the CONF_MySQL.txt file, also make sure +# that the machine running this program has read/write/update/delete access +# to that database +# +# In your Asterisk server setup you also need to have several things activated +# and defined. See the CONF_Asterisk.txt file for details +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# 50810-1547 - Added database server variable definitions lookup +# 50823-1527 - Altered commandline debug options with debug printouts +# 50902-1032 - Changed default logging to fulllog +# + +$FULL_LOG = 1; # set to 1 for a full response log to be created in /home/cron/action_full.date + +$secX = time(); + +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$Fhour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday $hour:$min:$sec"; +$action_log_date = "$year-$mon-$mday"; + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $ARGS = "$ARGS $ARGV[$i]\n"; + $i++; + } + +# print "DB0: |$ARGS|$#ARGV|$i|\n"; + + if ($ARGS =~ /--help/i) + { + print "allowed run time options:\n [-debug] = debug output\n [-debugX] = Extra-debug output\n [-fulllog] = full response logging\n [--data1=XXXXXXX] = vicidial_manager record\n\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\n\n"; + } + if ($args =~ /-fulllog/i) + { + $FULL_LOG=1; # Full response logging enabled + } + if ($ARGS =~ /--data1=/i) + { + $data1 = $ARGS; +# print "DB1: |$ARGS|\n"; + $data1 =~ s/^.*--data1=//gi; +# print "DB2: |$data1|\n"; + $data1 =~ s/ .*$//gi; +# print "DB3: |$data1|\n"; + } + + } +} +else +{ +#print "no command line options set\n\n"; +} + +if (length($data1)>1) + { +# print "Data1 value = |$data1|\n"; + +use Net::MySQL; +use Net::Telnet (); + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + ### Grab Server values from the database + $stmtA = "SELECT telnet_host,telnet_port,ASTmgrUSERNAME,ASTmgrSECRET,ASTmgrUSERNAMEsend FROM servers where server_ip = '$server_ip';"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBtelnet_host = "$record->[0]"; + $DBtelnet_port = "$record->[1]"; + $DBASTmgrUSERNAME = "$record->[2]"; + $DBASTmgrSECRET = "$record->[3]"; + $DBASTmgrUSERNAMEsend = "$record->[4]"; + if ($DBtelnet_host) {$telnet_host = $DBtelnet_host;} + if ($DBtelnet_port) {$telnet_port = $DBtelnet_port;} + if ($DBASTmgrUSERNAME) {$ASTmgrUSERNAME = $DBASTmgrUSERNAME;} + if ($DBASTmgrSECRET) {$ASTmgrSECRET = $DBASTmgrSECRET;} + if ($DBASTmgrUSERNAMEsend) {$ASTmgrUSERNAMEsend = $DBASTmgrUSERNAMEsend;} + } + } + + + $stmtA = "SELECT * FROM vicidial_manager where man_id = '$data1'"; + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + $man_id = "$record->[0]"; + $uniqueid = "$record->[1]"; + $channel = "$record->[6]"; + $action = "$record->[7]"; + $callid = "$record->[8]"; + $cmd_line_b = "$record->[9]"; + $cmd_line_c = "$record->[10]"; + $cmd_line_d = "$record->[11]"; + $cmd_line_e = "$record->[12]"; + $cmd_line_f = "$record->[13]"; + $cmd_line_g = "$record->[14]"; + $cmd_line_h = "$record->[15]"; + $cmd_line_i = "$record->[16]"; + $cmd_line_j = "$record->[17]"; + $cmd_line_k = "$record->[18]"; + + $originate_command = ''; + $originate_command .= "Action: $action\n"; + if (length($cmd_line_b)>3) {$originate_command .= "$cmd_line_b\n";} + if (length($cmd_line_c)>3) {$originate_command .= "$cmd_line_c\n";} + if (length($cmd_line_d)>3) {$originate_command .= "$cmd_line_d\n";} + if (length($cmd_line_e)>3) {$originate_command .= "$cmd_line_e\n";} + if (length($cmd_line_f)>3) {$originate_command .= "$cmd_line_f\n";} + if (length($cmd_line_g)>3) {$originate_command .= "$cmd_line_g\n";} + if (length($cmd_line_h)>3) {$originate_command .= "$cmd_line_h\n";} + if (length($cmd_line_i)>3) {$originate_command .= "$cmd_line_i\n";} + if (length($cmd_line_j)>3) {$originate_command .= "$cmd_line_j\n";} + if (length($cmd_line_k)>3) {$originate_command .= "$cmd_line_k\n";} + $originate_command .= "\n"; + } + } + + $dbhA->close; + + + print "$now_date|$data1|\n$originate_command"; + $event_string = "0|$data1|"; + $event_string .= "\n$originate_command"; + if ($FULL_LOG) {&full_event_logger;} + +if (!$telnet_port) {$telnet_port = '5038';} + + ### connect to asterisk manager through telnet + $tn = new Net::Telnet (Port => $telnet_port, + Prompt => '/.*[\$%#>] $/', + Output_record_separator => '', + Errmode => Return,); + #$fh = $tn->dump_log("$MStelnetlog"); # uncomment for telnet log + if (length($ASTmgrUSERNAMEsend) > 3) {$telnet_login = $ASTmgrUSERNAMEsend;} + else {$telnet_login = $ASTmgrUSERNAME;} + $tn->open("$telnet_host"); + $tn->waitfor('/0\n$/'); # print login + $tn->print("Action: Login\nUsername: $telnet_login\nSecret: $ASTmgrSECRET\n\n"); + $tn->waitfor('/Authentication accepted/'); # waitfor auth accepted + + $tn->buffer_empty; + + @list_channels = $tn->cmd(String => "$originate_command", Prompt => '/.*/'); + +sleep(3); + + if ($FULL_LOG) + { + $event_string = "1|$data1|"; $k=0; + foreach(@list_channels) {$event_string .= "$list_channels[$k]"; $k++;} + $read_input_buf = $tn->get(Errmode => Return, Timeout => 1,); + $event_string .= "$read_input_buf"; + &full_event_logger; + } + + $tn->buffer_empty; + + @hangup = $tn->cmd(String => "Action: Logoff\n\n", Prompt => "/.*/"); + +sleep(2); + + if ($FULL_LOG) + { + $event_string = "2|$data1|"; $k=0; + foreach(@list_channels) {$event_string .= "$list_channels[$k]"; $k++;} + $read_input_buf = $tn->get(Errmode => Return, Timeout => 1,); + $event_string .= "$read_input_buf"; + &full_event_logger; + } + + $tn->buffer_empty; + + $ok = $tn->close; + + + + + } + +$secZ = time(); +$script_time = ($secZ - $secX); +print "DONE execute time: $script_time seconds\n"; + +exit; + +sub full_event_logger { + ### open the log file for writing ### + open(Lout, ">>/home/cron/action_full.$action_log_date") + || die "Can't open /home/cron/action_full.$action_log_date: $!\n"; + + print Lout "$now_date|$event_string|\n"; + + close(Lout); + +$event_string=''; +} diff --git a/AST_update.pl b/AST_update.pl new file mode 100644 index 0000000..2ec731e --- /dev/null +++ b/AST_update.pl @@ -0,0 +1,1099 @@ +#!/usr/bin/perl +# +# AST_update.pl version 1.1.9 (build 60117-1202) +# +# DESCRIPTION: +# uses the Asterisk Manager interface and Net::MySQL to update the live_channels +# tables and verify the parked_channels table in the asterisk MySQL database +# This "near-live-status of Zap/SIP/Local/IAX channels" list is used by clients +# +# SUMMARY: +# This program was designed for people using the Asterisk PBX with Digium +# Zaptel telco cards and SIP VOIP hardphones or softphones as extensions, it +# could be adapted to other functions, but I designed it specifically for +# Zap/IAX2/SIP users. The program will run on UNIX or Win32 command line +# providing the following criteria are met: +# +# Win32 - ActiveState Perl 5.8.0 +# UNIX - Gnome or KDE with Tk/Tcl and perl Tk/Tcl modules loaded +# Both - Net::MySQL, Net::Telnet and Time::HiRes perl modules loaded +# +# For the client program to work, this program must always be running +# +# For this program to work you need to have the "asterisk" MySQL database +# created and create the tables listed in the MySQL_AST_CREATE_tables.sql file, +# also make sure that the machine running this program has select/insert/update/delete +# access to that database +# +# In your Asterisk server setup you also need to have several things activated +# and defined. See the CONF_Asterisk.txt file for details +# +# It is recommended that you run this program on the local Asterisk machine +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# version changes: +# 41228-1659 - modified to compensate for manager output response hiccups +# 50107-1611 - modified to add Zap and IAX2 clients (differentiate from trunks) +# 50117-1537 - modified to add MySQL port ($DB_port) var from conf file +# 50303-0958 - modified to compensate for Zap manager output hiccups +# 50406-1320 - added channel_data to live_channels table for more flexibility +# also added Zap/IAX client check every 100 loop instead of only once +# 50509-1047 - added collection of server_performance data with SYSPERF flag +# 50621-1307 - modified to allow for SIP trunks +# 50810-1601 - Added database server variable definitions lookup +# 50823-1625 - Altered Debug vars and initial CVS/1.2 support for changed output +# 50824-1606 - Altered CVS/1.2 support for "show channels concise" output +# 50829-1134 - Added processor percentage usage to stat collection +# 51229-1355 - Added ability to take ! as delimited for 1.2 show channels concise +# 60117-1202 - Changed IAX2 client phone channel to reflect change to '-' iteration +# + +$build = '60117-1202'; + +# constants +$SYSPERF=0; # system performance logging to MySQL server_performance table every 5 seconds +$SYSPERF_rec=0; # is dial-time recording turned on +$DB=0; # Debug flag, set to 1 for debug messages WARNING LOTS OF OUTPUT!!! +$DBX=0; # Debug flag, set to 1 for debug messages WARNING LOTS OF OUTPUT!!! +$US='__'; +$AMP='@'; +$MT[0]=''; +$cpuUSERprev=0; +$cpuSYSTprev=0; +$cpuIDLEprev=0; + +# DB table variables for testing + $parked_channels = 'parked_channels'; + $live_channels = 'live_channels'; + $live_sip_channels = 'live_sip_channels'; + $server_updater = 'server_updater'; +# $parked_channels = 'TEST_parked_channels'; +# $live_channels = 'TEST_live_channels'; +# $live_sip_channels = 'TEST_live_sip_channels'; +# $server_updater = 'TEST_server_updater'; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-sysperf] = system performance logging\n [-sysperfdebug] = system performance debug output\n [-debug] = verbose debug messages\n [-debugX] = Extra-verbose debug messages\n\n"; + exit; + } + else + { + if ($args =~ /-sysperf/i) + { + $SYSPERF=1; # System performance logging flag + } + if ($args =~ /-sysperfdebug/i) + { + $SYSPERFDB=1; # prints system performance data out to STDOUT + } + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /--debugX/i) + { + $DBX=1; + print "\n----- SUPER-DUPER DEBUGGING -----\nBUILD: $build\n"; + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + + + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + + &get_time_now; + + $event_string='PROGRAM STARTED||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||'; + &event_logger; + +use lib './lib', '../lib'; +use Time::HiRes ('gettimeofday','usleep','sleep'); # necessary to have perl sleep command of less than one second +use Net::MySQL; +use Net::Telnet (); + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + $event_string='LOGGED INTO MYSQL SERVER ON 1 CONNECTION|'; + &event_logger; + +### Grab Server values from the database +$stmtA = "SELECT telnet_host,telnet_port,ASTmgrUSERNAME,ASTmgrSECRET,ASTmgrUSERNAMEupdate,ASTmgrUSERNAMElisten,ASTmgrUSERNAMEsend,max_vicidial_trunks,answer_transfer_agent,local_gmt,ext_context,asterisk_version FROM servers where server_ip = '$server_ip';"; +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBtelnet_host = "$record->[0]"; + $DBtelnet_port = "$record->[1]"; + $DBASTmgrUSERNAME = "$record->[2]"; + $DBASTmgrSECRET = "$record->[3]"; + $DBASTmgrUSERNAMEupdate = "$record->[4]"; + $DBASTmgrUSERNAMElisten = "$record->[5]"; + $DBASTmgrUSERNAMEsend = "$record->[6]"; + $DBmax_vicidial_trunks = "$record->[7]"; + $DBanswer_transfer_agent= "$record->[8]"; + $DBSERVER_GMT = "$record->[9]"; + $DBext_context = "$record->[10]"; + $DBasterisk_version = "$record->[11]"; + if ($DBtelnet_host) {$telnet_host = $DBtelnet_host;} + if ($DBtelnet_port) {$telnet_port = $DBtelnet_port;} + if ($DBASTmgrUSERNAME) {$ASTmgrUSERNAME = $DBASTmgrUSERNAME;} + if ($DBASTmgrSECRET) {$ASTmgrSECRET = $DBASTmgrSECRET;} + if ($DBASTmgrUSERNAMEupdate) {$ASTmgrUSERNAMEupdate = $DBASTmgrUSERNAMEupdate;} + if ($DBASTmgrUSERNAMElisten) {$ASTmgrUSERNAMElisten = $DBASTmgrUSERNAMElisten;} + if ($DBASTmgrUSERNAMEsend) {$ASTmgrUSERNAMEsend = $DBASTmgrUSERNAMEsend;} + if ($DBmax_vicidial_trunks) {$max_vicidial_trunks = $DBmax_vicidial_trunks;} + if ($DBanswer_transfer_agent) {$answer_transfer_agent = $DBanswer_transfer_agent;} + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + if ($DBext_context) {$ext_context = $DBext_context;} + if ($DBasterisk_version) {$AST_ver = $DBasterisk_version;} + } + } + + if ($AST_ver =~ /^1\.2|^CVS|^SVN/i) {$show_channels_12_format = 1;} + else {$show_channels_12_format = 0;} + +##### LOOK FOR ZAP CLIENTS AS DEFINED IN THE phones TABLE SO THEY ARE NOT MISLABELED AS TRUNKS + print STDERR "LOOKING FOR Zap clients assigned to this server:\n"; + $Zap_client_count=0; + $Zap_client_list='|'; + $stmtA = "SELECT extension FROM phones where protocol = 'Zap' and server_ip='$server_ip'"; + if($DB){print STDERR "|$stmtA|\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + print STDERR $record->[0],"\n"; + $Zap_client_list .= "$record->[0]|"; + $Zap_client_count++; + } + } + +##### LOOK FOR IAX2 CLIENTS AS DEFINED IN THE phones TABLE SO THEY ARE NOT MISLABELED AS TRUNKS + print STDERR "LOOKING FOR IAX2 clients assigned to this server:\n"; + $IAX2_client_count=0; + $IAX2_client_list='|'; + $stmtA = "SELECT extension FROM phones where protocol = 'IAX2' and server_ip='$server_ip'"; + if($DB){print STDERR "|$stmtA|\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + print STDERR $record->[0],"\n"; + $IAX2_client_list .= "$record->[0]|"; + if ($record->[0] !~ /\@/) + {$IAX2_client_list .= "$record->[0]$AMP$record->[0]|";} + else + { + $IAX_user = $record->[0]; + $IAX_user =~ s/\@.*$//gi; + $IAX2_client_list .= "$IAX_user|"; + } + $IAX2_client_count++; + } + } + +##### LOOK FOR SIP CLIENTS AS DEFINED IN THE phones TABLE SO THEY ARE NOT MISLABELED AS TRUNKS + print STDERR "LOOKING FOR SIP clients assigned to this server:\n"; + $SIP_client_count=0; + $SIP_client_list='|'; + $stmtA = "SELECT extension FROM phones where protocol = 'SIP' and server_ip='$server_ip'"; + if($DB){print STDERR "|$stmtA|\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + print STDERR $record->[0],"\n"; + $SIP_client_list .= "$record->[0]|"; + if ($record->[0] !~ /\@/) + {$SIP_client_list .= "$record->[0]$AMP$record->[0]|";} + else + { + $SIP_user = $record->[0]; + $SIP_user =~ s/\@.*$//gi; + $SIP_client_list .= "$SIP_user|"; + } + $SIP_client_count++; + } + } + + print STDERR "Zap Clients: $Zap_client_list\n"; + print STDERR "IAX2 Clients: $IAX2_client_list\n"; + print STDERR "SIP Clients: $SIP_client_list\n"; + +$one_day_interval = 12; # 2 hour loops for one day +while($one_day_interval > 0) +{ + + $event_string="STARTING NEW MANAGER TELNET CONNECTION||ATTEMPT|ONE DAY INTERVAL:$one_day_interval|"; + &event_logger; + +if (!$telnet_port) {$telnet_port = '5038';} + + ### connect to asterisk manager through telnet + $t = new Net::Telnet (Port => $telnet_port, + Prompt => '/.*[\$%#>] $/', + Output_record_separator => '',); + #$fh = $t->dump_log("$telnetlog"); # uncomment for telnet log + if (length($ASTmgrUSERNAMEupdate) > 3) {$telnet_login = $ASTmgrUSERNAMEupdate;} + else {$telnet_login = $ASTmgrUSERNAME;} + $t->open("$telnet_host"); + $t->waitfor('/0\n$/'); # print login + $t->print("Action: Login\nUsername: $telnet_login\nSecret: $ASTmgrSECRET\n\n"); + $t->waitfor('/Authentication accepted/'); # waitfor auth accepted + + $event_string="STARTING NEW MANAGER TELNET CONNECTION|$telnet_login|CONFIRMED CONNECTION|ONE DAY INTERVAL:$one_day_interval|"; + &event_logger; + + $endless_loop=5769999; # 30 days minutes at .45 seconds per loop + + while($endless_loop > 0) + { + + + @DBchannels=@MT; + @DBsips=@MT; + @list_channels=@MT; + @test_channels=@MT; + + &get_current_channels; + + &validate_parked_channels; + + + $t->buffer_empty; + if (!$show_channels_12_format) + { + @list_channels = $t->cmd(String => "Action: Command\nCommand: show channels\n\n", Prompt => '/--END COMMAND-.*/'); + } + else + { + @list_channels = $t->cmd(String => "Action: Command\nCommand: show channels concise\n\n", Prompt => '/--END COMMAND-.*/'); + } + + ##### TEST CHANNELS ZAP/IAX2/Local TO SEE IF THERE WAS A LARGE HICCUP IN OUTPUT FROM PREVIOUS OUTPUT + @test_channels=@list_channels; + $test_zap_count=0; + $test_iax_count=0; + $test_local_count=0; + $test_sip_count=0; + $s=0; + foreach(@test_channels) + { + chomp($test_channels[$s]); + if($DBX){print "$s|$test_channels[$s]\n";} + $test_channels[$s] =~ s/Congestion\s+\(Empty\)/ SIP\/CONGEST/gi; + $test_channels[$s] =~ s/\(Outgoing Line\)|\(None\)/SIP\/ring/gi; + $test_channels[$s] =~ s/\(Empty\)/SIP\/internal/gi; + if (!$show_channels_12_format) + { + $test_channels[$s] =~ s/^\s*|\s*$//gi; + $test_channels[$s] =~ s/\(.*\)//gi; + } + else + { + @test_chan_12 = split(/:/, $test_channels[$s]); + if (length($test_chan_12[6])<2) {$test_chan_12[6] = 'SIP/ring';} + $test_channels[$s] = "$test_chan_12[0] $test_chan_12[6]"; + } + if ($test_channels[$s] =~ /^Zap|^IAX2|^SIP|^Local/) + { + if ($test_channels[$s] =~ /^(\S+)\s+.+\s+(\S+)$/) + { + $channel = $1; + $extension = $2; + if ($show_channels_12_format) + {$extension =~ s/^.*\(|\).*$//gi;} + $extension =~ s/^SIP\/|-\S+$//gi; + $extension =~ s/\|.*//gi; + if ($channel =~ /^SIP/) {$test_sip_count++;} + if ($channel =~ /^Local/) {$test_local_count++;} + if ($IAX2_client_count) + { + $channel_match=$channel; + $channel_match =~ s/\/\d+$|-\d+$//gi; + $channel_match =~ s/^IAX2\///gi; + if ($IAX2_client_list =~ /\|$channel_match\|/i) {$test_iax_count++;} + } + if ($Zap_client_count) + { + $channel_match=$channel; + $channel_match =~ s/^Zap\///gi; + if ($Zap_client_list =~ /\|$channel_match\|/i) {$test_zap_count++;} + } + } + } + $s++; + } + + + + # $DB_live_lines = ($#DBchannels + $#DBsips); + $DB_live_lines = ($channel_counter + $sip_counter); + if ( (!$DB_live_lines) or ($#list_channels < 2) ) + {$PERCENT_static = 0;} + else + { + $PERCENT_static = ( ($#list_channels / $DB_live_lines) * 100); + $PERCENT_static = sprintf("%6.2f", $PERCENT_static); + } + + if ( (!$test_zap_count) or ($zap_client_counter < 2) ) + {$PERCENT_ZC_static = 0;} + else + { + $PERCENT_ZC_static = ( ($test_zap_count / $zap_client_counter) * 100); + $PERCENT_ZC_static = sprintf("%6.2f", $PERCENT_ZC_static); + } + + if ( (!$test_iax_count) or ($iax_client_counter < 2) ) + {$PERCENT_IC_static = 0;} + else + { + $PERCENT_IC_static = ( ($test_iax_count / $iax_client_counter) * 100); + $PERCENT_IC_static = sprintf("%6.2f", $PERCENT_IC_static); + } + + if ( (!$test_local_count) or ($local_client_counter < 2) ) + {$PERCENT_LC_static = 0;} + else + { + $PERCENT_LC_static = ( ($test_local_count / $local_client_counter) * 100); + $PERCENT_LC_static = sprintf("%6.2f", $PERCENT_LC_static); + } + + if ( (!$test_sip_count) or ($sip_client_counter < 2) ) + {$PERCENT_SC_static = 0;} + else + { + $PERCENT_SC_static = ( ($test_sip_count / $sip_client_counter) * 100); + $PERCENT_SC_static = sprintf("%6.2f", $PERCENT_SC_static); + } + + if ($endless_loop =~ /0$/) + {print "-$now_date $PERCENT_static $#list_channels $#DBchannels:$channel_counter $#DBsips:$sip_counter $PERCENT_ZC_static|$test_zap_count:$zap_client_counter $PERCENT_IC_static|$test_iax_count:$iax_client_counter $PERCENT_LC_static|$test_local_count:$local_client_counter $PERCENT_SC_static|$test_sip_count:$sip_client_counter\n";} + + if ( ( ($PERCENT_static < 10) && ( ($channel_counter > 3) or ($sip_counter > 4) ) ) or + ( ($PERCENT_static < 20) && ( ($channel_counter > 10) or ($sip_counter > 10) ) ) or + ( ($PERCENT_static < 30) && ( ($channel_counter > 20) or ($sip_counter > 20) ) ) or + ( ($PERCENT_static < 40) && ( ($channel_counter > 30) or ($sip_counter > 30) ) ) or + ( ($PERCENT_static < 50) && ( ($channel_counter > 40) or ($sip_counter > 40) ) ) or + ( ($PERCENT_ZC_static < 20) && ( $zap_client_counter > 3 ) ) or + ( ($PERCENT_ZC_static < 40) && ( $zap_client_counter > 9 ) ) or + ( ($PERCENT_IC_static < 20) && ( $iax_client_counter > 3 ) ) or + ( ($PERCENT_IC_static < 40) && ( $iax_client_counter > 9 ) ) or + ( ($PERCENT_SC_static < 20) && ( $sip_client_counter > 3 ) ) or + ( ($PERCENT_SC_static < 40) && ( $sip_client_counter > 9 ) ) ) + { + $UD_bad_grab++; + $event_string="------ UPDATER BAD GRAB!!! UBGcount: $UD_bad_grab\n $PERCENT_static $#list_channels $#DBchannels:$channel_counter $#DBsips:$sip_counter $PERCENT_ZC_static|$test_zap_count:$zap_client_counter $PERCENT_IC_static|$test_iax_count:$iax_client_counter $PERCENT_LC_static|$test_local_count:$local_client_counter $PERCENT_SC_static|$test_sip_count:$sip_client_counter\n"; + print "$event_string\n"; + &event_logger; + if ($UD_bad_grab > 20) {$UD_bad_grab=0;} + } + else + { + $UD_bad_grab=0; + if ( ($endless_loop =~ /0$/) && ($SYSPERF) ) + { + $cpuUSERcent=0; $cpuSYSTcent=0; $cpuIDLEcent=0; + ### get processor usage seconds ### + # cpu 924841 211725 270473 6961811 + @cpuUSE = `/usr/bin/cat /proc/stat`; + if ($cpuUSE[0] =~ /cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/) + { + $cpuUSER = ($1 + $2); + $cpuSYST = $3; + $cpuIDLE = $4; + $cpuUSERdiff = ($cpuUSER - $cpuUSERprev); + $cpuSYSTdiff = ($cpuSYST - $cpuSYSTprev); + $cpuIDLEdiff = ($cpuIDLE - $cpuIDLEprev); + $cpuIDLEdiffTOTAL = (($cpuUSERdiff + $cpuSYSTdiff) + $cpuIDLEdiff); + if ($cpuIDLEdiffTOTAL > 0) + { + $cpuUSERcent = sprintf("%.0f", (($cpuUSERdiff / $cpuIDLEdiffTOTAL) * 100)); + $cpuSYSTcent = sprintf("%.0f", (($cpuSYSTdiff / $cpuIDLEdiffTOTAL) * 100)); + $cpuIDLEcent = sprintf("%.0f", (($cpuIDLEdiff / $cpuIDLEdiffTOTAL) * 100)); + } + $cpuUSERprev=$cpuUSER; + $cpuSYSTprev=$cpuSYST; + $cpuIDLEprev=$cpuIDLE; + } + + ### get system load ### + $serverLOAD = `/usr/bin/cat /proc/loadavg`; + $serverLOAD =~ s/ .*//gi; + $serverLOAD =~ s/\D//gi; + + ### get memory usage ### + @GRABserverMEMORY = `/usr/bin/free -m -t`; + if ($GRABserverMEMORY[1] =~ /Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+/) + { + $MEMused = $2; + $MEMfree = $3; + } + ### get number of system processes ### + @GRABserverPROCESSES = `/bin/ps -A --no-heading`; + $serverPROCESSES = $#GRABserverPROCESSES; + + if ($SYSPERF_rec) {$recording_count = ($test_local_count / 2)} + else {$recording_count=0;} + + if ($SYSPERFDB) + {print "$serverLOAD $MEMfree $MEMused $serverPROCESSES $#list_channels $cpuUSERcent $cpuSYSTcent $cpuIDLEcent\n";} + + $stmtA = "INSERT INTO server_performance (start_time,server_ip,sysload,freeram,usedram,processes,channels_total,trunks_total,clients_total,clients_zap,clients_iax,clients_local,clients_sip,live_recordings,cpu_user_percent,cpu_system_percent,cpu_idle_percent) values('$now_date','$server_ip','$serverLOAD','$MEMfree','$MEMused','$serverPROCESSES','$#list_channels','$channel_counter','$sip_counter','$test_zap_count','$test_iax_count','$test_local_count','$test_sip_count','$recording_count','$cpuUSERcent','$cpuSYSTcent','$cpuIDLEcent')"; + if( ($DB) or ($UD_bad_grab) ){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA) or die "Couldn't execute query: |$stmtA|\n"; + } + } + + if ($endless_loop =~ /00$/) + { + ##### LOOK FOR ZAP CLIENTS AS DEFINED IN THE phones TABLE SO THEY ARE NOT MISLABELED AS TRUNKS + print STDERR "LOOKING FOR Zap clients assigned to this server:\n"; + $Zap_client_count=0; + $Zap_client_list='|'; + $stmtA = "SELECT extension FROM phones where protocol = 'Zap' and server_ip='$server_ip'"; + if($DB){print STDERR "|$stmtA|\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + print STDERR $record->[0],"\n"; + $Zap_client_list .= "$record->[0]|"; + $Zap_client_count++; + } + } + + ##### LOOK FOR IAX2 CLIENTS AS DEFINED IN THE phones TABLE SO THEY ARE NOT MISLABELED AS TRUNKS + print STDERR "LOOKING FOR IAX2 clients assigned to this server:\n"; + $IAX2_client_count=0; + $IAX2_client_list='|'; + $stmtA = "SELECT extension FROM phones where protocol = 'IAX2' and server_ip='$server_ip'"; + if($DB){print STDERR "|$stmtA|\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + print STDERR $record->[0],"\n"; + $IAX2_client_list .= "$record->[0]|"; + if ($record->[0] !~ /\@/) + {$IAX2_client_list .= "$record->[0]$AMP$record->[0]|";} + else + { + $IAX_user = $record->[0]; + $IAX_user =~ s/\@.*$//gi; + $IAX2_client_list .= "$IAX_user|"; + } + $IAX2_client_count++; + } + } + + ##### LOOK FOR SIP CLIENTS AS DEFINED IN THE phones TABLE SO THEY ARE NOT MISLABELED AS TRUNKS + print STDERR "LOOKING FOR SIP clients assigned to this server:\n"; + $SIP_client_count=0; + $SIP_client_list='|'; + $stmtA = "SELECT extension FROM phones where protocol = 'SIP' and server_ip='$server_ip'"; + if($DB){print STDERR "|$stmtA|\n";} + $dbhA->query("$stmtA"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + print STDERR $record->[0],"\n"; + $SIP_client_list .= "$record->[0]|"; + if ($record->[0] !~ /\@/) + {$SIP_client_list .= "$record->[0]$AMP$record->[0]|";} + else + { + $SIP_user = $record->[0]; + $SIP_user =~ s/\@.*$//gi; + $SIP_client_list .= "$SIP_user|"; + } + $SIP_client_count++; + } + } + + + print STDERR "Zap Clients: $Zap_client_list\n"; + print STDERR "IAX2 Clients: $IAX2_client_list\n"; + print STDERR "SIP Clients: $SIP_client_list\n"; + } + + @list_chan_12=@MT; + $EXcount = 0; + $EXcount = @{[$list_channels[2] =~ /\!/g]}; + if ($EXcount > 10) + { + @list_chan_12 = split(/\!/, $list_channels[2]); + } + else + { + @list_chan_12 = split(/:/, $list_channels[2]); + } + if( ($DB) && ($show_channels_12_format) ) {print "concise: $#list_chan_12\n";} + if ( ( ( ($list_channels[1] =~ /State Appl\./) or ($list_channels[2] =~ /State Appl\.|Application\(Data\)/) or ($list_channels[3] =~ /State Appl\.|Application\(Data\)/) ) || ($#list_chan_12 > 8) ) && (!$UD_bad_grab) ) + { + + $c=0; + if($DB){print "lines: $#list_channels\n";} + if($DB){print "DBchn: $#DBchannels\n";} + if($DB){print "DBsip: $#DBsips\n";} + foreach(@list_channels) + { + # $DBchannels =~ s/^\|//g; + + chomp($list_channels[$c]); + if( ($DB) or ($UD_bad_grab) ){print "-|$c|$list_channels[$c]|\n";} + $list_channels[$c] =~ s/Congestion\s+\(Empty\)/ SIP\/CONGEST/gi; + $list_channels[$c] =~ s/\(Outgoing Line\)|\(None\)/SIP\/ring/gi; + $list_channels[$c] =~ s/\(Empty\)/SIP\/internal/gi; + if (!$show_channels_12_format) + { + $list_channels[$c] =~ s/^\s*|\s*$//gi; + $list_channels[$c] =~ s/\(.*\)//gi; + } + else + { + @list_chan_12=@MT; + if ($EXcount > 10) + { + @list_chan_12 = split(/\!/, $list_channels[$c]); + } + else + { + @list_chan_12 = split(/:/, $list_channels[$c]); + } + if ($DBX) {print "EXcount: $EXcount\n";} + if (length($list_chan_12[6])<2) {$list_chan_12[6] = 'SIP/ring';} + $list_channels[$c] = "$list_chan_12[0] $list_chan_12[6]"; + } + $list_SIP[$c] = $list_channels[$c]; + if( ($DB) or ($UD_bad_grab) ){print "+|$c|$list_channels[$c]|\n\n";} + + ########## PARSE EACH LINE TO DETERMINE WHETHER IT IS TRUNK OR CLIENT AND PUT IN APPROPRIATE TABLE + if ($list_channels[$c] =~ /^Zap|^IAX2|^SIP|^Local/) + { + if ($list_channels[$c] =~ /^(\S+)\s+.+\s+(\S+)$/) + { + $line_type = ''; + $channel = $1; + $extension = $2; + $channel_data = $extension; + if ($show_channels_12_format) + { + $extension =~ s/^.*\(|\).*$//gi; + #### new data in 1.2 for future use + # SIP/cc160-7b91:default:917274514920:3:Up:Dial:Zap/g2/17274514920|30|o:cc160::3:9:Zap/25-1 + # Zap/30-1:demo:010*010*010*012*8600091:1:Up:Dial:IAX2/TESTast2:test@10.10.10.12:4569/8600051|25|o:::3:123:IAX2/TESTast2-29 + # Zap/9-1:demo:990009*CL_UMGCOF_L**6317906**9545541833*13678*:2:Up:Dial:Zap/r3/90009*CL_UMGCOF_L**6317906**9545541833*13678*|120|o:::3:334:Zap/82-1 + # $channel = $list_chan_12[0]; + # $channel_data = $list_chan_12[0]; + # $context = $list_chan_12[1]; + # $dialed = $list_chan_12[2]; + # $priority = $list_chan_12[3]; + # $state = $list_chan_12[4]; + # $extension = $list_chan_12[6]; + # $callerid = $list_chan_12[7]; + # $duration = $list_chan_12[10]; + # $bridged = $list_chan_12[11]; + } + $extension =~ s/^SIP\/|-\S+$//gi; + $extension =~ s/\|.*//gi; + $QRYchannel = "$channel$US$extension"; + + if( ($DB) or ($UD_bad_grab) ){print "channel: |$channel|\n";} + if( ($DB) or ($UD_bad_grab) ){print "extension: |$extension|\n";} + if( ($DB) or ($UD_bad_grab) ){print "QRYchannel:|$QRYchannel|\n";} + + if ($channel =~ /^SIP|^Zap|^IAX2/) {$line_type = 'TRUNK';} + if ($channel =~ /^Local/) {$line_type = 'CLIENT';} + if ($IAX2_client_count) + { + $channel_match=$channel; + $channel_match =~ s/\/\d+$|-\d+$//gi; + $channel_match =~ s/^IAX2\///gi; + # print "checking for IAX2 client: |$channel_match|\n"; + if ($IAX2_client_list =~ /\|$channel_match\|/i) {$line_type = 'CLIENT';} + } + if ($Zap_client_count) + { + $channel_match=$channel; + $channel_match =~ s/^Zap\///gi; + # print "checking for Zap client: |$channel_match|\n"; + if ($Zap_client_list =~ /\|$channel_match\|/i) {$line_type = 'CLIENT';} + } + if ($SIP_client_count) + { + $channel_match=$channel; + $channel_match =~ s/-\S+$//gi; + $channel_match =~ s/^SIP\///gi; + # print "checking for SIP client: |$channel_match|\n"; + if ($SIP_client_list =~ /\|$channel_match\|/i) {$line_type = 'CLIENT';} + } + + if ($line_type eq 'TRUNK') + { + if( ($DB) or ($UD_bad_grab) ){print "current channels: $#DBchannels\n";} + + $k=0; + $channel_in_DB=0; + foreach(@DBchannels) + { + if ( ($DBchannels[$k] eq "$QRYchannel") && (!$channel_in_DB) ) + { + $DBchannels[$k] = ''; + $channel_in_DB++; + } + if( ($DB) or ($UD_bad_grab) ){print "DB $k|$DBchannels[$k]| |";} + $k++; + } + + if ( (!$channel_in_DB) && (length($QRYchannel)>3) ) + { + $stmtA = "INSERT INTO $live_channels (channel,server_ip,extension,channel_data) values('$channel','$server_ip','$extension','$channel_data')"; + if( ($DB) or ($UD_bad_grab) ){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA) or die "Couldn't execute query: |$stmtA|\n"; + } + } + + if ($line_type eq 'CLIENT') + { + if( ($DB) or ($UD_bad_grab) ){print "current sips: $#DBsips\n";} + + $k=0; + $sipchan_in_DB=0; + foreach(@DBsips) + { + if ( ($DBsips[$k] eq "$QRYchannel") && (!$sipchan_in_DB) ) + { + $DBsips[$k] = ''; + $sipchan_in_DB++; + } + if( ($DB) or ($UD_bad_grab) ){print "DB $k|$DBsips[$k]| |";} + $k++; + } + + if ( (!$sipchan_in_DB) && (length($QRYchannel)>3) ) + { + $stmtA = "INSERT INTO $live_sip_channels (channel,server_ip,extension,channel_data) values('$channel','$server_ip','$extension','$channel_data')"; + if( ($DB) or ($UD_bad_grab) ){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA) or die "Couldn't execute query: |$stmtA|\n"; + } + } + } + } + + $c++; + } + if($DB){print "COUNT: $c|$#list_channels|$endless_loop\n";} + + + if ($#DBchannels >= 0) + { + $d=0; + foreach(@DBchannels) + { + if (length($DBchannels[$d])>4) + { + ($DELchannel, $DELextension) = split(/\_\_/, $DBchannels[$d]); + $stmtB = "DELETE FROM $live_channels where server_ip='$server_ip' and channel='$DELchannel' and extension='$DELextension' limit 1"; + if( ($DB) or ($UD_bad_grab) ){print STDERR "\n|$stmtB|\n";} + $dbhA->query($stmtB); + # $dbhA->query($stmtB) or die "Couldn't execute query:\n"; + } + $d++; + } + } + + if ($#DBsips >= 0) + { + $d=0; + foreach(@DBsips) + { + if (length($DBsips[$d])>4) + { + ($DELchannel, $DELextension) = split(/\_\_/, $DBsips[$d]); + $stmtB = "DELETE FROM $live_sip_channels where server_ip='$server_ip' and channel='$DELchannel' and extension='$DELextension' limit 1"; + if( ($DB) or ($UD_bad_grab) ){print STDERR "\n|$stmtB|\n";} + $dbhA->query($stmtB); + # $dbhA->query($stmtB) or die "Couldn't execute query:\n"; + } + $d++; + } + } + + ### sleep for 45 hundredths of a second + usleep(1*450*1000); + + $endless_loop--; + if($DB){print STDERR "\nloop counter: |$endless_loop|\n";} + + ### putting a blank file called "update.kill" in a directory will automatically safely kill this program + if (-e '/home/cron/update.kill') + { + unlink('/home/cron/update.kill'); + $endless_loop=0; + $one_day_interval=0; + print "\nPROCESS KILLED MANUALLY... EXITING\n\n" + } + + $bad_grabber_counter=0; + $no_channels_12_counter=0; + } + + else + { + if ( ($list_channels[1] !~ /Privilege: Command/) && ($show_channels_12_format) ) + { + $bad_grabber_counter++; + if($DB){print STDERR "\nbad grab, trying again\n";} + ### sleep for 20 hundredths of a second + usleep(1*200*1000); + + $event_string="BAD GRAB TRYING AGAIN|BAD_GRABS: $bad_grabber_counter|$endless_loop|ONE DAY INTERVAL:$one_day_interval|"; + &event_logger; + + if ($bad_grabber_counter > 100) + { + $endless_loop=0; + $event_string="TOO MANY BAD GRABS, STARTING NEW CONNECTION|BAD_GRABS: $bad_grabber_counter|$endless_loop|ONE DAY INTERVAL:$one_day_interval|"; + &event_logger; + $bad_grabber_counter=0; + } + } + else + { + $no_channels_12_counter++; + $event_string="NO CHANNELS HERE|COUNTER: $no_channels_12_counter|$endless_loop|ONE DAY INTERVAL:$one_day_interval|$list_channels[1]"; + &event_logger; + + if($DBX) {print "*|EMPTY CHANNELS: $no_channels_12_counter|$#list_channels|$list_channels[1]";} + + ### sleep for 40 hundredths of a second + usleep(1*400*1000); + if ($no_channels_12_counter == 3) + { + ### there are no channels, delete all from live_channels, live_sip_channels + $event_string="NO CHANNELS HERE|COUNTER: $no_channels_12_counter|$endless_loop|ONE DAY INTERVAL:$one_day_interval|$list_channels[1]"; + &event_logger; + $stmtB = "DELETE FROM $live_sip_channels where server_ip='$server_ip'"; + if( ($DB) or ($UD_bad_grab) ){print STDERR "\n|$stmtB|\n";} + $dbhA->query($stmtB); + $stmtB = "DELETE FROM $live_channels where server_ip='$server_ip'"; + if( ($DB) or ($UD_bad_grab) ){print STDERR "\n|$stmtB|\n";} + $dbhA->query($stmtB); + } + + } + } + + + } + + + if($DB){print "DONE... Exiting... Goodbye... See you later... Not really, initiating next loop...\n";} + + $event_string='HANGING UP|'; + &event_logger; + + @hangup = $t->cmd(String => "Action: Logoff\n\n", Prompt => "/.*/"); + + $ok = $t->close; + + $one_day_interval--; + +} + + $event_string='CLOSING DB CONNECTION|'; + &event_logger; + + + $dbhA->close; + + + if($DB){print "DONE... Exiting... Goodbye... See you later... Really I mean it this time\n";} + + +exit; + + + + + + + +####################################################################### +# This subroutine simply grabs all active channel/extension combinations from +# the asterisk MySQL database to be compared to the Asterisk Manager results +####################################################################### +sub get_current_channels +{ +$channel_counter=0; +$sip_counter=0; +$zap_client_counter=0; +$iax_client_counter=0; +$local_client_counter=0; +$sip_client_counter=0; + + if($DB){print STDERR "\n|SELECT channel,extension FROM $live_channels where server_ip = '$server_ip'|\n";} + +$dbhA->query("SELECT channel,extension FROM $live_channels where server_ip = '$server_ip'"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + if($DB){print STDERR $record->[0],"|", $record->[1],"\n";} + $DBchannels[$channel_counter] = "$record->[0]$US$record->[1]"; + + $channel_counter++; + $rec_count++; + } + } + +$dbhA->query("SELECT channel,extension FROM $live_sip_channels where server_ip = '$server_ip'"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count_sip=0; + while ( $record = $iter->each) + { + if($DB){print STDERR $record->[0],"|", $record->[1],"\n";} + $DBsips[$sip_counter] = "$record->[0]$US$record->[1]"; + + if ($record->[0] =~ /^Zap/) {$zap_client_counter++;} + if ($record->[0] =~ /^IAX/) {$iax_client_counter++;} + if ($record->[0] =~ /^Local/) {$local_client_counter++;} + if ($record->[0] =~ /^SIP/) {$sip_client_counter++;} + $sip_counter++; + $rec_count_sip++; + } + } + + &get_time_now; + +$stmtU = "UPDATE $server_updater set last_update='$now_date' where server_ip='$server_ip'"; + if($DB){print STDERR "\n|$stmtU|\n";} +$dbhA->query($stmtU); + +} + + + + + +####################################################################### +# The purpose of this subroutine is to make sure that the calls that are +# listed as parked in the parked_channels table are in fact live (to make +# sure the caller has not hung up) and if the channel is not live to delete +# the parked_channels entry for that specific parked channel entry +# +# Yes it does use two DB connections all by itself, I just did that for speed +# and ease of programming to be backward compatible with MySQL < 4.1 or else +# I would have used a delete with subselect and saved all of this bloated code +####################################################################### +sub validate_parked_channels +{ + +if (!$run_validate_parked_channels_now) + { + + $parked_counter=0; + @ARchannel=@MT; @ARextension=@MT; @ARparked_time=@MT; @ARparked_time_UNIX=@MT; + $dbhA->query("SELECT channel,extension,parked_time,UNIX_TIMESTAMP(parked_time) FROM $parked_channels where server_ip = '$server_ip' order by channel desc, parked_time desc"); + if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + $PQchannel = $record->[0]; + $PQextension = $record->[1]; + $PQparked_time = $record->[2]; + $PQparked_time_UNIX = $record->[3]; + if($DB){print STDERR "\n|$PQchannel|$PQextension|$PQparked_time|$PQparked_time_UNIX|\n";} + + my $dbhC = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + + $AR=0; + $record_deleted=0; + foreach(@ARchannel) + { + if (@ARchannel[$AR] eq "$PQchannel") + { + if (@ARparked_time_UNIX[$AR] > $PQparked_time_UNIX) + { + if($DBX){print "Duplicate parked channel delete: |$PQchannel|$PQparked_time|\n";} + $stmtPQ = "DELETE FROM $parked_channels where server_ip='$server_ip' and channel='$PQchannel' and extension='$PQextension' and parked_time='$PQparked_time' limit 1"; + if($DB){print STDERR "\n|$stmtPQ|$$DEL_chan_park_counter|$DEL_chan_park_counter|\n\n";} + $dbhC->query($stmtPQ); + + $DEL_chan_park_counter = "DEL$PQchannel$PQextension"; + $$DEL_chan_park_counter=0; + $record_deleted++; + } + + } + + $AR++; + } + + + + if (!$record_deleted) + { + $ARchannel[$rec_count] = $record->[0]; + $ARextension[$rec_count] = $record->[1]; + $ARparked_time[$rec_count] = $record->[2]; + $ARparked_time_UNIX[$rec_count] = $record->[3]; + + + my $dbhB = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + + + $event_string='LOGGED INTO MYSQL SERVER ON 2 CONNECTIONS TO VALIDATE PARKED CALLS|'; + &event_logger; + + + $dbhB->query("SELECT count(*) FROM $live_channels where server_ip='$server_ip' and channel='$PQchannel' and extension='$PQextension'"); + if ($dbhB->has_selected_record) + { + $iterB=$dbhB->create_record_iterator; + $rec_countB=0; + while ( $recordB = $iterB->each) + { + $PQcount = $recordB->[0]; + if($DB){print STDERR "\n|$PQcount|\n";} + + if ($PQcount < 1) + { + $DEL_chan_park_counter = "DEL$PQchannel$PQextension"; + $$DEL_chan_park_counter++; + if($DBX){print STDERR "Parked counter down|$$DEL_chan_park_counter|$DEL_chan_park_counter|\n";} + + ### if the parked channel doesn't exist 6 times then delete it from table + if ($$DEL_chan_park_counter > 5) + { + if($DBX){print " parked channel delete: |$PQchannel|$PQparked_time|\n";} + $stmtPQ = "DELETE FROM $parked_channels where server_ip='$server_ip' and channel='$PQchannel' and extension='$PQextension' limit 1"; + if($DB){print STDERR "\n|$stmtPQ|$$DEL_chan_park_counter|$DEL_chan_park_counter|\n\n";} + $dbhC->query($stmtPQ); + + $ARchannel[$rec_count] = ''; + $ARextension[$rec_count] = ''; + $ARparked_time[$rec_count] = ''; + $ARparked_time_UNIX[$rec_count] = ''; + $$DEL_chan_park_counter=0; + } + } + else + { + $DEL_chan_park_counter = "DEL$PQchannel$PQextension"; + $$DEL_chan_park_counter=0; + } + } + } + + $event_string='CLOSING MYSQL CONNECTIONS OPENED TO VALIDATE PARKED CALLS|'; + &event_logger; + + + $dbhB->close; + + } + + $dbhC->close; + + } + $parked_counter++; + $rec_count++; + } + + $run_validate_parked_channels_now=5; # set to run every five times the subroutine runs + + } + + $run_validate_parked_channels_now--; + +} + + + + + +################################################################################ +##### get the current date and time and epoch for logging call lengths and datetimes +sub get_time_now +{ +($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); +$year = ($year + 1900); +$mon++; +if ($mon < 10) {$mon = "0$mon";} +if ($mday < 10) {$mday = "0$mday";} +if ($hour < 10) {$Fhour = "0$hour";} +if ($min < 10) {$min = "0$min";} +if ($sec < 10) {$sec = "0$sec";} + +$now_date_epoch = time(); +$now_date = "$year-$mon-$mday $hour:$min:$sec"; +} + + + + + +################################################################################ +##### open the log file for writing ### +sub event_logger +{ +open(Lout, ">>$LOGfile") + || die "Can't open $LOGfile: $!\n"; +print Lout "$now_date|$event_string|\n"; +close(Lout); +$event_string=''; +} diff --git a/AST_vm_update.pl b/AST_vm_update.pl new file mode 100644 index 0000000..6125ad7 --- /dev/null +++ b/AST_vm_update.pl @@ -0,0 +1,202 @@ +#!/usr/bin/perl +# +# AST_vm_update.pl version 0.3 +# +# DESCRIPTION: +# uses the Asterisk Manager interface and Net::MySQL to update the count of +# voicemail messages for each mailbox (in the phone table) in the voicemail +# table list is used by client apps for voicemail notification +# +# SUMMARY: +# This program was designed for people using the Asterisk PBX with voicemail +# +# For the client program to notify of new messaged, this program must be in the +# cron running every minute +# +# For this program to work you need to have the "asterisk" MySQL database +# created and create the tables listed in the CONF_MySQL.txt file, also make sure +# that the machine running this program has read/write/update/delete access +# to that database +# +# It is recommended that you run this program on the local Asterisk machine +# +# If this script is run ever minute there is a theoretical limit of +# 600 mailboxes that it can check due to the wait interval. If you have +# more than this either change the cron when this script is run or change the +# wait interval below +# +# Copyright (C) 2006 Matt Florell LICENSE: GPLv2 +# +# 50823-1422 - Added database server variable definitions lookup +# 50823-1452 - Added commandline arguments for debug at runtime +# + +# constants +$DB=0; # Debug flag, set to 0 for no debug messages per minute +$US='__'; +$MT[0]=''; + +### begin parsing run-time options ### +if (length($ARGV[0])>1) +{ + $i=0; + while ($#ARGV >= $i) + { + $args = "$args $ARGV[$i]"; + $i++; + } + + if ($args =~ /--help/i) + { + print "allowed run time options:\n [-t] = test\n [-debug] = verbose debug messages\n\n"; + } + else + { + if ($args =~ /-debug/i) + { + $DB=1; # Debug flag + } + if ($args =~ /-t/i) + { + $TEST=1; + $T=1; + } + } +} +else +{ +# print "no command line options set\n"; +} +### end parsing run-time options ### + +### Make sure this file is in a libs path or put the absolute path to it +require("/home/cron/AST_SERVER_conf.pl"); # local configuration file + +if (!$DB_port) {$DB_port='3306';} + +use lib './lib', '../lib'; +use Time::HiRes ('gettimeofday','usleep','sleep'); # necessary to have perl sleep command of less than one second +use Net::MySQL; +use Net::Telnet (); + + + my $dbhA = Net::MySQL->new(hostname => "$DB_server", database => "$DB_database", user => "$DB_user", password => "$DB_pass", port => "$DB_port") + or die "Couldn't connect to database: $DB_server - $DB_database\n"; + +### Grab Server values from the database +$stmtA = "SELECT telnet_host,telnet_port,ASTmgrUSERNAME,ASTmgrSECRET,ASTmgrUSERNAMEupdate,ASTmgrUSERNAMElisten,ASTmgrUSERNAMEsend,max_vicidial_trunks,answer_transfer_agent,local_gmt,ext_context FROM servers where server_ip = '$server_ip';"; +$dbhA->query("$stmtA"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + while ( $record = $iter->each) + { + $DBtelnet_host = "$record->[0]"; + $DBtelnet_port = "$record->[1]"; + $DBASTmgrUSERNAME = "$record->[2]"; + $DBASTmgrSECRET = "$record->[3]"; + $DBASTmgrUSERNAMEupdate = "$record->[4]"; + $DBASTmgrUSERNAMElisten = "$record->[5]"; + $DBASTmgrUSERNAMEsend = "$record->[6]"; + $DBmax_vicidial_trunks = "$record->[7]"; + $DBanswer_transfer_agent= "$record->[8]"; + $DBSERVER_GMT = "$record->[9]"; + $DBext_context = "$record->[10]"; + if ($DBtelnet_host) {$telnet_host = $DBtelnet_host;} + if ($DBtelnet_port) {$telnet_port = $DBtelnet_port;} + if ($DBASTmgrUSERNAME) {$ASTmgrUSERNAME = $DBASTmgrUSERNAME;} + if ($DBASTmgrSECRET) {$ASTmgrSECRET = $DBASTmgrSECRET;} + if ($DBASTmgrUSERNAMEupdate) {$ASTmgrUSERNAMEupdate = $DBASTmgrUSERNAMEupdate;} + if ($DBASTmgrUSERNAMElisten) {$ASTmgrUSERNAMElisten = $DBASTmgrUSERNAMElisten;} + if ($DBASTmgrUSERNAMEsend) {$ASTmgrUSERNAMEsend = $DBASTmgrUSERNAMEsend;} + if ($DBmax_vicidial_trunks) {$max_vicidial_trunks = $DBmax_vicidial_trunks;} + if ($DBanswer_transfer_agent) {$answer_transfer_agent = $DBanswer_transfer_agent;} + if ($DBSERVER_GMT) {$SERVER_GMT = $DBSERVER_GMT;} + if ($DBext_context) {$ext_context = $DBext_context;} + } + } + +@PTextensions=@MT; @PTvoicemail_ids=@MT; @PTmessages=@MT; @PTold_messages=@MT; @NEW_messages=@MT; @OLD_messages=@MT; +$dbhA->query("SELECT extension,voicemail_id,messages,old_messages from phones where server_ip='$server_ip'"); +if ($dbhA->has_selected_record) + { + $iter=$dbhA->create_record_iterator; + $rec_count=0; + while ( $record = $iter->each) + { + $PTextensions[$rec_count] = "$record->[0]"; + $PTvoicemail_ids[$rec_count] = "$record->[1]"; + $PTmessages[$rec_count] = "$record->[2]"; + $PTold_messages[$rec_count] = "$record->[3]"; + $rec_count++; + } + } + + +### connect to asterisk manager through telnet +$t = new Net::Telnet (Port => 5038, + Prompt => '/.*[\$%#>] $/', + Output_record_separator => '',); +#$fh = $t->dump_log("$telnetlog"); # uncomment for telnet log + if (length($ASTmgrUSERNAMEsend) > 3) {$telnet_login = $ASTmgrUSERNAMEsend;} + else {$telnet_login = $ASTmgrUSERNAME;} + +$t->open("$telnet_host"); +$t->waitfor('/0\n$/'); # print login +$t->print("Action: Login\nUsername: $telnet_login\nSecret: $ASTmgrSECRET\n\n"); +$t->waitfor('/Authentication accepted/'); # waitfor auth accepted + + +$i=0; +foreach(@PTextensions) + { + @list_channels=@MT; + $t->buffer_empty; + @list_channels = $t->cmd(String => "Action: MailboxCount\nMailbox: $PTvoicemail_ids[$i]\n\nAction: Ping\n\n", Prompt => '/Response: Pong.*/'); + + $j=0; + foreach(@list_channels) + { + if ($list_channels[$j] =~ /Mailbox: $PTvoicemail_ids[$i]/) + { + $NEW_messages[$i] = "$list_channels[$j+1]"; + $NEW_messages[$i] =~ s/NewMessages: |\n//gi; + $OLD_messages[$i] = "$list_channels[$j+2]"; + $OLD_messages[$i] =~ s/OldMessages: |\n//gi; + } + + $j++; + } + + if($DB){print "MailboxCount- $PTvoicemail_ids[$i] NEW:|$NEW_messages[$i]| OLD:|$OLD_messages[$i]| ";} + if ( ($NEW_messages[$i] eq $PTmessages[$i]) && ($OLD_messages[$i] eq $PTold_messages[$i]) ) + { + if($DB){print "MESSAGE COUNT UNCHANGED, DOING NOTHING FOR THIS MAILBOX\n";} + } + else + { + $stmtA = "UPDATE phones set messages='$NEW_messages[$i]', old_messages='$OLD_messages[$i]' where server_ip='$server_ip' and extension='$PTextensions[$i]'"; + if($DB){print STDERR "\n|$stmtA|\n";} + $dbhA->query($stmtA) or die "Couldn't execute query:|$stmtA|\n"; + } + + $i++; + ### sleep for 10 hundredths of a second + usleep(1*100*1000); + } + + +$t->buffer_empty; +@hangup = $t->cmd(String => "Action: Logoff\n\n", Prompt => "/.*/"); +$t->buffer_empty; +$ok = $t->close; + +$dbhA->close; + +if($DB){print "DONE... Exiting... Goodbye... See you later... \n";} + +exit; + + + + diff --git a/DTMF_sounds/0.wav b/DTMF_sounds/0.wav new file mode 100644 index 0000000000000000000000000000000000000000..db262db0bbdf89190fe2266e149e99dd2521a2d5 GIT binary patch literal 8044 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 QR@D$JD()S#^NV%+KQPz4H2?qr literal 0 HcmV?d00001 diff --git a/DTMF_sounds/1.wav b/DTMF_sounds/1.wav new file mode 100644 index 0000000000000000000000000000000000000000..94ec03e5467da9f90b3876782efc54c52382a746 GIT binary patch literal 8044 zcmWlc^>P0Vx4Rz(6e2b-B3p?p)ux`*yo+w{X4Pdak>>V;f36LA%4hX=z-gqH@HV5Sak%u6-N$$#$;psocABTC z1Lj|2nb1SNKlD>|6Cz36uaYIH9#F6YP;*7$4Lu>Y6M_Jzw=njE@{;!#{j&Fzbbi@4 z#Q`Xsek}hjUtT)>A**uPSzR5Aj1_IN&?`p9Ztf zNA+iBiyorybe*Z1*3|7fNBm5yMt22e!A8_()>S$sI!|6){!h#7$eyIP;#yWLcG0SG z8bNNv$&gTUu;r3&KdLNae*O&hA#Y9l?skIh8DtS+QRuYww);NzUW#9IgtWo%y^3G! zHSWRnut!n>AhsU}%!?z?bQ=K2u}`7ry&3L*q31c$G!gB9{Y%~aiiGZs$i+z$Id@S{{aXV& z5E*0^@`?FWBfep+Wkx(P_gC?E{CBIV`?TeI%nh;migeR8_kvT{FD04jw_wv%gKMig zo(5LnFW?6Oet4Rq+bOI0VAivMxcN*OPks=TpOlu;9(OC`b*4oxVG7WEPhI=Nh7)=f z-jgMi9HKpTO;Fi2Mqe7N05HME?pf*~P+n&yix1&$YAw}v={1KF_dWhQ>Rs?lU^e<) z!e$l+I7+dnyuL{hNaG^JvV>oOsrE&#<>2KgL}a{Xovg6C7p70&R^a76474ha%lBH> zfak(h0iMO|7>*g7QY;=oEi@1n5}LTIUeRc20um zDD$Lye#4dW51r#-Mouq#CfeYi8H^xzQHG+S){#&0N8h_gM|V-S3C^*7A;4$+uXGuyJ)t5<)p!lqoynnuUj=G8%KZs^Y;a+&qy zr$Cp_4YZR7yS?T$}kLvn?*w-B-WCwh}M{s_?aIWU6G3h;df3DRUy0rkm6tmAgEBF-h39QKb8Q z_%L~?a9ZN`*z~rR@NOqUF8y z16;cp!L;!l3*faP!En-k3^s3+3JJ-4o zK^H#EE@oj|sD|FEf3-tkT>7RsHL#!mH;|o@%-)Il-aD&CT(jKxi0sR#5O!jII_oXo zU?SoPxYb(OKBa>f*u%YC&?W*So@m--Ce8lv8)O{_=xVUu1DF!Z#cpn&(6#1ZS$0Pt z7SA-ZMx!~w{164bi}nexbgHF|Rfja!uxB&gXC9->@gC?2+Ni)4Fti`8K`1fKOYu0# zmuvvBpW$_5uDsK|0;NFP!$cP^^d+t^rI(Ztru@;}=}5e?1CM4*#{BSLz#iJn6`>;Bo~sDyOAL8hl$GXIUg z1Y8;Zey4?uqDm{dK+2uZAp+>CB%y z&zj0G&qd?%{lptP&;wQ(g3L~Mo8{dqnmvXVQD&&Misn=3Wd|C%}EQ^NI}N3hKv zu)hfY2YwW2k)F}GzqK|nH0gZ)!c-D$oZh6eTkZf@(6`>LJ)e4UkYs*}WM>jBD3D#O z-rBVr_>l6BI2u^zy90bk?MdX4IJTU+nu=9g6}Bt&V#+f-!8h676dsMb48z-Rw&7Yw zJJXr{^R1b5Tw(8#wjr98!9(yFz)HK)#EsxtgsfS@X`s{Xk1I0U%)u+vQbrUtG58>q ziJ>x&k-vHa&6bK?%2F66^+V=adR>rfYH&OOI$+=Y{BA|tcr%?eIOnQlR=m`@SEg&X zI=t}F$W@_HcDP?ea-^O}k)RfIrB-koHanZi+@v9tJAfu1610G{l6)iDu5?vBYYj#& z^NS?olF9+h-h+m6zZfb3{BB;Sk{hJREh2kfKh8=IPL?7)Z16%R<9~&H^j`K(MMk(= zg(nDr-lQsfRd??sB3;nRegn_&E(zR5L~!OfA+5GT4-eh z>mdugTl7LhKj7cI+mdMV>B#<8Wlg7QLF{bY3Bse8-|Gg%r~;mtGQdHoe_Osy4M8DO z@=_iW{`8-6T!>6W_k$bl6Wd-i)2*g>pJYq+TVj&=hrFLQ6!-|Mi`}(>jhBLp85grk zQeQ#w%9WL}mXNQAJcvFWl^Prz`i?QMx~QOFumxDLLjDufDOf4)WV{Z0&68Xa&^pAT zfJ@D2WAx6(r)HnY+sjz$_}aQuKFYy@%|@IKx~-4A&4lBrpmY+hQS)zwziy;$B#FQs zO3nv#`ThW%k2^rM0A6<@tKH50gULL9&fmNc=$kR4cXl8G{SbR=!YC>FYcOcq;{4-@ zLI01|zoiFz?t>gy6turL$pc4>Ni0fT9w#)HR+m-&qsQR-CU>*9AddQ;2j(N6k}_e) z-enCX(sbtw)+b48`X02(bV_~6+690@H+uin9nxjSf{CBRG0ZmPYUhHstI~m{O7h~|l{tgS z!)+TB|7eE=#>0-p>aDJxGJhd`WOgLog52JTsTv`x@lZ(8_>ah7U|VQ5c1^-+`t$I~ zwsYk#TgkDku`4k_#AR^P^(6?H0gF=_?gG^gRE-zzV@T?hd}C^f$V51=pI!C6cCaOi&u1?uzXW)F3Xq)qn)VSaP!FhjBBlB|IkR)P{IO7m z1#OZA{)YVyNH8-LrJ81NbxLBvoa7VXH|;x`F6#dRVzEOZ@t*gt&#?K4r_z}52CJx+ zQF%tgK^G5igBP8zwcdZT;wPa+^elvE!^?G$HF8;5$g1ccFHN zrZn_|J2G!V+8>YyN>wdHnHBmMe;WHbYVdT%3{Zva-kU=fR7uL(IE^?_kxybEY-&!|GDNF@dK*DG&m%$}7!( z=^e4lXc~BjTkM<*eaGD*LNm5I?R9@wd{-ADh}_zQp-4esb!aDANF9Q@V!qJuvhJ%% zO#PXCBjX!B!EVrI+qVJr5Tab10jqISF(6M)dtp6QcLOKS{9TevU+$jL^8gSbOz+7TCMAUCsY(@lZ9~BKpu>G0Bh#@H^LMH} zb9;Qgq8(E5eT{H`&1-^Aov5$rHa6=g}md~ST(3XojLy-9i zS6N?>;{ta>HuTAO2L@mr*=VWh?I|X4vc6{wAc0+F`u%nwXcF|c2iASQv)S9hJf4Tn zEJRmo5*y?l-M&$ny=YhXtLsg~OifAI#|=kzw_?kNwa$r5Vhi{MahJnGyfcCI_-i<^ zue9w}twS9~^%bv`>|x&X&Fb!}9~)Q$jgO8tw{@2~%1DJ-*Rqvyu-;>J!lwDQ59l{> zXW@GPbR8CB6<=7p60^wVNviDzAV_Qt<_n ze6wrx!oUHWI8}}C^8ke z#ZhV<3Zig*;wjv>ep(Z);%{XHxQ^Mue1>EP!Qpa@kH*FiwbwMRt;Xp-;}?pDi6@d5 zdq{>O4hd)pG}&9HPEsCmY3L;qRTdJTuTPgg?AYpkh5CjHg~mCDho4dCsh^YX0Grw~ z%4E$`f>h=cUU8f|QtzDy`iD52Fw3uKKV7ryf90K?K0-1r;X&}O*3|Pyzy&!LJ!DEz z@3X;)jhVb$T)fA8t|6sqo`r*CkjBCG`&a=E=3`=eQagg9URD04uFA2N=1aMmFc7lA z-Ot|ye~B9yd!(T@mdLfyN&&iHm0&3_PvcM>ux$sugEV{A^_(}e5D$fcoDBAT_sd3Y z^>FP|C^4Qvw8YANFTwWsx!fe&I^(12!&ODS8%c*m3xy9bK9|jQI&utI2$^B|+&Zn@ z?>)?Uoo^7`M^4sVZXK<2hsPlTv3h%oKQTTwy?apFQ0qmmpZI?=}vA0tXO9tiiC3W_4n&)-E zJpGZEkkfnH*L zo;p-g&SpgZ*3UO|1dc=hi5@bZ>=c-f;P~QyC4(97ZI`8}rl1J`pHFxPHF~|iG3ZZ; zV7>{>(Dtiv);zL0$oGU>5>(LXo~wamqygUoT%_I50B@s*h9)1)KPQZW!@9aoqMZlY z2$|{G`|^Ukr4gmYuYX2(eIVS60r@Z^Y-M zVTA$gSnnjq@aQYlLFhGWYHLr6(RG#8nD@8%H_R2o!1htP&JY>?TWpJMhv{OJ%O08S z<8J^ow!N&_+HMG8<4@DCBHjkBgtKr2)?D%;&xfW>73WnDgeYx{czgVXzzb7{O9OVo z5`EmRmu(=cH%^{2J9i9ay187op#$t*iI{=76xeS&7bqfsN&6t&j@YOwEdRCfqI)3q zF}F0%AFc4UfEnaC^7zO^g`m2;jRUwXD9Am-rN%ghP;YMV6SO^QHCmP5dIzJ&XTSh41n7o-vhwRi8BtA-&?;5+pFTR~i_Cswbv_ z59&JW3uUSP<%xImW~Wl&uMCUTO4~@#RLBpvM?20i6nvQv&b!JJhB8||*W{{u0Sn^# z5uLugQ5NHWM=pe#(~CI#V{zZl@p0E6-eo zPdDw9SL=p{q%cD4lJ!e(VfYwpQ}%!q8pI^OSvj+`VE_j=oT=kR*OUyBfM_dFoDp0N#jzom=dk)u=n~L4WgcFh{X|s@J#*f{P>?$A)GQ~YYi_%ko zKHm1c*MhXzrS{_5VO^f+9^yJ|ZG`M4f=Kk1WEFLd^HzOx`H3zTGRDsn^e6NSyl|}n ztie7-+_JB2xhO5Ngs2N8d$T8yc-9w+h5BPb4y;dflqJw(4q%ua*(cM+!!uPAsxn&7 z`A(BoP}d>W21bNm;s$ZZ^moBOTh5h5+a>`;{F50z`i#hCYm#?06p5Vfmvv5)750d6 zN3!IAG{36HFK$HbqEOm9>W+{%GYaeLCaF76hL{?f}h$wP2}FS zAUftLXo@S@y#jGPiI>iyz%2J_7FGV#O~;%{w6Z$k?|d6Ve2kua3$8by>LraW_R-9V zxrfrRnC+IJhU7>A6+!yAPj|;OvB>!(ecr0nYapPayY86kcz6+>gC$12 zZ1x;(yk6!|u7%#=arsXO7Xu7W5%2|WB}(q7ZaLi0+Iu1HM$X8b0C|CZoobQ(Rxk^u zj7%|;^{0GS>BqDCWptp%sQXsiWGeSk!mpGm@D+h+Asl`>cL&oFNohM%#%Q?}xt_E_ zWMbToj<;8OWl#~y5U_L}Y%*%RsP|%E{`LgEx45H9!Et_s$f5VW*{08~5BNdp4dT}% zN6*~quXXdx7qC6d`^0fEy`KRsh#$a6fv#0Us>U@g^VBB%%-Y1OhJLV$EbGD-;RgUc zy-V7Es!Ko{Q)qoo@kd7Cih;7V1`9xe`Uza)oaM}h-$|loVCbLi>2;FIrJ4`OuN(_= z5`2$;ewd9NPThdqV?11cwr;57s`#KWo4=R>32;{bTNS9qEw4CFQ8%h(;WLD)oXaueZJXsw3@YQZBg6|Ume zmGv-v7k+V8RZeF7FRo=>_@2|jF6iw@U(-NsnY&MXO7^tOA2^G~S(7UJ&)JIIL|zCZ z`2P!D!@cAtuyH`E{8d?`X;N?&dkbd(V@9&$=WM5^&BX zJ$s#JK${>S_kpfgoiX1+wkUsZ#v5dpy1K!qga`h{q@WIjXs)NxNwg=apE-i?G1)(5 zf3)3=&165~tso-8AHDOzpNJQ*bkBy?Iknp~TFf?aQSL=L(7U@k*MJTgpo1d0#?zjWCVj1>a_*eC~#xE){ za0pjH2jG@lF%69BdHN{fKk@pE5OIM!Vqm)1;F-|r?jxNnCBbussmR-%`4Lm5WlFKi zbH0IS8-gCBJI_Sa@dar`Nxaz0*2!guTXMr{=BT7<;<0eI?+N%i2}y{0>szPQK)Qs; z>!QoKbC}J3jdoyfIQS2AK_t$Y+x5_9A!)KYbM{k`O}{tfHkaBW$PvWP5WBa|e;#`! z3CwfDHg~3!k7?*}{X$=t!e{9~W8FDHJ)#C@jBHbr8y?dyjsl#e*zOb>^U0Bb=ieSJ&e7S6~Y2UwBn?j{hEHG<_16g+&?ORZXcLZTduh zmcEMrCo12)(&3D1P(jdp6ScLWy~@8oaZQ0WeLDQ8Hod)0kB-6+7o-1L|Fa-LHg0Lw z%tTITcynXL55*L&K5`J^T40P_99B{R=_39RaFTpZd0F#_0F&O6 zIF`6IqVwH@)WtOt`GMm09o2)ClcB5A#^-KKC=XrNuQO(adZ7m*orY#rsX2l-WomLq z(E3@gN;8_n76bxLXajHa%=A;RM|mRt6O=~1wfte7(w<6H38u2(5QTera2N_iNCfQd zR@P^eDz(&FW_z9D%W^j$k+%!lwy~h=VW^qHXWP;3436P%74nE(w*LS)UjL_Q5%`>uYr_M9>i@3y_ch_Ubg3gWWvR{PIe@6 z!7$5oJEVXfk32C{DtM*`*lptLdFPn_IX*U9nm(Dg!}{Zrz@t2H-*Jp6IU@Lh-L7q} z(ACsgzQ-Nl?`B#n!ePDc~X3AGn1u4>ogXepcceXviw z4_tQvaoAK?l4X!=v#h|`!Q7a4UOWdI>UDPn^kv~|u$~CgGOL#oC9uNdm693pzrv>kW=ED1_Ogp9}O$uxp) zyx%!`$&l2^@HFEtjmGg5WCTrhz39%=w}JNZ!+BtSUIf*4w(f@79-BZc!Hfw1@GJxG zX88DTDRb<@>pqpM)%#Hfz9%^WH^i@TlR#r}AvoMNK-MeGv2LM_k<7>{B78OHDQkKb Vh9|?uge7J|k1Vu}buuR`_&-X9#+v{D literal 0 HcmV?d00001 diff --git a/DTMF_sounds/2.wav b/DTMF_sounds/2.wav new file mode 100644 index 0000000000000000000000000000000000000000..abdf9b24bc1285519c0dc6e5a5f19e7ca7a747b3 GIT binary patch literal 8044 zcmWle^;g>m!^LCYxCi0_fe=W5;MBXidsix}yKQY(*}Ar?Y;D(7sBWcQWp%gJ-H-$c zBqT@z2?-=5A@2G<@3}wS`v-i^J@-x@H+u9k9tc!6Y}Tk1>o=zmK_C$LfBNQs+3q1A z5ClX7EnB>K@&EhRiB$f&0zZotRocdcuA(*ot1aW3JS`H?;H;V=Ei>Y;=!v?UV42WG zRzTgP(;v{)VB4mc%Y3snWkqw%YX4m`rKs zCcjB}M*t!j(Z$3xE*QVdG@vH8;fn7Dni)$FlH_^Vd(_%@I zEjY%D1z02uv`Rm>7S*;C5Q%@3jHEccdG-_Wa?I<5O0Sfc>UU#L=e^E5N33wvnsx_K z2yT3pu}Jm7+(h|M_)t=ab(#0-S9`i){6wv3n1<`z%4jLh$w9)MI&6EEb2$tJ=Jc5i ze*~^@hLzmO+!%k^DpHZ{a-bPl>H#d)=&vc1q9*mLU&C$e=ld;b1pPkjYv6ClJ(f7#i5aK= zT)9J`4cD{4+~KIc;Cs+0>RQeW;E2XlDNw%#>-aa*sxhtrJ@y?jl{zA^qwQApLhV5m zUUXELg|U0F!DbYj_#r&IwNp0AIGF^=`yuNEs=u?swF}yao)`SF?Qo;kDr6ie+M9I@ zKHLJaj*rhp><{eHZc&$cvUx*FM`s#9M8kRGhtMk^H`J&f(%}g|Nq3d*PdA1(X^K1F z`AgvW(J!Vzw*cHETvQV0R=6e2nl`Tg8FCc(hW(i>8Lr42Rai-@u&^2)w%iY>v6m1b zFWAjNEf=27eMLNI*eoqn9}h1i`*A}f2H*GS=JYP%OXL#GiYj-@Ux|O|7bx2j6N8gc zA5(HuuY(dQ_tz#(Eui2nVLXP+4!(dRSOXa|!VQY?Rqyl&T$1n#cMxokKOd5y^rxu2 zh^AfAk>(>wc{$?LrO*YQvr#E-5%ycxzNQ5Y3I~LpQvl*yK&$K;-vT5Pnd1Dcys2Ci zF!6sczMcAC)NMN82tW=3CdZQ&eS30ZMApTU<=l1tR6Sr>p7;nm>|WRLn_&rja!yW( zn(?ROcI)@u(_-HPYyIbpbjuX%CW)^QM!9ElD}L0IVh|KQy4`xn`H7Gx(dPOv-p=jy z19WMLFSr!QXxBIX>Eu`0W3q^_p{-M;$8|TM$0#P`-~c8>C4;k$^U+bJ;z)J4YYJ)s zGZoK^d@4 z5JcqD&fw--p2=7Z`@FSWYxWTaGxotFge)LtAkLUp*2R>r$Vov*ULK==D9!#jG7Iwv z`oJ{1@of777%@9hFpQ@5$V~%%Ux7j3E#{4?V#64$J8xg%eo9l{EZrst3;G|ppN*hd zYPm}OQurkQ6|S;J&^Fq-1OhN(D?|G-_?61@PrXB z_%V}^Sfq|Ml$oV~1zQo_;IKx{vYty&{HQ;@kzO~#G83Iewm_=9XW~?*NIaCe({ZfU zSR3r?A?7iDN7{lt@u{?Vg6ZU)<^eSY^%Fe*&~j7SvDEOV*k|%Ft{bz_5UrZtxHQD$ z9TxmYI3L;?R^cwvl(3Dux7FXNaNwArSX4(u1>`;}{1Yh)biJcm_KTK-g<4iK20 zsZKK3h|xtkoz8|e#(#;I^Y`Wku>bXudcTC{B5p?Z=$o5=wq2o3FCP3qY)mzs>yr5f z181UVy5DGuywf<|l9!^_;C-D^4ckKpb|(y$=Z5WJcIu-NnP6F1($c@>C)YQKUC_}k zwZ)#uO8cd#g15zeuW^p@k!v^l66$zhkaIqCO&T#Tnf}=FU9C_t&AWl{fp9rS^%Vg> zrWT312`z>b)pr%C!8ues^(V;v02$cMX-z9f&e#4}$x>a7!#K~_Kfq@M??PtL{y1 zHYXp++a*jx^fwG&~PB)HnB(s5HR|Xr3wDjwn3mS_!>;>BIv=jv{@K=;- z(&xs8bj5lk(GP&%KUD|S#p12P?9ziN%ltpLf2)5G8U{}aFX_3{(+2xT)Li^G(_=rN z+^szoJc%q%T(tG;>qDItA1H88v^`@QD%uOeyRlP&_im|s0QRs*n>`l0x05ct(#DIQ zBGWPd1`GT7fy*dNzlS@^{J)R#$beBWg1av9J)WKNq%=x`YIF-E8nl*V;1ae`cFF+rhIuSL2Te z-RMt!B6+U7&hv^_ou9=!8{cmKA^0Oo3twl0DfX*;kzE;OMXNdcLPYa*S1;@b7_<+n zuF+OQ>&4FEZF-d#9{%O}num`&)QRci9xR@--Ef=0i zpjCHeR}3A%b21se#kW2(C;7C<#1jT9UjIr4I(3U8R5=zgbQ(Vt$!O}Eq zI(ctOHmND{Q)C9=JXcKm%d%E_yjI})$~cy>CrKUM9-ND2(@W6}Jx8QQ`P1OZ)a~MI zDl9_q7Qv2^#sO?&|60Df1@t0ww1iDX2EN!EV}D_!V6gFk+^IDoSLG%ZY^GGW=9oVO z4j=|64s;8f1Kqm`0}F`xHwkB~WAt0y>tKOIe(&~nMqe6rPw{cd2h=!YcU!l80<;hO zuJ2mcY42X9qIi_}IpmY(Ni)pW3VRC>yeoR=hF@{MEqW)&ig22%8^5uc5MR;MFx!ER zVaJ4zt50ykD_(KDT$0^8TT`?xbs!zV&#U|p_IpICAe5n7!?uc(K^9;&D$zU z+b&?UaAT$uH`zZda0j)BG&|;QYnE2%8gReo`b8C3xr^u;2fc=w9e&yVprOxHofIy3 zAwG(N+Wxl8i}gbiLcex+nqv+p>wd`)(E`}u?rX+5!RvrBtnNChog64g9bK9&{5{6+ zINY(;_Ys&MJ7r#Mq{U6?@X}QN2|rzZtF_L(C;#;3g5C$*}LSoReCQO z^8``hYji$=^D<--Ern;ABKx^{Z6HXj!wDkw-Vh=^T_Bo*9iU6EKBVGB6;x`{2+)YY ze#Cy>RsL3(q^*DD{MJ;6j}v3Kp&No6Ad|I?bt!gU<*Z!U@d`CZpyTcXG6UBpfl`!nA-gbM|CtD83~ zmUyJR<;ADd7AKm`UG{YlDZIP_=Cj;V8amfRV5l2W!1~TU6?Y>Cl>ZOmnE~0* zt^GMR7uyB-$+g6*BcQU6ijO04nhnydj%^?^=O{V)#79 zB0_H@5&D1^rI&LgAyi|yN@9G9rKT)NdKI4;I)Nc_FHkSL!*$Ar)2#bq1g@L#hCfP zcfHicS*`gXVb;UKz09kAw@Kht179H0UY4r8vjtI>d#s2?V>u7$b@m;Qn-ICp*?!uz z7yn=WnL;J$TrZ~mABzw?4wmcE=*QYOP*xVIB|HqMi_`prX(O0|pa=A3zHb1nuz)Nc z0Ih9<%e_5O=y2@x*c}HrT*AoC3#R`ZVJOenUNI#h*O6PH<=$t}RK^GKRL*WsPW@*Y z)G`PUX4Igz1b4=YscSN(Q%QZJq!Vh}?7NcSylHrS7#=%BF5~UTf9!cu&1l%{>tO8= zxJmzobHcB&Qo0oJr@>l1Oev45()NfVWO8VoPX%u$p`cpbSsAVEB8(tv%DGGC`0S1m zpc&Y3{H_inckA9`k$GDs-w|&*M)eBAy@(01dtL12ljhWZ1HI^@VCP@}# z4n%Enkv&S_0%mMrN-Ie|)K*9TP`DZSNBuh&TS_nqF1dbAy*r`bBsERBwFT zb2ai3zy`PKTDAW059x|hQ2NKvFIt&ye6SIi5INDKH&%fsW>QNRaKCXcQ=v7<0Wad0 z#BEzY>katJtT%<9Y3D8BhKkk=!RMH>fX;ozbqn<*lOxF{_BV`@{nt_$`H4iw&IwKT z2{0ovR%VtWhiSr9%GR93Q`!OYwfNSc0iDEuoO%OvUsYa-(ryM`ai7puf{z4;Bdgf` znd%T(QCI2L$FO~ZI`&B@J0OHEr|zOOdi&&wn$2c(^04gml(!I>Cp$ib5XU}oPHiOA z!=0zu)AC~}M?s|ybTEN@i+pJBuc%T^4%|)C6_uobq94uOE-*|7z#Qf3!|f{*)GT@l zgRAh9y3SiOLBnAt=NC;?*SGLVIq;HgjD3z^+wqp&V7)Df}Lx9BAcXN3)PW+ zhlhQuLklT?ir(=TMx9L)YHAISkpmet94_1wo=E;ju$9&0y;d)%aho*wyc84}8T&IV z!4KeJlV;chvZK;leP5}|GFp;H#dZZ}qY4;Q+^@a+Wy>2RzN_3uaV9-EYVgj1E+cj+1;U^hdx-5fcneG9Bbe7oQTwZyy59PpPRh~R9KQ+cTC zDCR}p!NNCWuwAadF=W$0;IZuWG4DYLUH{K1c;;u?B!Y4wZNEJcA-~#tL=T$eWu7dXoXj$h83jtxLxv*J|ff zwyL7>-E1vW4f{Sg05+Ie$*BPAS_W0VX(Pd#(sEMph$(?}pk+x%sm~%{Ray1lx+l14 zB6HeKgxcFL@(n(d_|PwC-X?o&x|>YQa|kW~zd85#<{=oEMc$>Vq(-)5D|=UwUeFIx zZaHMfLtOBGJaT%}v?Zmh(vxF(hF##I`>DUm;LY|5_EMIL{G$vb7kdw5`^iKmqLywHcK8Boh zjdMeAW5mDbAh5E|4(Xis>e!E@RVZ3utA8>6Qsz+Mdst^nUsZ|r3*-RZLhwhpp))vb z+VB)rv`>+!^y(&~a(Rhlb|M(;ME7uv^bLN#d`fk&yNT!%jAJeUw+7b1<+LJlwhPfP zN2au8(-Yzb_TK;X!L6V{q-sn?-`Iv_@{gWVyoS6FoPT0M$Bb}0+5~r74>sOVVWQ%U z0YxJ@EkT#L%CjC2z&fqdo7Zb)&?(vN#Q<}I=e0iDj)X{{zuOPB|84Z3`{$i0T9rJ@ zW@v--5<#nB8240twe=3MF~2&WKzL)!Xx4V0N|YgZ!Bb|uN0>Cb;J2Kih%4HC@}G@F z$Z^d6*i-w=z)9)`$!y`ZgiV!EJEmI=tR_)lSG_|c<<#5a@)SkjfxJR`+4KN2f*wPy z4#HxVr1FgQj7`oZwLeS4)|uomcRK+e85qqZg?J65gO;kAXsy##$+#(~CKpG+;XT-) zj1c-#_YXB1xg+RK*(_2fZ3uty6~H^m-vLt$3uM2jYe6>Q^W4H@Nubj4E)mCKz$?0j zG*oMa$mzMVyiCeecWtjQq(+QPd~2Mo6m}0Mb{CAy_u`wZxrUM6J=XeO9%)9Z-dr7Uy_Rs0vkp~0ws?mG>838TB&=6&HF?zp0Z0!(DR z3Q(Z?wD2dWhrw-jfBawG^ZdoUZSJau^>T}SHr7oTA7AE?fyZ(WWS^v=txoBe2BMoz zMpJjfasqh}5c_Hdk632BQZ=A)Qy`16lZ{59LS|4Yt&dlMT;I8)@|yBPtd2LEe+82r zydQf*+DoTHUbeSYgJvPt(D7YUZBYlYuX?rN$s59d5xy2$iX1^QnX@bqd zDnmotr#Eaf^-q3YkSTtFWcEF@y@*SYQ-c}W%gR@d8Ekk-f6;i@Pd&$a7KI1HSA@Rm zueRI#IKI5}r;JUp_Z{nWlLEDXDe|QUWSA4L6%>^Y;cxcK)l=Ke-h~J@2-H_#`3pK% z=qx(KdT3wSh;DKDCZn$bH6FF&GlD1NQ*BL^&oYtn`Xe-t-}&=~#qL zQeB{48l6x5NuVB+6FNcS1%6P4JEE8SGzTx0`>jgm-R0 zvC}P_a#&^HF(ijNH@P7oP$;S=8;_Gdh~TLn*lO>U7=g$p_&hh8RJBU0h_O1am7fWH z=J+;XL5)Wjx@IeXYdY*%k}{^q%MZk#^)D7n<4(!d6iukRYpV5p zf&-j$gSFFixsULJ%TdFp*mpq&LE=Nyakl6>bh?e{4+HjJ?IvQ#tOf zb$4n6mj5Ul1rF-J@%F$Db& zef)`edzfXBV~(aM66=C7%(ohrwIqXn%DP>!hH>5RHgESU;U6Iz%sZ6XIyGWL?((AE z2KQiC*3WambVG*)6Z^x zYhDT-4=?mpn2^2-y05?@E`Tc9&dGl;6~oAwvk`qCGmK`oX!j7O0;A$JwC%#j)U&pg(s{L49RreroHFdo(3Kb` z>3Pa${QVwwHLu~L56r%vwv_lbTpHPhCo-QQ)^shdKBrg|Q}B;xeIV5aX9Zk{MU*_q z{m!9MV%u#f)kc`(UvLV?p@nIA=>UPOiZcEuGl zDJfhyO5#E{^*%Pf_iq5Wv8Zuk+cIYn^FYb5?D4>juH_vOw-nYd@w9iSUh8*p@0YY> zZiruQ*R)J>-U6y2!yR~YT1?2_SG=D8-oIK2YCh^5gDgN^@@=z!2Oi5`oBx0nwX^Ft zHHqC9aL4fDqB}efp%+u5*^(r{#FTDrn&`Voi6k*0zxa>C#N2m+=~%n&ugZB!ROA^` z#oP%W5F|tYV6^ZWVTyK2!5cn3E|<8aL9Pj z_zHYCGq>~#x6(aDU8U^^tU#dRO_psI0-`5tO3^{uRLdc`r|m>=3a0XZOXMCW4Q2H*e(MBNLD1ka-w+#aSeWNn00-PgavYSSmOCPS0~73>9Veo~L8N}eV?V1Ai& zUi1g|I)vl1CDxEIc#lIM2kO^4H?mLV4NOrczIQALfiahni|mC>Y0A`KX&SbOmeLV< z-7EK00$|`#A4^4Q$AB`kvPv)19d$SV@S6nZpF*$MjZ7 zQyXr3v$<8G57b0N=9>V3D3cL^Zino!^4sV&!8a0sHYW7g*&CmRLqVm+_w{?)PQYf0 z@8_>g{>A&PWnicn34nv$(-h0PFj!BXt>8Twu#YxKJm+DHLFMMbt@ADK$lxMG{!Sdm z)Ty~)2cS#AHro>2CFg8>s>XjCV%9EHf5O@X%9EW?u*Er(S@#^#T7R9!ZYB7VR|B2? z3!qA7r?4O8uz73^ui?FiMeSz4MlA|$Nvx!HrtHL==o(cuwy7=zo*y9k*4K8&3=KoVP9*D#?nyF!izihDAw4@>0gTXWj{fjFc0c!2y)=p!%1DDw!0n| zr?ga+wH92ef7NOBtp;kN1*Vz$E8&&=?@AX4hK1FwincMHZ{S}+%lgim4}<6eUa^sz zcLd%5*jIz+5D`kTsICkE$ch}uW*zvBX)@Jl6cHNR+%JonMldsNRFo;0X)u} z02?(Bv(%$%9wguH*vbA{cv`Rz^7sEfsR_{Ch@I|=maVECfz4^O(ran4D5+aunHV1l zEc0H_!aB~zXJ-Cg+Qh5$@710$$|FO7h`+pRdN&CEmpHp*DRZOakP4|=5+)()BIUhs z+d$0Z9ATlE^1$?uT&KMg`4M9QKX?4=dV!mmeLa_hJ=KY<4QR&3=MmQ<0=^V~8bOxj z5MG0+TUx969W3Y#Y6LebA`4pa7X&RSkD@CS!>dyCt5BCXvy(2ze-2H@KIBE{7k!T! z994UINW{wYI(jN-VsH{-1>+R?xbs2Xa2cb|N;@y=V0}s0yk{XgvK3Qr3)dZQc;LCi NTPu;W%VH(2{{z-{w`c$W literal 0 HcmV?d00001 diff --git a/DTMF_sounds/3.wav b/DTMF_sounds/3.wav new file mode 100644 index 0000000000000000000000000000000000000000..e94977ce9be74082bd895c5bccc16cb383cc5791 GIT binary patch literal 8044 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 IBu+~FKcxc|LjV8( literal 0 HcmV?d00001 diff --git a/DTMF_sounds/4.wav b/DTMF_sounds/4.wav new file mode 100644 index 0000000000000000000000000000000000000000..b20dcba756b98d560309eacd5cbce134cb695c1e GIT binary patch literal 8044 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 literal 0 HcmV?d00001 diff --git a/DTMF_sounds/5.wav b/DTMF_sounds/5.wav new file mode 100644 index 0000000000000000000000000000000000000000..6d0ef37dcde5d1c2657fe937d01a340bc64e1733 GIT binary patch literal 8044 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 UjT;7gEpQn<1=nY2YA9>}KP3RGe*gdg literal 0 HcmV?d00001 diff --git a/DTMF_sounds/6.wav b/DTMF_sounds/6.wav new file mode 100644 index 0000000000000000000000000000000000000000..b67755aa523ddd145f578c6f9e0377f3d97fe379 GIT binary patch literal 8044 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 Pd5!xU$r~9r_!#qlis`># literal 0 HcmV?d00001 diff --git a/DTMF_sounds/7.wav b/DTMF_sounds/7.wav new file mode 100644 index 0000000000000000000000000000000000000000..0cc3acd2fd3a6810cab6e6194926916a1015d809 GIT binary patch literal 8044 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 TxyY352&|FVDX}ertU~`E&rrqD literal 0 HcmV?d00001 diff --git a/DTMF_sounds/8.wav b/DTMF_sounds/8.wav new file mode 100644 index 0000000000000000000000000000000000000000..c83eab797369f9a6f2ad84c36b6cc4eceaea5204 GIT binary patch literal 8044 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 Wij)RchqSnNNhiqnZND|#k^LVwZnP5s literal 0 HcmV?d00001 diff --git a/DTMF_sounds/9.wav b/DTMF_sounds/9.wav new file mode 100644 index 0000000000000000000000000000000000000000..b9910531cf367e8827397469e4a26c9eea18d077 GIT binary patch literal 8044 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 Z4s1|=BwfwhmR=hCL;7>?65ki>{{cB-!ixX^ literal 0 HcmV?d00001 diff --git a/DTMF_sounds/US_pol_survey_hello.gsm b/DTMF_sounds/US_pol_survey_hello.gsm new file mode 100644 index 0000000000000000000000000000000000000000..62e844ee9588a28e746affbe54dbeaeabfb888fa GIT binary patch literal 55737 zcmWJscUaTO+7-oJ%X&WqRH{n~9duPhuwSE(tvW;g?FN zOA8>j1QMzR(hF!v=*6|K>+Zdu|IaheGjraV_sltS6xb14av;c8l{Jcqdp4-38x+<7 zk*d<=p$r|RD0R?G!5U&H?x7^Acc?6O0hJ#fT0n7o02@NibGxK z32|xbc-ESBasWBa{kFTqXhFdgE>JTrv2H(~S0;ddrDS>36k5#37adYg!)(!Mefz$z zf_W@W?YfRIS-Pxt>i*r*pscCy*zuzkqcCfF>Cj09OK1yD2gO#GdZ>U)BNsLgkOyH7 z(w2$8*r=y|DFd@g>mfEZ1%zhWtX9rm>LtnFgr75PX5vs@ZGo-RgDCH)2o6QDF!_!`;5vV7kQ!#`^qDj;*V4E2nG2gM z@6G#alO+npaxW7R3Pk+wK%V(0m?_}&6WlQzPm)9eDEa*`l(!yek$IxTK7qQR!byF6i z6pK@_t*^JJcfn`UZs^#|O<$SGavLE2=y1CMWN#B}d0kOf(JxW_FoLC&0@kHYUoyP=^12)hX9e_mQt zJaAgDcb z;v~Cm78XWMvHvC3V&<%ed^fMDD!BdU$x(~yRQRf$VtJUoh5=b#OzMkm0HAHQH_$I( z2NKWs@8hBzqkOH?({T%Ta}7f2t-qve)ewjyo{M|;iPLKlnO?qHMhNKgx-odc6(l7cOmz+uPn_CrjGzO*<^|x@tg^~lx z(hnFoUSUE1CTKXge7DX3&R{EWJuB3=DA`h(RZIV~SN}UYMY=(7Ay+iD$H1oiB6Uu4pxG>m= zautsFUUIh4eDy_mO#edvmS=sQ17Vq4(seW z+~*m?*Etcx7$=8t2i8+M78P-8I9e%TK-j*itmH<{T(=9aepc^K(!VCRVD9@kCX+?6 zBQMFhaiXeI(2P+p<$s)&Q2^^dhYx8V04?a}MHA8sgjWVLT-+`@^Wp_U7}V|viO6f_ zr~0nNfESliU3Ddm8RbX7}AX+(06=w}+sECU54mhs+`9>7+9)T)!;+k`%A_QQtHqYR1)O9!tQE&HOj+Hf zO%Z5j&sw`3$C9LR-cZhAYjNyUNu1ONe!Y7@mcaT%O$x7zYlh~OxgLxgNmx88EB-#f z|Lr*YEH)KthbcT6Rmv$x3l`iTm@2sTBj3(1JBV6_Cwe2`mKK{zX!jD7|K1kUwLQNu z?5DKWaTnED7oh}bfBrL4cdlW_Hce-{i>egRTx!18jt~$z0_lNnCu5QNaRBpC!ESnx z9CDe?sc-T7!1ajM#yn&sL+}n|dSqd8%YNrEYUMuUqj{yEe=0?wBmwfZ7w(Yx>?O!% zgt;(�dW<)$nv!YX^a+JVs`^H1R%P$U5Ut&SQrKBtu2i_NIWx*x5|dLn5J-bNlL$ zS(4x$zOZ?o&IQi1`E()lqmgoeX0_SQ*PEg_s&KR5PQqX?q1OlCb^5j3)CjTbk(4_3 zGiLbd-89}FNFdHYa|P={!qf@yI$P8NBY_tNnN+_G;= z<3WphhKvhO)0074Q8=R;hjYX=@rbB9TLbo$6MAu_{Hs_Q^v{n9PBNC-*)tTOC`BGcLyzRd zjyrL6R!+`%eHeuaeT#k-%G6f|@hzW)`@a*62JqzJcdUBcHX9}GurVDtv)!?+Xu