init project
This commit is contained in:
commit
b78685db0b
2
.env
Normal file
2
.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
RABBITMQ_DEFAULT_USER = rabbit
|
||||||
|
RABBITMQ_DEFAULT_PASS = PYIjVMuouv2fYQzg7ZzPcTdGcAmCRVWa
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
**/.venv
|
79
docker-compose.yaml
Normal file
79
docker-compose.yaml
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
version: '3.2'
|
||||||
|
services:
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:3.11-management
|
||||||
|
ports:
|
||||||
|
- 15672:15672
|
||||||
|
- 5672:5672
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- RABBITMQ_DEFAULT_USER=rabbit
|
||||||
|
- RABBITMQ_DEFAULT_PASS=mrl2X0jwnYuCCiKFTshG7WKyOAhfDo
|
||||||
|
- RABBITMQ_DEFAULT_VHOST=mkt
|
||||||
|
- TZ=Asia/Krasnoyarsk
|
||||||
|
networks:
|
||||||
|
- phone
|
||||||
|
volumes:
|
||||||
|
- rabbitmq:/var/lib/rabbitmq
|
||||||
|
reciever:
|
||||||
|
build: ./reciever
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 5000:5000
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Krasnoyarsk
|
||||||
|
depends_on:
|
||||||
|
- rabbitmq
|
||||||
|
networks:
|
||||||
|
- phone
|
||||||
|
mongo:
|
||||||
|
image: mongo:6.0.4
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
environment:
|
||||||
|
- MONGO_INITDB_ROOT_USERNAME=mongodb
|
||||||
|
- MONGO_INITDB_ROOT_PASSWORD=Cc03Wz5XX3iI3uY3
|
||||||
|
- TZ=Asia/Krasnoyarsk
|
||||||
|
volumes:
|
||||||
|
- mongodb:/data/db
|
||||||
|
networks:
|
||||||
|
- phone
|
||||||
|
worker:
|
||||||
|
build: ./worker
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Krasnoyarsk
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
networks:
|
||||||
|
- phone
|
||||||
|
web:
|
||||||
|
build: ./web
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Krasnoyarsk
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
networks:
|
||||||
|
- phone
|
||||||
|
ports:
|
||||||
|
- 5001:5001
|
||||||
|
web_admin:
|
||||||
|
build: ./web_admin
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- TZ=Asia/Krasnoyarsk
|
||||||
|
depends_on:
|
||||||
|
- mongo
|
||||||
|
networks:
|
||||||
|
- phone
|
||||||
|
ports:
|
||||||
|
- 5002:5002
|
||||||
|
networks:
|
||||||
|
phone:
|
||||||
|
volumes:
|
||||||
|
rabbitmq:
|
||||||
|
mongodb:
|
||||||
|
|
||||||
|
|
0
rabbitmq/dockerfile
Normal file
0
rabbitmq/dockerfile
Normal file
53
reciever/app.py
Normal file
53
reciever/app.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import pika
|
||||||
|
from flask import Flask, jsonify, request
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
exch = 'mobilon'
|
||||||
|
server = 'rabbitmq'
|
||||||
|
r_user = 'rabbit'
|
||||||
|
r_pass = 'mrl2X0jwnYuCCiKFTshG7WKyOAhfDo'
|
||||||
|
|
||||||
|
|
||||||
|
class RMQ(object):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.connection = pika.BlockingConnection(pika.ConnectionParameters(
|
||||||
|
'rabbitmq', 5672, 'mkt', pika.PlainCredentials('rabbit', 'mrl2X0jwnYuCCiKFTshG7WKyOAhfDo')))
|
||||||
|
self.channel = self.connection.channel()
|
||||||
|
self.channel.exchange_declare(
|
||||||
|
exchange='mobilion', exchange_type='fanout')
|
||||||
|
result = self.channel.queue_declare(queue='incoming')
|
||||||
|
self.channel.queue_bind(exchange='mobilion', queue=result.method.queue)
|
||||||
|
|
||||||
|
def call(self, n):
|
||||||
|
self.response = None
|
||||||
|
self.channel.basic_publish(
|
||||||
|
exchange='mobilion', routing_key='', body=str(n))
|
||||||
|
self.channel.close()
|
||||||
|
self.connection.close()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def default():
|
||||||
|
return ("Ok")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/call/add/", methods=["POST"])
|
||||||
|
def rm():
|
||||||
|
try:
|
||||||
|
d: dict = request.get_json(True)
|
||||||
|
s: str = json.dumps(d).encode('utf8', errors="ignore")
|
||||||
|
rp = RMQ()
|
||||||
|
threading.Thread(target=rp.call(str(d),)).start()
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(e)
|
||||||
|
finally:
|
||||||
|
return ("Ok")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.debug = False
|
||||||
|
app.run(host="0.0.0.0", port=5000)
|
||||||
|
app.run(port=5000)
|
14
reciever/dockerfile
Normal file
14
reciever/dockerfile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
FROM alpine
|
||||||
|
|
||||||
|
RUN apk update && apk upgrade && apk add python3 && apk add -U tzdata
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt requirements.txt
|
||||||
|
ADD app.py /app
|
||||||
|
RUN python3 -m venv .venv
|
||||||
|
RUN /app/.venv/bin/pip3 install -r /app/requirements.txt
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
CMD [".venv/bin/python3", "app.py"]
|
8
reciever/phone.code-workspace
Normal file
8
reciever/phone.code-workspace
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": ".."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
9
reciever/requirements.txt
Normal file
9
reciever/requirements.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
click==8.1.3
|
||||||
|
Flask==2.2.3
|
||||||
|
importlib-metadata==6.1.0
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.2
|
||||||
|
pika==1.3.1
|
||||||
|
Werkzeug==2.2.3
|
||||||
|
zipp==3.15.0
|
59
web/app.py
Normal file
59
web/app.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
from urllib import request
|
||||||
|
from flask import Flask, jsonify, render_template, url_for, request, redirect
|
||||||
|
import json
|
||||||
|
import datetime
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
CONNECTION_STRING = "mongodb://mongodb:Cc03Wz5XX3iI3uY3@mongo"
|
||||||
|
|
||||||
|
db_connection = MongoClient(CONNECTION_STRING)
|
||||||
|
db_base = db_connection["phone"]
|
||||||
|
coll_call = db_base["phone"]
|
||||||
|
coll_history = db_base["history"]
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
internal = {}
|
||||||
|
external = {}
|
||||||
|
State = ""
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def root():
|
||||||
|
return "Ok"
|
||||||
|
|
||||||
|
|
||||||
|
IgnoreList = ['83919865589', '83912051046', '83912051045', '84950213944', '84951252791', '83919865589',
|
||||||
|
'84951183750', '89919237009', '89919241441', '89919863883', '89919505445', '89919398228', '89919500798']
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/")
|
||||||
|
def WebCall():
|
||||||
|
call = coll_call.find().sort('time', -1)
|
||||||
|
return render_template("WebCall.html", call=call)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/test/")
|
||||||
|
def GetTest():
|
||||||
|
call = coll_call.find({'client': {'$nin': IgnoreList}}).sort('time', -1)
|
||||||
|
return render_template("TestCall.html", call=call)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/test/<id>")
|
||||||
|
def GetTestId(id):
|
||||||
|
call = coll_call.find(
|
||||||
|
{'client': {'$nin': IgnoreList}, 'status': int(id)}).sort('time', -1)
|
||||||
|
return render_template("TestCall.html", call=call)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/status/<id>")
|
||||||
|
def WebCallStatus(id):
|
||||||
|
call = coll_call.find({"status": int(id)}).sort('time', -1)
|
||||||
|
call = coll_call.find(
|
||||||
|
{'client': {'$nin': IgnoreList}, 'status': int(id)}).sort('time', -1)
|
||||||
|
return render_template("WebCall.html", call=call)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.debug = True
|
||||||
|
app.run(host="0.0.0.0", port=5001)
|
16
web/dockerfile
Normal file
16
web/dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
FROM alpine
|
||||||
|
|
||||||
|
RUN apk update && apk upgrade && apk add python3 && apk add -U tzdata
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt requirements.txt
|
||||||
|
COPY app.py /app
|
||||||
|
COPY static /app/static
|
||||||
|
COPY templates /app/templates
|
||||||
|
RUN python3 -m venv .venv
|
||||||
|
RUN /app/.venv/bin/pip3 install -r /app/requirements.txt
|
||||||
|
|
||||||
|
EXPOSE 5001
|
||||||
|
|
||||||
|
CMD [".venv/bin/python3", "app.py"]
|
10
web/requirements.txt
Normal file
10
web/requirements.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
click==8.1.3
|
||||||
|
dnspython==2.3.0
|
||||||
|
Flask==2.2.3
|
||||||
|
importlib-metadata==6.2.0
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.2
|
||||||
|
pymongo==4.3.3
|
||||||
|
Werkzeug==2.2.3
|
||||||
|
zipp==3.15.0
|
47
web/static/main.css
Normal file
47
web/static/main.css
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 5px solid #fff;
|
||||||
|
border-top: 5px solid #fff;
|
||||||
|
border-bottom: 3px solid #fff;
|
||||||
|
border-collapse: collapse;
|
||||||
|
outline: 3px solid #ffd300;
|
||||||
|
font-size: 15px;
|
||||||
|
background: #fff !important;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 7px;
|
||||||
|
background: #ffd300;
|
||||||
|
border: none;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 15px;
|
||||||
|
border-top: 3px solid #fff;
|
||||||
|
border-bottom: 3px solid #ffd300;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
padding: 7px;
|
||||||
|
border: none;
|
||||||
|
border-top: 3px solid #fff;
|
||||||
|
border-bottom: 3px solid #fff;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody tr:nth-child(even) {
|
||||||
|
background: #f8f8f8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: large;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
color: chocolate;
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
height: 50px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
22
web/templates/DeleteCall.html
Normal file
22
web/templates/DeleteCall.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Удаление документа</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h2>Вы действительно хотите удалить номер: {{ id }}?</h2>
|
||||||
|
Причина удаления?
|
||||||
|
<form action="/web/call/delete/confirm" method="post">
|
||||||
|
<textarea name="reason"></textarea>
|
||||||
|
<input type="hidden" name="id" value="{{id}}">
|
||||||
|
<button type="submit">Удалить</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
43
web/templates/TestCall.html
Normal file
43
web/templates/TestCall.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='main.css') }}">
|
||||||
|
<title>Lost Calls</title>
|
||||||
|
</head>
|
||||||
|
<header>
|
||||||
|
<a href="/web/call/status/0">Входящий вызов принят</a>
|
||||||
|
<a href="/web/call/status/1">Входящий вызов не принят</a>
|
||||||
|
<a href="/web/call/status/2">Перезвонили</a>
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Номер клиента</td>
|
||||||
|
<!-- <td>Номер оператора</td> -->
|
||||||
|
<td>Дата время</td>
|
||||||
|
<td>Ссылка на запись</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% for entry in call %}
|
||||||
|
<tr>
|
||||||
|
<!-- <td>{{ entry.client }}</td> -->
|
||||||
|
<td><a href="tel:{{ entry.client }}">{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}</a></td>
|
||||||
|
<!-- <td>{{ entry.Operator }}</td> -->
|
||||||
|
<td>{{ entry.time }}</td>
|
||||||
|
<td>
|
||||||
|
{% if entry.recordUrl|length > 1 %}
|
||||||
|
<audio src="{{ entry.recordUrl }}" type="audio/mp3" preload="none" controls>Запись</audio>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</html>
|
47
web/templates/WebCall.html
Normal file
47
web/templates/WebCall.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='main.css') }}">
|
||||||
|
<title>Lost Calls</title>
|
||||||
|
</head>
|
||||||
|
<header>
|
||||||
|
<a href="/web/call/status/0">Входящий вызов принят</a>
|
||||||
|
<a href="/web/call/status/1">Входящий вызов не принят</a>
|
||||||
|
<a href="/web/call/status/2">Перезвонили</a>
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Номер клиента</td>
|
||||||
|
<!-- <td>Номер оператора</td> -->
|
||||||
|
<td>Дата время</td>
|
||||||
|
<td>Ссылка на запись</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% for entry in call %}
|
||||||
|
<tr>
|
||||||
|
<!-- <td>{{ entry.client }}</td> -->
|
||||||
|
{% if entry.important == True %}
|
||||||
|
<td><font color="#C30000">{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}</font></td>
|
||||||
|
{% else %}
|
||||||
|
<td >{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}</td>
|
||||||
|
{% endif %}
|
||||||
|
<!-- <td>{{ entry.Operator }}</td> -->
|
||||||
|
<td>{{ entry.time }}</td>
|
||||||
|
<td>
|
||||||
|
{% if entry.recordUrl|length > 1 %}
|
||||||
|
<audio src="{{ entry.recordUrl }}" type="audio/mp3" preload="none" controls>Запись</audio>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</html>
|
74
web_admin/app.py
Normal file
74
web_admin/app.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
from urllib import request
|
||||||
|
from flask import Flask, jsonify, render_template, url_for, request, redirect
|
||||||
|
import json
|
||||||
|
import datetime
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
CONNECTION_STRING = "mongodb://mongodb:Cc03Wz5XX3iI3uY3@mongo"
|
||||||
|
|
||||||
|
db_connection = MongoClient(CONNECTION_STRING)
|
||||||
|
db_base = db_connection["phone"]
|
||||||
|
coll_call = db_base["phone"]
|
||||||
|
coll_history = db_base["history"]
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
internal = {}
|
||||||
|
external = {}
|
||||||
|
State = ""
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def root():
|
||||||
|
return "Ok"
|
||||||
|
|
||||||
|
|
||||||
|
IgnoreList = ['83919865589', '83912051046', '83912051045', '84950213944', '84951252791', '83919865589',
|
||||||
|
'84951183750', '89919237009', '89919241441', '89919863883', '89919505445', '89919398228', '89919500798']
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/")
|
||||||
|
def WebCall():
|
||||||
|
call = coll_call.find().sort('time', -1)
|
||||||
|
return render_template("WebCall.html", call=call)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/test/")
|
||||||
|
def GetTest():
|
||||||
|
call = coll_call.find({'client': {'$nin': IgnoreList}}).sort('time', -1)
|
||||||
|
return render_template("TestCall.html", call=call)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/test/<id>")
|
||||||
|
def GetTestId(id):
|
||||||
|
call = coll_call.find(
|
||||||
|
{'client': {'$nin': IgnoreList}, 'status': int(id)}).sort('time', -1)
|
||||||
|
return render_template("TestCall.html", call=call)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/status/<id>")
|
||||||
|
def WebCallStatus(id):
|
||||||
|
call = coll_call.find({"status": int(id)}).sort('time', -1)
|
||||||
|
call = coll_call.find(
|
||||||
|
{'client': {'$nin': IgnoreList}, 'status': int(id)}).sort('time', -1)
|
||||||
|
return render_template("WebCall.html", call=call)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/delete/<id>")
|
||||||
|
def WebCallDelete(id):
|
||||||
|
return render_template("DeleteCall.html", id=id)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/web/call/delete/confirm", methods=['POST'])
|
||||||
|
def WebCallDeleteConfirm():
|
||||||
|
phone = request.form.get("id")
|
||||||
|
reason = request.form.get('reason')
|
||||||
|
res = phone + " - " + reason
|
||||||
|
coll_call.update_one({'$and': [{'client': phone}, {'status': 1}]}, {
|
||||||
|
'$set': {'status': 9, 'reason': reason}})
|
||||||
|
return redirect("http://192.168.0.20:5001/web/call/status/1")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.debug = True
|
||||||
|
app.run(host="0.0.0.0", port=5002)
|
16
web_admin/dockerfile
Normal file
16
web_admin/dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
FROM alpine
|
||||||
|
|
||||||
|
RUN apk update && apk upgrade && apk add python3 && apk add -U tzdata
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt requirements.txt
|
||||||
|
COPY app.py /app
|
||||||
|
COPY static /app/static
|
||||||
|
COPY templates /app/templates
|
||||||
|
RUN python3 -m venv .venv
|
||||||
|
RUN /app/.venv/bin/pip3 install -r /app/requirements.txt
|
||||||
|
|
||||||
|
EXPOSE 5001
|
||||||
|
|
||||||
|
CMD [".venv/bin/python3", "app.py"]
|
10
web_admin/requirements.txt
Normal file
10
web_admin/requirements.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
click==8.1.3
|
||||||
|
dnspython==2.3.0
|
||||||
|
Flask==2.2.3
|
||||||
|
importlib-metadata==6.2.0
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.2
|
||||||
|
pymongo==4.3.3
|
||||||
|
Werkzeug==2.2.3
|
||||||
|
zipp==3.15.0
|
47
web_admin/static/main.css
Normal file
47
web_admin/static/main.css
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 5px solid #fff;
|
||||||
|
border-top: 5px solid #fff;
|
||||||
|
border-bottom: 3px solid #fff;
|
||||||
|
border-collapse: collapse;
|
||||||
|
outline: 3px solid #ffd300;
|
||||||
|
font-size: 15px;
|
||||||
|
background: #fff !important;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 7px;
|
||||||
|
background: #ffd300;
|
||||||
|
border: none;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 15px;
|
||||||
|
border-top: 3px solid #fff;
|
||||||
|
border-bottom: 3px solid #ffd300;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td {
|
||||||
|
padding: 7px;
|
||||||
|
border: none;
|
||||||
|
border-top: 3px solid #fff;
|
||||||
|
border-bottom: 3px solid #fff;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody tr:nth-child(even) {
|
||||||
|
background: #f8f8f8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: large;
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
color: chocolate;
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
height: 50px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
23
web_admin/templates/DeleteCall.html
Normal file
23
web_admin/templates/DeleteCall.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Удаление документа</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h2>Вы действительно хотите удалить номер: {{ id }}?</h2>
|
||||||
|
Причина:
|
||||||
|
<form action="/web/call/delete/confirm" method="post">
|
||||||
|
<input type="hidden" name="id" value="{{id}}">
|
||||||
|
<textarea name="reason" rows="5" cols="60"></textarea>
|
||||||
|
<br>
|
||||||
|
<button type="submit">Удалить</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
43
web_admin/templates/TestCall.html
Normal file
43
web_admin/templates/TestCall.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='main.css') }}">
|
||||||
|
<title>Lost Calls</title>
|
||||||
|
</head>
|
||||||
|
<header>
|
||||||
|
<a href="/web/call/status/0">Входящий вызов принят</a>
|
||||||
|
<a href="/web/call/status/1">Входящий вызов не принят</a>
|
||||||
|
<a href="/web/call/status/2">Перезвонили</a>
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Номер клиента</td>
|
||||||
|
<!-- <td>Номер оператора</td> -->
|
||||||
|
<td>Дата время</td>
|
||||||
|
<td>Ссылка на запись</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% for entry in call %}
|
||||||
|
<tr>
|
||||||
|
<!-- <td>{{ entry.client }}</td> -->
|
||||||
|
<td><a href="tel:{{ entry.client }}">{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}</a></td>
|
||||||
|
<!-- <td>{{ entry.Operator }}</td> -->
|
||||||
|
<td>{{ entry.time }}</td>
|
||||||
|
<td>
|
||||||
|
{% if entry.recordUrl|length > 1 %}
|
||||||
|
<audio src="{{ entry.recordUrl }}" type="audio/mp3" preload="none" controls>Запись</audio>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</html>
|
64
web_admin/templates/WebCall.html
Normal file
64
web_admin/templates/WebCall.html
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='main.css') }}">
|
||||||
|
<title>Lost Calls</title>
|
||||||
|
</head>
|
||||||
|
<header>
|
||||||
|
<a href="/web/call/status/0">Входящий вызов принят</a>
|
||||||
|
<a href="/web/call/status/1">Входящий вызов не принят</a>
|
||||||
|
<a href="/web/call/status/2">Перезвонили</a>
|
||||||
|
<a href="/web/call/status/9">Удаленные</a>
|
||||||
|
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>Номер клиента</td>
|
||||||
|
<!-- <td>Номер оператора</td> -->
|
||||||
|
<td>Дата время</td>
|
||||||
|
<td>Дополнитительная информация</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% for entry in call %}
|
||||||
|
<tr>
|
||||||
|
<!-- <td>{{ entry.client }}</td> -->
|
||||||
|
{% if entry.important == True %}
|
||||||
|
<td>
|
||||||
|
<font color="#C30000">{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7],
|
||||||
|
entry.client[7:11]) }}</font>
|
||||||
|
{% if entry.status == 1 %}
|
||||||
|
<a href="/web/call/delete/{{entry.client}}">[удалить]</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td>{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}
|
||||||
|
{% if entry.status == 1 %}
|
||||||
|
<a href="/web/call/delete/{{entry.client}}">[удалить]</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- <td>{{ entry.Operator }}</td> -->
|
||||||
|
<td>{{ entry.time }}</td>
|
||||||
|
<td>
|
||||||
|
{% if entry.recordUrl|length > 1 %}
|
||||||
|
<audio src="{{ entry.recordUrl }}" type="audio/mp3" preload="none" controls>Запись</audio>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if entry.reason|length > 1 %}
|
||||||
|
{{ entry.reason }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</html>
|
119
worker/app.py
Normal file
119
worker/app.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import pika
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import datetime
|
||||||
|
import requests
|
||||||
|
from pymongo import MongoClient
|
||||||
|
|
||||||
|
|
||||||
|
db_connection = MongoClient("mongodb://mongodb:Cc03Wz5XX3iI3uY3@mongo")
|
||||||
|
db_base = db_connection["phone"]
|
||||||
|
coll_phone = db_base["phone"]
|
||||||
|
coll_userkey = db_base['userkey']
|
||||||
|
|
||||||
|
#def sendMessage(dt, num):
|
||||||
|
# token = "2035324623:AAGACtvZ551m9V--yTYF9cFuegGejylSsLg"
|
||||||
|
# chat_id = "-1001941363918"
|
||||||
|
# message = "*" + num + "* - " + dt
|
||||||
|
# send_text = 'https://api.telegram.org/bot' + token + '/sendMessage?chat_id=' + chat_id + '&parse_mode=Markdown&text=' + message
|
||||||
|
# response = requests.get(send_text)
|
||||||
|
# return response
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
connection = pika.BlockingConnection(pika.ConnectionParameters(
|
||||||
|
'rabbitmq', 5672, 'mkt', pika.PlainCredentials('rabbit', 'mrl2X0jwnYuCCiKFTshG7WKyOAhfDo')))
|
||||||
|
channel = connection.channel()
|
||||||
|
|
||||||
|
channel.queue_declare(queue='incoming')
|
||||||
|
tmpIncoming = {}
|
||||||
|
# tmpExternal = {}
|
||||||
|
|
||||||
|
def callback(ch, method, properties, body: bytearray):
|
||||||
|
try:
|
||||||
|
# Парсим строку
|
||||||
|
srcJson = json.loads(str(body.decode('utf-8')).replace("\'", "\""))
|
||||||
|
srcJson["time"] = datetime.datetime.fromtimestamp(
|
||||||
|
srcJson["time"]).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
# Определяем направление соединения
|
||||||
|
if srcJson['direction'] == 'incoming':
|
||||||
|
# Определяем начальный статус
|
||||||
|
if srcJson['state'] == 'START':
|
||||||
|
# Создаем переменную. Ответ = false, можно закрывать = false, Приоритетная линия
|
||||||
|
if srcJson['to'] == '83912051045':
|
||||||
|
tmpIncoming[srcJson['uuid']] = [False, False, True]
|
||||||
|
else:
|
||||||
|
tmpIncoming[srcJson['uuid']] = [False, False, False]
|
||||||
|
# Обновление статуса при входящем звонке
|
||||||
|
coll_phone.delete_one({'$and': [{'client': srcJson['from']}, {'status': 1}]})
|
||||||
|
if srcJson['state'] == 'ANSWER' and srcJson['uuid'] in tmpIncoming:
|
||||||
|
tmpIncoming[srcJson['uuid']][0] = True
|
||||||
|
if srcJson['state'] == 'END' and srcJson['uuid'] in tmpIncoming:
|
||||||
|
tmpIncoming[srcJson['uuid']][1] = True
|
||||||
|
if srcJson['state'] == 'HANGUP' and srcJson['uuid'] in tmpIncoming and tmpIncoming[srcJson['uuid']][1] == True:
|
||||||
|
try:
|
||||||
|
srcJson['callstatus']
|
||||||
|
insDict = {"client": srcJson['from'], "time": srcJson['time'], "status": 0,
|
||||||
|
"recordUrl": srcJson["recordUrl"], "duration": srcJson["duration"], "important": tmpIncoming[srcJson['uuid']][2]}
|
||||||
|
except Exception as e:
|
||||||
|
# print(srcJson)
|
||||||
|
print(e)
|
||||||
|
insDict = {
|
||||||
|
"client": srcJson['from'], "time": srcJson['time'], "status": 1, "important": tmpIncoming[srcJson['uuid']][2]}
|
||||||
|
#sendMessage(srcJson['time'], srcJson['from'])
|
||||||
|
finally:
|
||||||
|
print(insDict)
|
||||||
|
coll_phone.insert_one(insDict)
|
||||||
|
tmpIncoming.pop(srcJson['uuid'])
|
||||||
|
try:
|
||||||
|
insUserKey = {'userkey': srcJson['userkey']}
|
||||||
|
print(insUserKey)
|
||||||
|
print(srcJson['to'])
|
||||||
|
coll_userkey.update_one(
|
||||||
|
filter={
|
||||||
|
'operator': srcJson['to'],
|
||||||
|
},
|
||||||
|
update={
|
||||||
|
'$set': insUserKey,
|
||||||
|
},
|
||||||
|
upsert=True
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
if srcJson['direction'] == 'external':
|
||||||
|
# coll_phone.update_one({'$and': [{'client': srcJson['to']}, {'status': 1}]}, {
|
||||||
|
# '$set': {'status': 2}})
|
||||||
|
r = coll_phone.update_one({'$and': [{'client': {'$regex': srcJson['to']}}, {'status': 1}]}, {
|
||||||
|
'$set': {'status': 2, 'callid': srcJson['uuid']}})
|
||||||
|
if srcJson['state'] == 'HANGUP':
|
||||||
|
try:
|
||||||
|
coll_phone.update_one({'callid':srcJson['uuid']}, {'$set': {'recordUrl': srcJson['recordUrl']}})
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print(r)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(e.with_traceback)
|
||||||
|
print(e)
|
||||||
|
# print(None)
|
||||||
|
exit()
|
||||||
|
|
||||||
|
channel.basic_consume(
|
||||||
|
queue='incoming', on_message_callback=callback, auto_ack=True)
|
||||||
|
|
||||||
|
print(' [*] Waiting for messages. To exit press CTRL+C')
|
||||||
|
channel.start_consuming()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print('Interrupted')
|
||||||
|
try:
|
||||||
|
sys.exit(0)
|
||||||
|
except SystemExit:
|
||||||
|
os._exit(0)
|
14
worker/dockerfile
Normal file
14
worker/dockerfile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
FROM alpine
|
||||||
|
|
||||||
|
RUN apk update && apk upgrade && apk add python3 && apk add -U tzdata
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY requirements.txt requirements.txt
|
||||||
|
ADD app.py /app
|
||||||
|
RUN python3 -m venv .venv
|
||||||
|
RUN /app/.venv/bin/pip3 install -r /app/requirements.txt
|
||||||
|
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
CMD [".venv/bin/python3", "app.py"]
|
8
worker/requirements.txt
Normal file
8
worker/requirements.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
dnspython==2.3.0
|
||||||
|
pika==1.3.1
|
||||||
|
pymongo==4.3.3
|
||||||
|
certifi==2022.12.7
|
||||||
|
charset-normalizer==3.1.0
|
||||||
|
idna==3.4
|
||||||
|
requests==2.28.2
|
||||||
|
urllib3==1.26.15
|
Loading…
x
Reference in New Issue
Block a user