commit b78685db0b6e72b17468264d597e4a35967e57dc Author: root Date: Tue Jun 27 14:47:56 2023 +0700 init project diff --git a/.env b/.env new file mode 100644 index 0000000..e1a0162 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +RABBITMQ_DEFAULT_USER = rabbit +RABBITMQ_DEFAULT_PASS = PYIjVMuouv2fYQzg7ZzPcTdGcAmCRVWa \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba32daf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +**/.venv \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..228702e --- /dev/null +++ b/docker-compose.yaml @@ -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: + + diff --git a/rabbitmq/dockerfile b/rabbitmq/dockerfile new file mode 100644 index 0000000..e69de29 diff --git a/reciever/app.py b/reciever/app.py new file mode 100644 index 0000000..fd5792f --- /dev/null +++ b/reciever/app.py @@ -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) diff --git a/reciever/dockerfile b/reciever/dockerfile new file mode 100644 index 0000000..1f7b546 --- /dev/null +++ b/reciever/dockerfile @@ -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"] \ No newline at end of file diff --git a/reciever/phone.code-workspace b/reciever/phone.code-workspace new file mode 100644 index 0000000..bab1b7f --- /dev/null +++ b/reciever/phone.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": ".." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/reciever/requirements.txt b/reciever/requirements.txt new file mode 100644 index 0000000..3b9bd74 --- /dev/null +++ b/reciever/requirements.txt @@ -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 \ No newline at end of file diff --git a/web/app.py b/web/app.py new file mode 100644 index 0000000..3d0e437 --- /dev/null +++ b/web/app.py @@ -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/") +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/") +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) diff --git a/web/dockerfile b/web/dockerfile new file mode 100644 index 0000000..431797f --- /dev/null +++ b/web/dockerfile @@ -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"] \ No newline at end of file diff --git a/web/requirements.txt b/web/requirements.txt new file mode 100644 index 0000000..65581cf --- /dev/null +++ b/web/requirements.txt @@ -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 diff --git a/web/static/main.css b/web/static/main.css new file mode 100644 index 0000000..2e8a484 --- /dev/null +++ b/web/static/main.css @@ -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; +} \ No newline at end of file diff --git a/web/templates/DeleteCall.html b/web/templates/DeleteCall.html new file mode 100644 index 0000000..760664f --- /dev/null +++ b/web/templates/DeleteCall.html @@ -0,0 +1,22 @@ + + + + + + + + Удаление документа + + + +

Вы действительно хотите удалить номер: {{ id }}?

+ Причина удаления? +
+ + + +
+ + + + \ No newline at end of file diff --git a/web/templates/TestCall.html b/web/templates/TestCall.html new file mode 100644 index 0000000..021dbf5 --- /dev/null +++ b/web/templates/TestCall.html @@ -0,0 +1,43 @@ + + + + + + + + + Lost Calls + +
+ Входящий вызов принят + Входящий вызов не принят + Перезвонили + +
+ + + + + + + + + + {% for entry in call %} + + + + + + + + + {% endfor %} + +
Номер клиентаДата времяСсылка на запись
{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}{{ entry.time }} + {% if entry.recordUrl|length > 1 %} + + {% endif %} +
+ + \ No newline at end of file diff --git a/web/templates/WebCall.html b/web/templates/WebCall.html new file mode 100644 index 0000000..6d8e05c --- /dev/null +++ b/web/templates/WebCall.html @@ -0,0 +1,47 @@ + + + + + + + + + Lost Calls + +
+ Входящий вызов принят + Входящий вызов не принят + Перезвонили + +
+ + + + + + + + + + {% for entry in call %} + + + {% if entry.important == True %} + + {% else %} + + {% endif %} + + + + + + {% endfor %} + +
Номер клиентаДата времяСсылка на запись
{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}{{ entry.time }} + {% if entry.recordUrl|length > 1 %} + + {% endif %} +
+ + \ No newline at end of file diff --git a/web_admin/app.py b/web_admin/app.py new file mode 100644 index 0000000..1626a3a --- /dev/null +++ b/web_admin/app.py @@ -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/") +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/") +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/") +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) diff --git a/web_admin/dockerfile b/web_admin/dockerfile new file mode 100644 index 0000000..431797f --- /dev/null +++ b/web_admin/dockerfile @@ -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"] \ No newline at end of file diff --git a/web_admin/requirements.txt b/web_admin/requirements.txt new file mode 100644 index 0000000..65581cf --- /dev/null +++ b/web_admin/requirements.txt @@ -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 diff --git a/web_admin/static/main.css b/web_admin/static/main.css new file mode 100644 index 0000000..2e8a484 --- /dev/null +++ b/web_admin/static/main.css @@ -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; +} \ No newline at end of file diff --git a/web_admin/templates/DeleteCall.html b/web_admin/templates/DeleteCall.html new file mode 100644 index 0000000..1bd9d31 --- /dev/null +++ b/web_admin/templates/DeleteCall.html @@ -0,0 +1,23 @@ + + + + + + + + Удаление документа + + + +

Вы действительно хотите удалить номер: {{ id }}?

+ Причина: +
+ + +
+ +
+ + + + \ No newline at end of file diff --git a/web_admin/templates/TestCall.html b/web_admin/templates/TestCall.html new file mode 100644 index 0000000..021dbf5 --- /dev/null +++ b/web_admin/templates/TestCall.html @@ -0,0 +1,43 @@ + + + + + + + + + Lost Calls + +
+ Входящий вызов принят + Входящий вызов не принят + Перезвонили + +
+ + + + + + + + + + {% for entry in call %} + + + + + + + + + {% endfor %} + +
Номер клиентаДата времяСсылка на запись
{{ "%s %s %s %s"|format(entry.client[0:1], entry.client[1:4], entry.client[4:7], entry.client[7:11]) }}{{ entry.time }} + {% if entry.recordUrl|length > 1 %} + + {% endif %} +
+ + \ No newline at end of file diff --git a/web_admin/templates/WebCall.html b/web_admin/templates/WebCall.html new file mode 100644 index 0000000..b462625 --- /dev/null +++ b/web_admin/templates/WebCall.html @@ -0,0 +1,64 @@ + + + + + + + + + Lost Calls + +
+ Входящий вызов принят + Входящий вызов не принят + Перезвонили + Удаленные + + +
+ + + + + + + + + + {% for entry in call %} + + + {% if entry.important == True %} + + {% else %} + + {% endif %} + + + + + + + {% endfor %} + +
Номер клиентаДата времяДополнитительная информация
+ {{ "%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 %} + [удалить] + {% endif %} + {{ "%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 %} + [удалить] + {% endif %} + {{ entry.time }} + {% if entry.recordUrl|length > 1 %} + + {% endif %} + + {% if entry.reason|length > 1 %} + {{ entry.reason }} + {% endif %} +
+ + \ No newline at end of file diff --git a/worker/app.py b/worker/app.py new file mode 100644 index 0000000..881e393 --- /dev/null +++ b/worker/app.py @@ -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) diff --git a/worker/dockerfile b/worker/dockerfile new file mode 100644 index 0000000..1f7b546 --- /dev/null +++ b/worker/dockerfile @@ -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"] \ No newline at end of file diff --git a/worker/requirements.txt b/worker/requirements.txt new file mode 100644 index 0000000..e575685 --- /dev/null +++ b/worker/requirements.txt @@ -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 \ No newline at end of file