заливка базы
This commit is contained in:
@@ -0,0 +1,513 @@
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import config
|
||||
import medods
|
||||
import aiofiles
|
||||
import os
|
||||
|
||||
|
||||
async def calls_to_log(in_dict):
|
||||
date_dir = datetime.now().strftime("%Y-%m-%d")
|
||||
date_dir_path = os.path.join("log", date_dir)
|
||||
if not os.path.exists(date_dir_path):
|
||||
os.makedirs(date_dir_path)
|
||||
file_name = f"log/{date_dir}/{in_dict.get('Linkedid')}.log"
|
||||
async with aiofiles.open(file_name, "a") as f:
|
||||
await f.write(f"\n\n{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
||||
for key, value in in_dict.items():
|
||||
await f.write(f"{key}: {value}\n")
|
||||
|
||||
|
||||
def redirect_ids(responsibles):
|
||||
if type(responsibles) is not list:
|
||||
responsibles = [responsibles]
|
||||
resp_list = []
|
||||
for resp in responsibles:
|
||||
resp_list.extend(config.REDIRECT_IDS[resp])
|
||||
return [{"id": int(x)} for x in resp_list]
|
||||
|
||||
|
||||
def phone_number(number: str):
|
||||
if len(number) == 6:
|
||||
number = f"78162{number}"
|
||||
else:
|
||||
if number.startswith("8"):
|
||||
number = f"7{number[1:]}"
|
||||
return number
|
||||
|
||||
|
||||
class CallHandler:
|
||||
def __init__(self):
|
||||
self.calls = {}
|
||||
self.date = datetime.now().date()
|
||||
self.finished = []
|
||||
|
||||
async def handle_event(self, event):
|
||||
def check_linkedid(event):
|
||||
linkedid = event.get("Linkedid")
|
||||
try:
|
||||
linkedid_split = linkedid.split(".")
|
||||
uniqueid = event.get("Uniqueid")
|
||||
uniqueid_split = uniqueid.split(".")
|
||||
if int(linkedid_split[1]) <= int(uniqueid_split[1]):
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def check_date():
|
||||
if self.date != datetime.now().date():
|
||||
self.date = datetime.now().date()
|
||||
logging.info(f"Date changed to {self.date} and reset calls database")
|
||||
if config.DEBUG:
|
||||
logging.warning(self.calls)
|
||||
self.finished = []
|
||||
self.calls = {}
|
||||
|
||||
def channel_to_responsible(channel: str):
|
||||
try:
|
||||
if channel.startswith("Local"):
|
||||
channel_data = channel.split("@")
|
||||
resp = channel_data[0]
|
||||
resp_data = resp.split("/")
|
||||
responsible = resp_data[-1]
|
||||
int(responsible)
|
||||
return responsible
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
try:
|
||||
if check_linkedid(event):
|
||||
if config.DEBUG:
|
||||
await calls_to_log(event)
|
||||
|
||||
linkedid = event.get("Linkedid")
|
||||
|
||||
if linkedid in self.finished:
|
||||
return
|
||||
|
||||
if linkedid not in self.calls.keys():
|
||||
check_date()
|
||||
context = event.get("Context")
|
||||
if context == "from-internal":
|
||||
self.finished.append(linkedid)
|
||||
return
|
||||
if context != "from-trunk":
|
||||
return
|
||||
if len(config.AMI_CHANNEL_FILTER) > 0:
|
||||
event_channel = event.get("Channel")
|
||||
if len(event_channel) < 7:
|
||||
return
|
||||
for filter in config.AMI_CHANNEL_FILTER:
|
||||
if event_channel.startswith(filter):
|
||||
await self.incoming_call(event, linkedid)
|
||||
return
|
||||
self.finished.append(linkedid)
|
||||
return
|
||||
else:
|
||||
await self.incoming_call(event, linkedid)
|
||||
return
|
||||
else:
|
||||
if event.get("ChannelStateDesc") == "Ring":
|
||||
if (
|
||||
event.get("Context") == "macro-user-callerid"
|
||||
and (
|
||||
event.get("Event") == "VarSet"
|
||||
or (
|
||||
event.get("Event") != "Newexten"
|
||||
and event.get("Variable") == "MACRO_DEPTH"
|
||||
)
|
||||
)
|
||||
and event.get("Application")
|
||||
not in ("ExecIf", "Goto", "Return")
|
||||
) or (
|
||||
event.get("Variable") == "DIALEDPEERNUMBER"
|
||||
and event.get("Exten") in config.OPERATORS
|
||||
):
|
||||
try:
|
||||
uniq = event.get("Uniqueid")
|
||||
if uniq != linkedid:
|
||||
if (
|
||||
uniq
|
||||
not in self.calls[linkedid]["records"].values()
|
||||
):
|
||||
target = "records"
|
||||
else:
|
||||
target = "records_duble"
|
||||
uniq_data = uniq.split(".")
|
||||
if int(uniq_data[1]) > int(linkedid.split(".")[1]):
|
||||
responsible = channel_to_responsible(
|
||||
event.get("Channel")
|
||||
)
|
||||
if responsible in config.OPERATORS:
|
||||
if (
|
||||
responsible
|
||||
in self.calls[linkedid][
|
||||
"records"
|
||||
].keys()
|
||||
):
|
||||
resp_uniq = self.calls[linkedid][
|
||||
"records"
|
||||
][responsible]
|
||||
if int(uniq_data[-1]) < int(
|
||||
resp_uniq.split(".")[-1]
|
||||
):
|
||||
return
|
||||
if (
|
||||
responsible
|
||||
not in self.calls[linkedid][
|
||||
"responsibles"
|
||||
]
|
||||
):
|
||||
self.calls[linkedid][
|
||||
"responsibles"
|
||||
].append(responsible)
|
||||
if target == "records":
|
||||
if (
|
||||
responsible
|
||||
in self.calls[linkedid][
|
||||
target
|
||||
].keys()
|
||||
):
|
||||
free_uniq = self.calls[linkedid][
|
||||
target
|
||||
][responsible]
|
||||
self.calls[linkedid][target][
|
||||
responsible
|
||||
] = uniq
|
||||
for (
|
||||
dub_id,
|
||||
dub_uniq,
|
||||
) in self.calls[
|
||||
linkedid
|
||||
]["records_duble"].items():
|
||||
if dub_uniq == free_uniq:
|
||||
if (
|
||||
dub_id
|
||||
not in self.calls[
|
||||
linkedid
|
||||
][target].keys()
|
||||
):
|
||||
self.calls[linkedid][
|
||||
target
|
||||
][dub_id] = dub_uniq
|
||||
self.calls[linkedid][
|
||||
"records_duble"
|
||||
].pop(dub_id)
|
||||
else:
|
||||
self.calls[linkedid][target][
|
||||
responsible
|
||||
] = uniq
|
||||
else:
|
||||
if (
|
||||
uniq
|
||||
not in self.calls[linkedid][
|
||||
target
|
||||
].values()
|
||||
):
|
||||
if responsible not in self.calls[
|
||||
linkedid
|
||||
]["records"].keys() or (
|
||||
responsible
|
||||
in self.calls[linkedid][
|
||||
"records"
|
||||
].keys()
|
||||
and self.calls[linkedid][
|
||||
"records"
|
||||
][responsible]
|
||||
!= uniq
|
||||
):
|
||||
self.calls[linkedid][target][
|
||||
responsible
|
||||
] = uniq
|
||||
return
|
||||
except:
|
||||
return
|
||||
if (
|
||||
event.get("Context") == "sub-record-check"
|
||||
and (
|
||||
".wav" in event.get("AppData")
|
||||
or "external" in event.get("AppData")
|
||||
)
|
||||
and event.get("Exten")
|
||||
== event.get("Extension")
|
||||
== "recordcheck"
|
||||
and event.get("Uniqueid") != linkedid
|
||||
):
|
||||
responsible = channel_to_responsible(event.get("Channel"))
|
||||
if (
|
||||
responsible in config.OPERATORS
|
||||
and responsible
|
||||
not in self.calls[linkedid]["responsibles"]
|
||||
):
|
||||
self.calls[linkedid]["records"][responsible] = (
|
||||
event.get("Uniqueid")
|
||||
)
|
||||
if (
|
||||
responsible
|
||||
not in self.calls[linkedid]["responsibles"]
|
||||
):
|
||||
self.calls[linkedid]["responsibles"].append(
|
||||
responsible
|
||||
)
|
||||
if self.calls[linkedid]["started"] is None:
|
||||
if (
|
||||
(
|
||||
event.get("DialStatus") == "ANSWER"
|
||||
and event.get("DestChannelStateDesc") == "Up"
|
||||
)
|
||||
or event.get("Variable")
|
||||
in ("BRIDGEPVTCALLID", "BRIDGEPEER")
|
||||
or event.get("BridgeTechnology") == "simple_bridge"
|
||||
):
|
||||
for var in config.ID_VARS:
|
||||
answered = event.get(var)
|
||||
if answered in self.calls[linkedid]["responsibles"]:
|
||||
await self.call_started(linkedid, answered)
|
||||
return
|
||||
if event.get("Disposition") == "NO ANSWER":
|
||||
if len(self.calls[linkedid]["responsibles"]) == 0:
|
||||
if event.get("ConnectedLineNum") is not None:
|
||||
self.calls[linkedid]["responsibles"].append(
|
||||
event.get("ConnectedLineNum")
|
||||
)
|
||||
await self.call_lost(linkedid)
|
||||
return
|
||||
else:
|
||||
if (
|
||||
event.get("BridgeTechnology") == "simple_bridge"
|
||||
and event.get("Context") == "from-internal-xfer"
|
||||
and event.get("ChannelStateDesc") == "Up"
|
||||
):
|
||||
self.call_transfered(
|
||||
linkedid, event.get("CallerIDNum"), event.get("Exten")
|
||||
)
|
||||
if (
|
||||
event.get("BridgeTechnology")
|
||||
== event.get("ToBridgeTechnology")
|
||||
== event.get("FromBridgeTechnology")
|
||||
== "simple_bridge"
|
||||
and event.get("CallerIDNum") not in config.OPERATORS
|
||||
and event.get("ChannelStateDesc") == "Up"
|
||||
):
|
||||
self.call_transfered(
|
||||
linkedid,
|
||||
event.get("ConnectedLineNum"),
|
||||
event.get("CallerIDNum"),
|
||||
)
|
||||
duration = int(
|
||||
(
|
||||
datetime.now() - self.calls[linkedid]["started"]
|
||||
).total_seconds()
|
||||
)
|
||||
if (
|
||||
(
|
||||
(
|
||||
"BillableSeconds" in event.keys()
|
||||
and event.get("Disposition") == "ANSWERED"
|
||||
and (
|
||||
event.get("Uniqueid") != linkedid
|
||||
or (
|
||||
event.get("Cause-txt") == "Normal Clearing"
|
||||
or event.get("Context")
|
||||
== "macro-hangupcall"
|
||||
or event.get("Application") == "Hangup"
|
||||
)
|
||||
)
|
||||
)
|
||||
or (
|
||||
"TalkTime" in event.keys()
|
||||
and event.get("Event") == "VarSet"
|
||||
)
|
||||
or (
|
||||
event.get("Application") == "Hangup"
|
||||
and event.get("Disposition") != "NO ANSWER"
|
||||
and event.get("ChannelStateDesc") != "Ring"
|
||||
and (
|
||||
event.get("Uniqueid") != linkedid
|
||||
or event.get("Cause-txt") == "Normal Clearing"
|
||||
)
|
||||
and duration > 1
|
||||
)
|
||||
or (
|
||||
event.get("AppData") == "hangupcall,"
|
||||
and event.get("ChannelStateDesc") == "Up"
|
||||
and (
|
||||
event.get("Uniqueid") != linkedid
|
||||
or event.get("Context") == "ext-queues"
|
||||
)
|
||||
)
|
||||
or (
|
||||
(
|
||||
event.get("Context") == "macro-hangupcall"
|
||||
and event.get("ChannelStateDesc") == "Up"
|
||||
)
|
||||
and (
|
||||
event.get("Uniqueid") != linkedid
|
||||
or duration > 1
|
||||
)
|
||||
and (
|
||||
(
|
||||
event.get("ConnectedLineNum")
|
||||
in config.OPERATORS
|
||||
or event.get("ConnectedLineNum")
|
||||
in self.calls[linkedid]["responsibles"]
|
||||
)
|
||||
or (
|
||||
(
|
||||
event.get("CallerIDNum")
|
||||
in config.OPERATORS
|
||||
or event.get("CallerIDNum")
|
||||
in self.calls[linkedid]["responsibles"]
|
||||
)
|
||||
and event.get("Event") == "BridgeLeave"
|
||||
)
|
||||
)
|
||||
)
|
||||
or (event.get("Event") == "Cdr")
|
||||
)
|
||||
and event.get("Context") != "from-internal-xfer"
|
||||
and not event.get("Event").startswith("RTC")
|
||||
):
|
||||
transfer_duration = None
|
||||
if self.calls[linkedid]["transfered"] is not None:
|
||||
transfer_duration = int(
|
||||
(
|
||||
datetime.now()
|
||||
- self.calls[linkedid]["transfered"]
|
||||
).total_seconds()
|
||||
)
|
||||
talk_time = 0
|
||||
for var in ("BillableSeconds", "TalkTime"):
|
||||
if var in event.keys():
|
||||
talk_time += int(event.get(var))
|
||||
break
|
||||
if talk_time > duration:
|
||||
duration = talk_time
|
||||
if duration >= 1 and (
|
||||
transfer_duration is None or transfer_duration > 1
|
||||
):
|
||||
record_id = None
|
||||
if (
|
||||
event.get("AppData") == "hangupcall,"
|
||||
and event.get("Cause") == "16"
|
||||
and event.get("Context") == "ext-local"
|
||||
and event.get("ConnectedLineNum")
|
||||
in config.OPERATORS
|
||||
and event.get("Uniqueid") != linkedid
|
||||
):
|
||||
record_id = event.get("Uniqueid")
|
||||
if record_id is None and duration < 2:
|
||||
return
|
||||
if event.get("Event") == "AttendedTransfer":
|
||||
record_id = event.get("TransfereeUniqueid")
|
||||
if (
|
||||
event.get("Context") == "macro-hangupcall"
|
||||
and event.get("Uniqueid") != linkedid
|
||||
and event.get("ConnectedLineNum")
|
||||
in config.OPERATORS
|
||||
and "BillableSeconds" not in event.keys()
|
||||
):
|
||||
record_id = event.get("Uniqueid")
|
||||
if (
|
||||
event.get("Application") == "Hangup"
|
||||
and event.get("Membership") == "static"
|
||||
and event.get("ConnectedLineNum")
|
||||
in config.OPERATORS
|
||||
and event.get("Uniqueid") != linkedid
|
||||
):
|
||||
record_id = event.get("Uniqueid")
|
||||
if record_id is None:
|
||||
for var in config.ID_VARS:
|
||||
answered = event.get(var)
|
||||
if (
|
||||
answered
|
||||
in self.calls[linkedid]["responsibles"]
|
||||
):
|
||||
try:
|
||||
record_id = self.calls[linkedid][
|
||||
"records"
|
||||
][answered]
|
||||
except:
|
||||
record_id = self.calls[linkedid][
|
||||
"records_duble"
|
||||
][answered]
|
||||
break
|
||||
if record_id is None:
|
||||
answered = channel_to_responsible(
|
||||
event.get("Channel")
|
||||
)
|
||||
if answered in self.calls[linkedid]["responsibles"]:
|
||||
try:
|
||||
record_id = self.calls[linkedid]["records"][
|
||||
answered
|
||||
]
|
||||
except:
|
||||
record_id = self.calls[linkedid][
|
||||
"records_duble"
|
||||
][answered]
|
||||
if record_id is None:
|
||||
return
|
||||
await self.call_finished(linkedid, duration, record_id)
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
||||
def call_transfered(self, linkedid, old_responsible, new_responsible):
|
||||
try:
|
||||
int(new_responsible)
|
||||
if new_responsible not in self.calls[linkedid]["responsibles"]:
|
||||
if old_responsible in self.calls[linkedid]["records"].keys():
|
||||
self.calls[linkedid]["records"][new_responsible] = self.calls[
|
||||
linkedid
|
||||
]["records"][old_responsible]
|
||||
self.calls[linkedid]["responsibles"].append(new_responsible)
|
||||
self.calls[linkedid]["transfered"] = datetime.now()
|
||||
else:
|
||||
if old_responsible in self.calls[linkedid]["records_duble"].keys():
|
||||
self.calls[linkedid]["records"][new_responsible] = self.calls[
|
||||
linkedid
|
||||
]["records_duble"][old_responsible]
|
||||
self.calls[linkedid]["responsibles"].append(new_responsible)
|
||||
self.calls[linkedid]["transfered"] = datetime.now()
|
||||
except:
|
||||
pass
|
||||
|
||||
async def incoming_call(self, event, linkedid):
|
||||
self.calls[linkedid] = {
|
||||
"responsibles": [],
|
||||
"started": None,
|
||||
"transfered": None,
|
||||
"records": {},
|
||||
"records_duble": {},
|
||||
}
|
||||
exten = event.get("Exten") if event.get("Exten") else event.get("Extension")
|
||||
logging.info(
|
||||
f"New incoming call: ID={linkedid}, Client={phone_number(event.get('CallerIDNum'))}, Phone={exten}"
|
||||
)
|
||||
await medods.incoming_call(
|
||||
linkedid, phone_number(event.get("CallerIDNum")), exten
|
||||
)
|
||||
|
||||
async def call_started(self, linkedid, responsible):
|
||||
logging.info(f"Call started: ID={linkedid}, Responsible={responsible}")
|
||||
self.calls[linkedid]["started"] = datetime.now()
|
||||
await medods.call_started(linkedid, redirect_ids(responsible))
|
||||
|
||||
async def call_finished(self, linkedid, duration, record_id):
|
||||
logging.info(
|
||||
f"Call finished: ID={linkedid}, Duration={duration}, Record ID={record_id}"
|
||||
)
|
||||
self.finished.append(linkedid)
|
||||
self.calls.pop(linkedid)
|
||||
await medods.call_finished(linkedid, duration)
|
||||
await medods.call_record_file(linkedid, record_id)
|
||||
|
||||
async def call_lost(self, linkedid):
|
||||
logging.info(
|
||||
f"Call lost: ID={linkedid}, responsibles: {redirect_ids(self.calls[linkedid]['responsibles'])}"
|
||||
)
|
||||
await medods.call_lost(
|
||||
linkedid, redirect_ids(self.calls[linkedid]["responsibles"])
|
||||
)
|
||||
Reference in New Issue
Block a user