Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
[add-trim-prefix-clid-custom]
; Ignoring non-working hours for VIP numbers for a double work rate:
exten => _.X!/_X9522140000,1,Set(IGNORE_TIME=1)
same => n,return
exten => _.X!/_X9629771111,1,Set(IGNORE_TIME=1)
same => n,return






[internal-users-custom]
exten => _X!,1,ExecIf($[ "${FROM_DID}x" == "x" ]?Set(__QUEUE_SRC_CHAN=${CHANNEL}))
same => n,return

1,Answer()
n,ChanSpy(${CHANNEL(channeltype)}/${EXTEN:3},qw)
n(hangup),Hangup();1,Answer()
n,ChanSpy(${CHANNEL(channeltype)}/${EXTEN:3},qBx)
n(hangup),Hangup();1,Answer()
n,ChanSpy(${CHANNEL(channeltype)}/${EXTEN:3},wvq(4)x)
n(hangup),Hangup();
[queue-pre-dial-custom]
exten => 2001,1,Set(CHANNEL(musicclass)=moh-5)
exten => 2002,1,Set(CHANNEL(musicclass)=moh-6)
exten => _X!,2,returnExample of using REST API to generate employees
{"jsonapi":{"version":"1.0"},
"result":true,
"data":
{
"type":"SIP",
"show_in_phonebook":"1",
"is_general_user_number":"1",
"public_access":"0",
"number":"275",
"user_id":"",
"user_avatar":"",
"user_username":"",
"user_email":"",
"mobile_uniqid":"EXTERNAL-7870338E",
"mobile_number":"",
"mobile_dialstring":"",
"sip_uniqid":"SIP-PHONE-5A230111",
"sip_secret":"Nk9oOFI2ZXdDMEU9",
"sip_type":"peer",
"sip_qualify":"1",
"sip_qualifyfreq":60,
"sip_enableRecording":"1",
"sip_dtmfmode":"auto",
"sip_transport":" ",
"sip_networkfilterid":"none",
"sip_manualattributes":"",
"fwd_ringlength":45,
"fwd_forwarding":"",
"fwd_forwardingonbusy":"",
"fwd_forwardingonunavailable":""
},
"messages":[],
"function":"getRecord",
"processor":"MikoPBX\\PBXCoreREST\\Lib\\Extensions\\GetRecord::main",
"pid":42448,
"meta":{
"timestamp":"2023-09-06T08:51:51+03:00",
"hash":"a16f973985cb3d0cbe75abb2c10a7c39b2203a87"
}
}Configuring simulated external calls

























import requests
import random
from faker import Faker
def generate_random_mobile():
return ''.join(random.choice("1234567890") for _ in range(11))
def main():
faker_ru = Faker('ru_RU')
faker_en = Faker('en_US')
faker_zh = Faker('zh_CN')
faker_ja = Faker('ja_JP')
for number in range(1, 699):
# Retrieving the data template for a new employee
get_response = requests.get("http://127.0.0.1:8081/pbxcore/api/extensions/getRecord?id=")
if get_response.json().get('result', False) == False:
print(f"getRecord error: {get_response.json().get('messages', 'Unknown error')}")
continue
data = get_response.json().get('data', {})
# Generating random data
locale = random.choice(['ru_RU', 'en_US', 'zh_CN', 'ja_JP'])
faker = locals()[f"faker_{locale.split('_')[0].lower()}"]
data['user_username'] = faker.name()
data['user_email'] = faker.email()
data['mobile_number'] = generate_random_mobile()
data['fwd_forwarding'] = data['mobile_number']
data['fwd_forwardingonbusy'] = data['mobile_number']
# Creating a new employee
post_response = requests.post("http://127.0.0.1:8081/pbxcore/api/extensions/saveRecord", data=data, headers={'Content-Type': 'application/x-www-form-urlencoded'})
if post_response.json().get('result', False):
print(f"Created user {number} -> Username: {data['user_username']}, Email: {data['user_email']}, Mobile: {data['mobile_number']}")
else:
print(f"saveRecord error for user {number}: {post_response.json().get('messages', 'Unknown error')}")
if post_response.status_code != 200 or not isinstance(post_response.json(), dict):
print(f"Unknown error: {post_response.text}")
if __name__ == "__main__":
main()
cd /tmp
python -m venv venv --without-pip
source venv/bin/activate
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py
pip3 install faker requests
python CreateRandomExtensions.py<?php
require_once 'Globals.php';
use \MikoPBX\Core\Asterisk\AGI;
use MikoPBX\Core\System\Notifications;
use MikoPBX\Core\System\{MikoPBXConfig};
$agi = new AGI();
$faxFile = "/tmp/" .$agi->get_variable("CDR(linkedid)", true).'.tiff';
$caller = $agi->get_variable("CALLERID(num)", true);
$agi->exec("ReceiveFax", "{$faxFile},d");
$result = $agi->get_variable("FAXOPT(status)", true);
if($result === 'SUCCESS' && file_exists($faxFile)){
$notify = new Notifications();
$notify->sendMail('your-email@gmail.com', 'Fax msg from '.$caller, 'Incoming fax <br><br>', $faxFile);
unlink($faxFile);
}
sleep(1);[SIP-1687941868-incoming](+)
exten => fax,1,Goto(internal,2200100,1)load => res_fax.so
load => res_fax_spandsp.so[endpoint]
fax_detect=yes
fax_detect_timeout=30[endpoint]
context=public-direct-dial[aor]
max_contacts = 1mkdir -p /usr/src/miko-mysql-to-sqlite;
cd /usr/src/miko-mysql-to-sqlitecurl '' -o 'mysql2sqlite.sh' ./mysql2sqlite.sh -ufreepbxuser -p123 asteriskcdrdb PT1C_cdr | grep -v 'CREATE INDEXmkdir -p /storage/usbdisk1/mikopbx/freepbx-dmp;
cd /storage/usbdisk1/mikopbx/freepbx-dmp;sqlite3 master.db 'SELECT strftime("%Y/%m/%d/", calldate) || recordingfile FROM PT1C_cdr WHERE recordingfile<>"" AND calldate > "2020-02-07"' > file-for-download.txtcurl '' -o ./download-and-convert.sh
sh ./download-and-convert.sh file-for-download.txt 10.70.10.6:443cp /storage/usbdisk1/mikopbx/astlogs/asterisk/cdr.db /storage/usbdisk1/mikopbx/astlogs/asterisk/cdr.db.dmpcp /storage/usbdisk1/mikopbx/astlogs/asterisk/cdr.db ./cdr.db sqlite3 << EOF
ATTACH DATABASE 'cdr.db' as 'new';
ATTACH DATABASE 'master.db' as 'old';
delete from new.cdr_general;
INSERT INTO new.cdr_general (
src_num,
dst_num,
src_chan,
dst_chan,
start,
answer,
endtime,
duration,
billsec,
disposition,
UNIQUEID,
did,
linkedid,
recordingfile
)
SELECT src,
dst,
channel,
dstchannel,
calldate,
answer,
"end",
duration,
billsec,
disposition,
linkedid || id,
did,
linkedid,
"/storage/usbdisk1/mikopbx/voicemailarchive/monitor/" || strftime("%Y/%m/%d/", calldate) || recordingfile || ".mp3"
FROM old.PT1C_cdr;
DETACH DATABASE 'new';
DETACH DATABASE 'old';
.exit
EOFsqlite3 ./cdr.db 'UPDATE cdr_general SET recordingfile="" where recordingfile LIKE "%/.mp3"'sqlite3 ./cdr.db 'UPDATE cdr_general SET recordingfile="" where start<"2020-02-07"'cp ./cdr.db /storage/usbdisk1/mikopbx/astlogs/asterisk/cdr.db 
<?php
namespace MikoPBX\Core\System;
require_once('Globals.php');
use MikoPBX\Common\Models\ExternalPhones;
use MikoPBX\Core\Asterisk\AGI;
use Phalcon\Mvc\Model\Resultset;
$agi = new AGI();
$number = substr($agi->request['agi_callerid'],-10);
if(strlen($number) < 7){
$agi->noop('Count < 7');
//Checking for the length of the number.
exit(0);
}
$outPhone = ExternalPhones::findFirst([
'conditions' => 'dialstring LIKE :number:',
'bind' => [
'number' => "%$number",
],
'hydration' => Resultset::HYDRATE_ARRAYS,
]);
if(count($outPhone) !== 1){
$agi->noop('ExternalPhones not found '.$number);
// Checking whether the phone number belongs to an employee of the company.
exit(0);
}
$agi->set_variable('AGIEXITONHANGUP', 'yes');
$agi->set_variable('AGISIGHUP', 'yes');
$agi->set_variable('__ENDCALLONANSWER', 'yes');
$agi->exec('Ringing', '');
$agi->Answer();
$result = $agi->getData('vm-enter-num-to-call', 3000, 11);
$selectednum = $result['result']??'';
if(!empty($selectednum)){
// Everything is OK. Ending the call.
$agi->set_variable('__pt1c_UNIQUEID', '');
$agi->exec(
'Dial',
"Local/{$selectednum}@all_peers/n,300," . 'TtekKHhU(dial_answer)b(dial_create_chan,s,1)'
);
}else{
$agi->noop('selectednum is empty');
}[add-trim-prefix-clid-custom]
exten => _.!,1,ExecIf($[ "${EXTEN}" == "h" ]?Hangup()
same => n,AGI(«DIALPLAN-APP-6A9902C631C5E7B5AC8F501C559FD678.php)
same => n,Return()

[endpoint]
callerid = 66179878899 <66179878899>[SIP-TRUNK-0CDC0182-22-outgoing]
exten => _X!,1,NoOp(Outgoing call to ${EXTEN})
same => n,Set(CALLERID(num)=66179878899)
same => n,Dial(PJSIP/${EXTEN}@SIP-TRUNK-0CDC0182)
same => n,Return()
1,Answer()
n,Set(PeerNumber=${CHANNEL(endpoint)})
n,Set(MemberStatus=${DB(QueueMemberOnPause/${PeerNumber})})
n,Set(AppName=${IF($[ "${MemberStatus}" != "1" ]?PauseQueueMember:UnpauseQueueMember)})
n,Set(NewMemberStatus=${IF($[ "${MemberStatus}" == "1" ]?0:1)})
n,Exec(${AppName}(,Local/${PeerNumber}@internal/n))
n,Set(DB(QueueMemberOnPause/${PeerNumber})=${NewMemberStatus})
n,Playback(beep)
n,Hangup()[all_peers](+)
exten => *46,1,Goto(applications,2200105,1)This article describes two ways to implement Telegram notifications for missed calls.
[add-trim-prefix-clid-custom]
exten => _[0-9*#+a-zA-Z][0-9*#+a-zA-Z]!,1,NoOp(start check blacklist)
same => n,Set(CHANNEL(hangup_handler_push)=hangup-ext-queues,h,1);
same => n,Return()
[hangup-ext-queues]
exten => h,1,ExecIf($["${M_DIALSTATUS}" = "ANSWER"]?return)
same => n,Set(TOKEN=5118292900:AAEWCOAXkay5fXb8AJptZmDyqkNk8QbP200)
same => n,Set(CHAT_ID=939950800)
same => n,Set(URL=https://api.telegram.org/bot${TOKEN}/sendMessage)
same => n,Set(TEXT=MISSED CALL from: ${CALLERID(name)}, did: ${FROM_DID}, callid: ${CHANNEL(callid)})
same => n,SHELL(curl -s -X POST '${URL}' -d chat_id='${CHAT_ID}' -d text='${TEXT}')
same => n,Set(MISSED=${SHELL(curl -s -X POST '${URL}' -d chat_id='${CHAT_ID}' -d text='${TEXT}')})
same => n,returnThe "Dynamic Queue Agents" application in MikoPBX is a feature designed to enhance the flexibility and efficiency of call center operations.












































*/1 * * * * /bin/touch /etc/asterisk/confbridge.conf > /dev/null 2> /dev/nullload => bridge_softmix.so
load => app_confbridge.so
load => app_page.so[paging-users]
exten => _X!,1,Set(dС=${PJSIP_DIAL_CONTACTS(${EXTEN})})
same => n,ExecIf($["${dС}x" != "x"]?Dial(${dС},,b(paging_create_chan,s,1)))
[paging_create_chan]
exten => s,1,Set(PJSIP_HEADER(add,Call-Info)=\;answer-after=0)
same => n,return1,Page(Local/202@paging-users&Local/203@paging-users)[public-direct-dial-custom]
exten => _.!,1,NoOp(check time)
same => n,Gosub(check-out-work-time-custom,${FROM_DID},1)
same => n,return
[check-out-work-time-custom]
exten => 584611,1,NoOp(check time)
same => n,ExecIfTime(00:00-09:00,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
same => n,ExecIfTime(18:00-23:59,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
same => n,return
exten => 420296,1,NoOp(check time)
same => n,ExecIfTime(00:00-08:00,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
same => n,ExecIfTime(20:00-23:59,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))
same => n,returnExecIfTime(times,weekdays,mdays,months?appname[(appargs)])ExecIfTime(00:00-08:00,mon-fri,*,*?Macro(playback-exit,/offload/asterisk/sounds/other/out_work_times))load => func_md5.so[dial_create_chan-custom]
exten => s,1,NoOp()
; Only for incoming calls
same => n,ExecIf($["${CHANNEL(channeltype)}" == "Local" || "${FROM_DID}x" == "x"]?return)
; No processing required if an internal number is calling
same => n,ExecIf(${DIALPLAN_EXISTS(internal,${CONNECTEDLINE(num)},1)}?return)
; No processing required if the number is not in the list
same => n,ExecIf($[${DIALPLAN_EXISTS(bluring-extensions,${CHANNEL(endpoint)},1)} == 0]?return)
; Retrieve the cached number
same => n,Set(blNumber=${CONNECTEDLINE(num):-4}*${MD5(${CONNECTEDLINE(num)})})
same => n,Set(DB(bluring/${blNumber})=${CONNECTEDLINE(num)})
; Replace the caller ID
same => n,Set(CONNECTEDLINE(num)=${blNumber})
same => n,Set(CONNECTEDLINE(name)=${blNumber})
same => n,return
[bluring-extensions]
; Allow number substitution for extension 201
exten => 201,1,NoOp()
; Allow substitution for all four-digit extensions
exten => _XXXX,1,NoOp()[all_peers](+)
exten => _XXXX*.,1,Set(number=${DB(bluring/${EXTEN})})
same => n,ExecIf($["${number}x" == "x"]?hangup)
same => n,Goto(all_peers,${number},1)load => func_shell.so<?php
require_once 'Globals.php';
use \GuzzleHttp\Client;
const API_KEY = '';
const CHAT_ID = '';
$agi = new MikoPBX\Core\Asterisk\AGI();
$name = $agi->get_variable('CALLERID(name)', true);
$num = $agi->get_variable('CALLERID(num)', true);
$did = $agi->get_variable('$FROM_DID', true);
$id = $agi->get_variable('CHANNEL(linkedid)', true);
$date = date('Y.d.m H:i:s', str_replace('mikopbx-', '', $id));
$TEXT = "Missed call: $name, did: $did, callid: $num, id: $id, date: $date";
$apiURL = 'https://api.telegram.org/bot' . API_KEY . '/';
$client = new Client([
'base_uri' => $apiURL,
'timeout' => 1,
'http_errors' => false,
]);
try {
$client->post( 'sendMessage', ['query' => ['chat_id' => CHAT_ID, 'text' => $TEXT]] );
}catch (Throwable $e){
}[add-trim-prefix-clid-custom]
exten => _.X!,1,Set(CHANNEL(hangup_handler_push)=hangup-ext-queues,h,1);
same => n,return
[hangup-ext-queues]
exten => h,1,ExecIf($["${M_DIALSTATUS}" = "ANSWER"]?return)
same => n,AGI(DIALPLAN-APP-1B2B846E.php)
same => n,returnsame => n,Set(MISSED=${SHELL(curl -X POST --data-urlencode "payload={\"channel\": \"#cannel_name\", \"username\": \"bot_name\", \"text\": \"Missed call from ${CALLERID(name)} using external line: ${FROM_DID} в ${STRFTIME(${EPOCH},,%H:%M:%S %d-%m-%Y)}\", \"icon_emoji\": \":sos:\"}" https://hooks.slack.com/services/T76G7L0/B01R/VMPQUeAN)})[SIP-PROVIDER-SIP-1687947415-after-dial-custom]
exten => _.!,1,Goto(quality-start,s,1)
same => n,return
[SIP-PROVIDER-SIP-1687947415-outgoing-custom]
exten => _.!,1,Set(DOPTIONS=${DOPTIONS}F(quality-start,s,1))
same => n,return
[quality-start]
exten => _.!,1,NoOp(--- Quality assessment ---)
same => n,ExecIf($[${M_DIALSTATUS}!=ANSWER]?return)
; We describe the path to the conversation termination file. Thank you for the quality assessment.
same => n,Set(filename_bye=/storage/usbdisk1/quality/sounds/bye)
; We describe the paths to the survey files.
same => n,Set(filename_1=/storage/usbdisk1/quality/sounds/miko_hello)
same => n,Set(filename_2=/storage/usbdisk1/quality/sounds/q_1)
same => n,Set(filename_3=/storage/usbdisk1/quality/sounds/q_2)
same => n,Set(f_num=0);
same => n,Goto(ivr-quality,s,1)
[ivr-quality]
exten => s,1,NoOP( start ivr quality )
same => n,Set(f_num=$[${f_num} + 1])
same => n,Set(filename=${filename_${f_num}})
same => n,GotoIf($["x${filename}" == "x"]?ivr-quality,bye,1);
same => n,Background(${filename})
same => n,WaitExten(5)
exten => _[1-5],1,NoOP( quality is ${EXTEN})
; AGI a script that will save the result of the evaluation of the conversation by the client.
same => n,AGI(/storage/usbdisk1/quality/quality_agi.php)
same => n,Goto(ivr-quality,s,1)
exten => _[06-9],Goto(ivr-quality,s,1)
exten => bye,1,ExecIf($["x${filename_bye}" != "x"]?Playback(${filename_bye}));
same => n,Hangup()[PROVIDER ID-outgoing-custom]
[PROVIDER ID-after-dial-custom]#!/usr/bin/php
<?php
require_once 'phpagi.php';
require_once 'globals.php';
$agi = new AGI();
$linkedid = $agi->get_variable('CDR(linkedid)', true);
$arr = [
'quality' => $agi->request['agi_extension'],
'f_num' => $agi->get_variable('f_num', true),
'filename' => $agi->get_variable('filename', true),
// 'Оцененный оператор:' => $operator,
'linkedid' => $linkedid,
'date' => date('Y-m-d H:i:s'),
'callerid' => $agi->request['agi_callerid']
];
$file_log = '/storage/usbdisk1/quality/'.$linkedid.'.log';
Util::mw_mkdir(dirname($file_log));
file_put_contents($file_log, json_encode($arr)."\n", FILE_APPEND);{"quality":"1","f_num":"1","filename":"\/offload\/asterisk\/sounds\/other\/miko_hello","linkedid":"mikopbx-1574331248.66","date":"2019-11-21 13:14:13","callerid":"79265775289"}
{"quality":"2","f_num":"2","filename":"beep","linkedid":"mikopbx-1574331248.66","date":"2019-11-21 13:14:15","callerid":"79265775289"}
{"quality":"3","f_num":"3","filename":"\/offload\/asterisk\/sounds\/other\/out_work_times","linkedid":"mikopbx-1574331248.66","date":"2019-11-21 13:14:16","callerid":"79265775289"}
<?php
use MikoPBX\Common\Models\CallDetailRecordsTmp;
use MikoPBX\Common\Models\CallQueueMembers;
use MikoPBX\Common\Models\CallQueues;
use MikoPBX\Common\Models\Extensions;
use MikoPBX\Core\Asterisk\AGI;
use MikoPBX\Core\Asterisk\Configs\QueueConf;
require_once 'Globals.php';
$agi = new AGI();
$agi->answer();
$extension = $agi->get_variable("CHANNEL(peername)", true);
if(empty($extension)){
$extension = $agi->get_variable("CALLERID(num)", true);
}
$q_exten = substr($agi->request['agi_extension'], 4);
$add_agent = substr($agi->request['agi_extension'], 3, 1);
$agi->noop("$extension $q_exten $add_agent");
$res = Extensions::findFirst("number='{$extension}'");
if(!$res){
// Error, such extension number does not exist.
$agi->hangup();
}
$res = CallQueues::findFirst("extension='{$q_exten}'");
if(!$res){
// Error, such queue does not exist.
$agi->hangup();
}
$a_count = CallQueueMembers::maximum([ "queue='{$res->uniqid}'", 'column' => 'priority'] ) + 1;
$member = CallQueueMembers::findFirst("queue='{$res->uniqid}' AND extension='{$extension}'");
if($add_agent === '1' && !$member){
$member = new CallQueueMembers();
$member->extension = $extension;
$member->queue = $res->uniqid;
$member->priority = $a_count;
$member->save();
QueueConf::queueReload();
}
if($add_agent == false && $member){
$member->delete();
QueueConf::queueReload();
}
$linkedid = $agi->get_variable("CDR(linkedid)", true);
$res = CallDetailRecordsTmp::find("linkedid='{$linkedid}'");
foreach ($res as $data){
$data->delete();
}
sleep(2);
$agi->hangup();










