Commit c796e765 authored by okhin's avatar okhin
Browse files

Everything things is plugged i now, need to do the application work

parent f89712dc
......@@ -4,13 +4,34 @@ import sqlite3
import datetime
import uuid
import json
import logging
import concurrent.futures
import asyncio
from wsgiref.simple_server import make_server
from bottle import route, run, request, abort, install, get, post
from bottle import request, abort, Bottle, ServerAdapter, JSONPlugin
from bottle_sqlite import SQLitePlugin
import jwt
import Asterisk
import websockets
import requests
logging.basicConfig(filename='app.log', level=logging.DEBUG)
app = Bottle(autojson=False)
class PiphoneJSONEncoder(json.JSONEncoder):
def default(self, obj):
We need to implement this to be able to JSONEncode
if isinstance(obj, Call):
return { 'caller': obj.caller
, 'callee': obj.callee
, 'callid':
, 'url': obj.url()
, 'history': obj.history
, 'owner': obj.owner }
return json.JSONEncoder.default(self, o)
def sanitize_phonenumber(number):
......@@ -26,34 +47,29 @@ def sanitize_phonenumber(number):
raise TypeError('{} is not a valid international number, it should start with 00')
return number
class Call(json.JSONEncoder):
class Call(object):
This Class is used to manage operatiosn on a call, to print it and dump it.
history = {}
def __init__(self, caller, callee, owner):
history = []
actions = {'Created': 'call_caller'}
def __init__(self, caller, callee, owner, callid=None, db=None):
self.caller = caller
self.callee = callee
self.owner = owner = uuid.uuid4()
if callid == None: = uuid.uuid4()
else: = callid
self.db = db
except Exception as e:
raise e
def url(self):
return ''.join(['/calls/',])
def default(self, o):
We need to implement this to be able to JSONEncode
return { 'caller': self.caller
, 'callee': self.callee
, 'callid': self.callee
, 'url': self.url()
, 'history': self.history
, 'owner': self.owner }
def load(cls, callid, db):
results = db.execute('SELECT caller, callee, owner, callid, history FROM calls WHERE callid = ?;', (callid,))
......@@ -61,18 +77,47 @@ class Call(json.JSONEncoder):
result = results.fetchone()
object = cls(result[0], result[1], result[2]) = result[3]
object.db = db
object.history = json.loads(result[4])
return object
return None
def save(self, db):
def change(self, new_state):
Let's change the state of the call. new_state is a tuple in the form (newstate, timestamp,)
logging.debug("Got a new state: {}".format(new_state,))
state = new_state[0]
if state in self.actions:
getattr(self, self.actions[state])()
def call_caller(self):
Let's do a request on the ARI system
results = self.db.execute('SELECT login_ari, pass_ari FROM users WHERE api = ?', (self.owner,))
login_ari, token_ari = results.fetchone()
payload = {}
payload['app'] = 'piphone'
payload['api_key'] = ':'.join([login_ari, token_ari])
payload['endpoint'] = 'SIP/' + sanitize_phonenumber(self.caller) + '@forfait-kwaoo'
logging.debug('Preparing to send a request to the ARI with payload {}'.format(payload,))
r ='', data=payload)
logging.debug('Requests sent, got response: {}'.format(r,))
except Exception as e:
raise e
def save(self):
Save the Call to database.
db.execute('''INSERT OR REPLACE INTO calls (caller, callee, owner, callid, history)
self.db.execute('''INSERT OR REPLACE INTO calls (caller, callee, owner, callid, history)
VALUES (?, ?, ?, ?, ?)
''', (self.caller, self.callee, self.owner, self.callid, json.dumps(self.history)))
''', (self.caller, self.callee, self.owner,, json.dumps(self.history)))
# We need a decorator to check if our query is authenticated.
# We will store an API key and SECRET in ur database, the client
......@@ -104,18 +149,105 @@ def authenticated(f):
return f(db, *args, **kwargs)
return wrapped
class Server(ServerAdapter):
This class manage all that is needed to run the REST App of the piphone. It is in charge of
launching an Executor, start the event loop in its own thread and run the bottle app.
server = None
running = False
options = None
callbacks = {}
def __init__(self, debug=False, *args, **kwargs):
We're starting the logger subsytem, create the Thread Pool and stuff
self.logger = logging.getLogger(__name__)
if debug:
self.log_handler = logging.FileHandler('app.log')
self.threads = concurrent.futures.ThreadPoolExecutor()
self.loop = asyncio.get_event_loop()
self.db = sqlite3.connect('call.db')
super(Server, self).__init__(*args, **kwargs)
async def __listen(self):
logger = logging.getLogger('websockets')
async with websockets.connect('ws://') as websocket:
while self.running == True:
logger.debug("Waiting for events")
event = await websocket.recv()
# Let's call the applications function
await self.dispatch(json.loads(event))
# This is now where I want to create callbacks from events
async def dispatch(self, event):
Let's work on our events. Parse them and do request on the ARI API. Event is
a dict loaded from JSON.
self.logger.debug('Event received: {}'.format(event,))
# Let's get the call ID
if 'channel' not in event:
self.logger.debug('Not a channel event, skip')
call = Call.load(event['channel']['id'], self.db)
await call.change((event['channel']['state'], event['channel']['creationtime'],))
def run(self, handler):
We're starting the REST application, and the launch applications
self.running = True
self.server = make_server('', 8080, handler, **self.options)
def stop(self):
self.running = False
def calls(db):
Return the list of calls associated to the connected user.
The call has a status, caller, callee and history (status change+timestamp)
logging.debug("/calls/ called")
results = db.execute('SELECT callid FROM calls WHERE owner = ?;', (request.params['api'],))
calls = []
for call in results.fetchall():
calls.append(Calls.load(call[0], db))
head = {'call': '/calls/', 'user': request.params['api'], 'hits': len(calls)}
calls.append(Call.load(call[0], db))
head = {'call': request.fullpath, 'user': request.params['api'], 'hits': len(calls)}
return {'head': head, 'data': calls}
run(host='localhost', port=8080, debug=True)'/calls/<callid>/caller/<caller>/callee/<callee>/')
def originate(db, callid, caller, callee):
call = Call(caller, callee, request.params['api'], callid, db)
logging.debug("Originate a call: {}".format(json.dumps(call, cls=PiphoneJSONEncoder)))
head = {'call': call.url()
, 'user': request.params['api']
, 'hits': 1}
return {'header': head, 'data': call}
if __name__ == '__main__':
app.install(JSONPlugin(json_dumps=lambda s: json.dumps(s, cls=PiphoneJSONEncoder)))
except Exception as e:
raise e
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment