.venv/
+__pycache__/
+dialer.egg-info/
secrets.json
--- /dev/null
+all : .venv/touchfile
+ .venv/bin/python -m flask --app dialer run
+
+.venv/touchfile : requirements.txt
+ python -m venv .venv
+ .venv/bin/python -m pip install -r requirements.txt
+ touch .venv/touchfile
--- /dev/null
+#!.venv/bin/python
+
+from werkzeug.security import generate_password_hash, check_password_hash
+from flask import Flask, request, render_template, jsonify
+from requests.auth import HTTPBasicAuth as requests_auth
+from panoramisk.call_manager import CallManager
+from flask_socketio import SocketIO, emit
+from flask_httpauth import HTTPBasicAuth
+from panoramisk import Manager
+import threading
+import requests
+import asyncio
+import json
+import os
+
+from websockets.sync.client import connect
+
+app = Flask(__name__)
+auth = HTTPBasicAuth()
+socketio = SocketIO(app)
+
+
+
+# ==============================================================================
+# SETUP
+
+# get the player data ready
+with open(f"{app.root_path}/players.json", "r", encoding="utf-8") as f:
+ players = json.loads(f.read())
+
+# get the secrets ready
+with open(f"{app.root_path}/secrets.json", "r", encoding="utf-8") as f:
+ users_raw = json.loads(f.read())
+users = {k: generate_password_hash(v) for (k, v) in users_raw.items()}
+
+@auth.verify_password
+def verify_password(username, password):
+ if username in users and \
+ check_password_hash(users.get(username), password):
+ return username
+
+
+
+# ==============================================================================
+# FRONTEND
+
+@app.route("/", methods=["GET", "POST"])
+@auth.login_required
+def main():
+ data = {
+ "players": players,
+ "sounds": [x.rsplit(".", maxsplit=1)[0] for x in os.listdir(f"{app.root_path}/sounds")],
+ "admin": "+447594768180"
+ }
+
+ return render_template("panel.html", data=data)
+
+
+
+# ==============================================================================
+# UTILS
+
+async def get_channel(number):
+ manager = Manager.from_config(f"{app.root_path}/config.ini")
+ await manager.connect()
+
+ callers = await manager.send_action({
+ 'Action': 'ConfbridgeList',
+ 'Conference': 'blood',
+ })
+
+ channel = None
+
+ if type(callers) == list:
+ for message in callers:
+ channel_num = message.get("CallerIDNum") #"Channel"
+ if channel_num == number:
+ channel = message.channel
+
+ manager.close()
+
+ return channel
+
+
+
+# ==============================================================================
+# DIALPLAN
+
+@app.route("/call", methods=["GET", "POST"])
+@auth.login_required
+async def call():
+ number = request.json["number"]
+
+ manager = Manager.from_config(f"{app.root_path}/config.ini")
+ await manager.connect()
+
+ phonecall = await manager.send_action({
+ "Action": "Originate",
+ "Channel": f"PJSIP/{number}@gotrunk",
+ "Exten": "blood",
+ "Context": "from-internal",
+ "Priority": "1",
+ # "Application": <value>,
+ # "Data": <value>,
+ # "Timeout": 10,
+ "CallerID": "Departed",
+ "Variable": f"CALLERID(num)={number}",
+ # "Account": <value>,
+ # "EarlyMedia": <value>,
+ "Async": "true",
+ # "Codecs": <value>,
+ # "ChannelId": <value>,
+ # "OtherChannelId": <value>,
+ # "PreDialGoSub": <value>
+ })
+
+ manager.close()
+
+ return "", 200
+
+
+@app.route("/kick", methods=["GET", "POST"])
+@auth.login_required
+async def kick():
+ number = request.json["number"]
+
+ if number == "participants":
+ channel = number
+ else:
+ channel = await get_channel(number)
+
+ if channel is None:
+ return f"Caller is not connected ({number})", 422
+
+ manager = Manager.from_config(f"{app.root_path}/config.ini")
+ await manager.connect()
+
+ result = await manager.send_action({
+ "Action": "ConfbridgeKick",
+ "Conference": "blood",
+ "Channel": channel
+ })
+
+ manager.close()
+ return "", 200
+
+
+@app.route("/mute", methods=["GET", "POST"])
+@auth.login_required
+async def mute():
+ number = request.json["number"]
+
+ if number == "participants":
+ channel = number
+ else:
+ channel = await get_channel(number)
+
+ if channel is None:
+ return f"Caller is not connected ({number})", 422
+
+ manager = Manager.from_config(f"{app.root_path}/config.ini")
+ await manager.connect()
+
+ result = await manager.send_action({
+ "Action": "ConfbridgeMute",
+ "Conference": "blood",
+ "Channel": channel
+ })
+
+ manager.close()
+ return "", 200
+
+
+@app.route("/unmute", methods=["GET", "POST"])
+@auth.login_required
+async def unmute():
+ number = request.json["number"]
+
+ if number == "participants":
+ channel = number
+ else:
+ channel = await get_channel(number)
+
+ if channel is None:
+ return "Caller is not connected", 200
+
+ manager = Manager.from_config(f"{app.root_path}/config.ini")
+ await manager.connect()
+
+ result = await manager.send_action({
+ "Action": "ConfbridgeUnmute",
+ "Conference": "blood",
+ "Channel": channel
+ })
+
+ manager.close()
+ return "", 200
+
+
+@app.route("/play", methods=["GET", "POST"])
+@auth.login_required
+async def play():
+ path = request.json["path"]
+
+ manager = Manager.from_config(f"{app.root_path}/config.ini")
+ await manager.connect()
+
+ phonecall = await manager.send_action({
+ "Action": "Originate",
+ "Channel": "Local/bloodadmin@from-internal",
+ "Application": "Playback",
+ "Data": f"/home/will/{path}",
+ "Async": "true",
+ "CallerID": f"file_{path}"
+ })
+
+ manager.close()
+
+ return "", 200
+
+
+@app.route("/stopall")
+@auth.login_required
+async def stopall():
+ manager = Manager.from_config(f"{app.root_path}/config.ini")
+ await manager.connect()
+
+ callers = await manager.send_action({
+ 'Action': 'ConfbridgeList',
+ 'Conference': 'blood',
+ })
+
+ if type(callers) == list:
+ for message in callers:
+ if message.CallerIDName[:4] == "file":
+ result = await manager.send_action({
+ "Action": "ConfbridgeKick",
+ "Conference": "blood",
+ "Channel": message.channel
+ })
+
+ manager.close()
+ return "", 200
+
+
+
+# ==============================================================================
+# EVENTS
+
+watcher = Manager.from_config(f"{app.root_path}/config.ini")
+
+def start_watch():
+ loop = asyncio.new_event_loop()
+ watcher.loop = loop
+
+ print(" * Event watcher started")
+ watcher.connect(run_forever=True)
+
+
+def send_emit(data):
+ with app.test_request_context('/'):
+ emit("status", data, json=True, broadcast=True, namespace="/")
+
+
+@watcher.register_event('DialState') # Register all events
+async def dialstate_callback(manager, message):
+ send_emit({
+ "number": message.DestCallerIDNum,
+ "status": "Ringing",
+ "mute": True
+ })
+
+
+@watcher.register_event('ConfbridgeJoin') # Register all events
+async def confbridgejoin_callback(manager, message):
+ if message.CallerIDName[:4] == "file":
+ number = message.CallerIDName[5:]
+ else:
+ number = message.CallerIDNum
+
+ send_emit({
+ "number": number,
+ "status": "Connected",
+ "mute": message.Muted == "yes"
+ })
+
+
+@watcher.register_event('ConfbridgeLeave') # Register all events
+async def confbridgeleave_callback(manager, message):
+ if message.CallerIDName[:4] == "file":
+ number = message.CallerIDName[5:]
+ else:
+ number = message.CallerIDNum
+
+ send_emit({
+ "number": number,
+ "status": "Disconnected",
+ "mute": True
+ })
+
+
+@watcher.register_event('ConfbridgeMute') # Register all events
+async def confbridgemute_callback(manager, message):
+ send_emit({
+ "number": message.CallerIDNum,
+ "status": "Connected",
+ "mute": True
+ })
+
+
+@watcher.register_event('ConfbridgeUnmute') # Register all events
+async def confbridgeunmute_callback(manager, message):
+ send_emit({
+ "number": message.CallerIDNum,
+ "status": "Connected",
+ "mute": False
+ })
+
+
+
+# ==============================================================================
+
+if __name__ == "__main__":
+ # start the event watch manager
+ thread = threading.Thread(target=start_watch, daemon=True)
+ thread.start()
+
+ socketio.run(app, host='127.0.0.1', port=8000, debug=True)
+++ /dev/null
-#!.venv/bin/python
-
-from werkzeug.security import generate_password_hash, check_password_hash
-from flask import Flask, request, render_template, jsonify
-from requests.auth import HTTPBasicAuth as requests_auth
-from panoramisk.call_manager import CallManager
-from flask_socketio import SocketIO, emit
-from flask_httpauth import HTTPBasicAuth
-from panoramisk import Manager
-import threading
-import requests
-import asyncio
-import json
-import os
-
-from websockets.sync.client import connect
-
-app = Flask(__name__)
-auth = HTTPBasicAuth()
-socketio = SocketIO(app)
-
-
-
-# ==============================================================================
-# SETUP
-
-# get the player data ready
-with open(f"{app.root_path}/players.json", "r", encoding="utf-8") as f:
- players = json.loads(f.read())
-
-# get the secrets ready
-with open(f"{app.root_path}/secrets.json", "r", encoding="utf-8") as f:
- users_raw = json.loads(f.read())
-users = {k: generate_password_hash(v) for (k, v) in users_raw.items()}
-basic = requests_auth('controller', users_raw['controller'])
-
-@auth.verify_password
-def verify_password(username, password):
- if username in users and \
- check_password_hash(users.get(username), password):
- return username
-
-
-
-# ==============================================================================
-# FRONTEND
-
-@app.route("/", methods=["GET", "POST"])
-@auth.login_required
-def main():
- data = {
- "players": players,
- "sounds": [x.rsplit(".", maxsplit=1)[0] for x in os.listdir(f"{app.root_path}/sounds")],
- "admin": "+447594768180"
- }
-
- return render_template("panel.html", data=data)
-
-
-
-# ==============================================================================
-# UTILS
-
-async def get_channel(number):
- manager = Manager.from_config(f"{app.root_path}/config.ini")
- await manager.connect()
-
- callers = await manager.send_action({
- 'Action': 'ConfbridgeList',
- 'Conference': 'blood',
- })
-
- channel = None
-
- if type(callers) == list:
- for message in callers:
- channel_num = message.get("CallerIDNum") #"Channel"
- if channel_num == number:
- channel = message.channel
-
- manager.close()
-
- return channel
-
-
-
-# ==============================================================================
-# DIALPLAN
-
-@app.route("/call", methods=["GET", "POST"])
-@auth.login_required
-async def call():
- number = request.json["number"]
-
- manager = Manager.from_config(f"{app.root_path}/config.ini")
- await manager.connect()
-
- phonecall = await manager.send_action({
- "Action": "Originate",
- "Channel": f"PJSIP/{number}@gotrunk",
- "Exten": "blood",
- "Context": "from-internal",
- "Priority": "1",
- # "Application": <value>,
- # "Data": <value>,
- # "Timeout": 10,
- "CallerID": "Departed",
- "Variable": f"CALLERID(num)={number}",
- # "Account": <value>,
- # "EarlyMedia": <value>,
- "Async": "true",
- # "Codecs": <value>,
- # "ChannelId": <value>,
- # "OtherChannelId": <value>,
- # "PreDialGoSub": <value>
- })
-
- manager.close()
-
- return "", 200
-
-
-@app.route("/kick", methods=["GET", "POST"])
-@auth.login_required
-async def kick():
- number = request.json["number"]
-
- if number == "participants":
- channel = number
- else:
- channel = await get_channel(number)
-
- if channel is None:
- return f"Caller is not connected ({number})", 422
-
- manager = Manager.from_config(f"{app.root_path}/config.ini")
- await manager.connect()
-
- result = await manager.send_action({
- "Action": "ConfbridgeKick",
- "Conference": "blood",
- "Channel": channel
- })
-
- manager.close()
- return "", 200
-
-
-@app.route("/mute", methods=["GET", "POST"])
-@auth.login_required
-async def mute():
- number = request.json["number"]
-
- if number == "participants":
- channel = number
- else:
- channel = await get_channel(number)
-
- if channel is None:
- return f"Caller is not connected ({number})", 422
-
- manager = Manager.from_config(f"{app.root_path}/config.ini")
- await manager.connect()
-
- result = await manager.send_action({
- "Action": "ConfbridgeMute",
- "Conference": "blood",
- "Channel": channel
- })
-
- manager.close()
- return "", 200
-
-
-@app.route("/unmute", methods=["GET", "POST"])
-@auth.login_required
-async def unmute():
- number = request.json["number"]
-
- if number == "participants":
- channel = number
- else:
- channel = await get_channel(number)
-
- if channel is None:
- return "Caller is not connected", 200
-
- manager = Manager.from_config(f"{app.root_path}/config.ini")
- await manager.connect()
-
- result = await manager.send_action({
- "Action": "ConfbridgeUnmute",
- "Conference": "blood",
- "Channel": channel
- })
-
- manager.close()
- return "", 200
-
-
-@app.route("/play", methods=["GET", "POST"])
-@auth.login_required
-async def play():
- path = request.json["path"]
-
- manager = Manager.from_config(f"{app.root_path}/config.ini")
- await manager.connect()
-
- phonecall = await manager.send_action({
- "Action": "Originate",
- "Channel": "Local/bloodadmin@from-internal",
- "Application": "Playback",
- "Data": f"/home/will/{path}",
- "Async": "true",
- "CallerID": f"file_{path}"
- })
-
- manager.close()
-
- return "", 200
-
-
-@app.route("/stopall")
-@auth.login_required
-async def stopall():
- manager = Manager.from_config(f"{app.root_path}/config.ini")
- await manager.connect()
-
- callers = await manager.send_action({
- 'Action': 'ConfbridgeList',
- 'Conference': 'blood',
- })
-
- if type(callers) == list:
- for message in callers:
- if message.CallerIDName[:4] == "file":
- result = await manager.send_action({
- "Action": "ConfbridgeKick",
- "Conference": "blood",
- "Channel": message.channel
- })
-
- manager.close()
- return "", 200
-
-
-
-# ==============================================================================
-# EVENTS
-
-watcher = Manager.from_config(f"{app.root_path}/config.ini")
-
-def start_watch():
- loop = asyncio.new_event_loop()
- watcher.loop = loop
-
- print(" * Event watcher started")
- watcher.connect(run_forever=True)
-
-
-def send_emit(data):
- with app.test_request_context('/'):
- emit("status", data, json=True, broadcast=True, namespace="/")
-
-
-@watcher.register_event('DialState') # Register all events
-async def dialstate_callback(manager, message):
- send_emit({
- "number": message.DestCallerIDNum,
- "status": "Ringing",
- "mute": True
- })
-
-
-@watcher.register_event('ConfbridgeJoin') # Register all events
-async def confbridgejoin_callback(manager, message):
- if message.CallerIDName[:4] == "file":
- number = message.CallerIDName[5:]
- else:
- number = message.CallerIDNum
-
- send_emit({
- "number": number,
- "status": "Connected",
- "mute": message.Muted == "yes"
- })
-
-
-@watcher.register_event('ConfbridgeLeave') # Register all events
-async def confbridgeleave_callback(manager, message):
- if message.CallerIDName[:4] == "file":
- number = message.CallerIDName[5:]
- else:
- number = message.CallerIDNum
-
- send_emit({
- "number": number,
- "status": "Disconnected",
- "mute": True
- })
-
-
-@watcher.register_event('ConfbridgeMute') # Register all events
-async def confbridgemute_callback(manager, message):
- send_emit({
- "number": message.CallerIDNum,
- "status": "Connected",
- "mute": True
- })
-
-
-@watcher.register_event('ConfbridgeUnmute') # Register all events
-async def confbridgeunmute_callback(manager, message):
- send_emit({
- "number": message.CallerIDNum,
- "status": "Connected",
- "mute": False
- })
-
-
-
-# ==============================================================================
-
-if __name__ == "__main__":
- # start the event watch manager
- thread = threading.Thread(target=start_watch, daemon=True)
- thread.start()
-
- socketio.run(app, host='127.0.0.1', port=8000, debug=True)
--- /dev/null
+from distutils.core import setup
+setup(name='dialer',
+ version='1.0',
+ py_modules=['dialer'],
+ )
+++ /dev/null
-import asyncio
-from panoramisk import Manager
-
-async def extension_status():
- manager = Manager(loop=asyncio.get_event_loop(),
- host='127.0.0.1', port=5038,
- username='asterisk', secret='test')
-
- await manager.connect()
- action = {
- 'Action': 'ConfbridgeList',
- 'Conference': 'blood',
- }
-
- extension = await manager.send_action(action)
- print(extension)
- manager.close()
-
-
-def main():
- loop = asyncio.get_event_loop()
- loop.run_until_complete(extension_status())
- loop.close()
-
-
-if __name__ == '__main__':
- main()
-