заливка базы

This commit is contained in:
Dev PC
2025-07-03 01:35:09 +03:00
parent e7da06dcc1
commit ccde82adf3
25 changed files with 565320 additions and 0 deletions
+513
View File
@@ -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"])
)