Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
La Quadrature du Net
piphone
piphone-sip
Commits
696f9e55
Commit
696f9e55
authored
Apr 27, 2016
by
okhin
Browse files
Everything is working, but the redirct
parent
c187a7b6
Changes
1
Hide whitespace changes
Inline
Side-by-side
app.py
View file @
696f9e55
...
@@ -8,6 +8,7 @@ import logging
...
@@ -8,6 +8,7 @@ import logging
import
concurrent.futures
import
concurrent.futures
import
asyncio
import
asyncio
from
wsgiref.simple_server
import
make_server
from
wsgiref.simple_server
import
make_server
from
operator
import
itemgetter
from
bottle
import
request
,
abort
,
Bottle
,
ServerAdapter
,
JSONPlugin
from
bottle
import
request
,
abort
,
Bottle
,
ServerAdapter
,
JSONPlugin
from
bottle_sqlite
import
SQLitePlugin
from
bottle_sqlite
import
SQLitePlugin
...
@@ -53,7 +54,9 @@ class Call(object):
...
@@ -53,7 +54,9 @@ class Call(object):
"""
"""
history
=
[]
history
=
[]
actions
=
{
'Created'
:
'call_caller'
actions
=
{
'Created'
:
'call_caller'
,
'ChannelStateChange'
:
'change'
}
,
'ChannelStateChange'
:
'change'
,
'ChannelDtmfReceived'
:
'dtmf'
}
def
__init__
(
self
,
caller
,
callee
,
owner
,
callid
=
None
,
db
=
None
):
def
__init__
(
self
,
caller
,
callee
,
owner
,
callid
=
None
,
db
=
None
):
try
:
try
:
self
.
caller
=
caller
self
.
caller
=
caller
...
@@ -72,6 +75,10 @@ class Call(object):
...
@@ -72,6 +75,10 @@ class Call(object):
def
url
(
self
):
def
url
(
self
):
return
''
.
join
([
'/calls/'
,
self
.
id
])
return
''
.
join
([
'/calls/'
,
self
.
id
])
def
state
(
self
):
sort
=
sorted
(
self
.
history
,
reverse
=
True
,
key
=
itemgetter
(
1
))
return
sort
[
0
][
0
]
@
classmethod
@
classmethod
def
load
(
cls
,
callid
,
db
):
def
load
(
cls
,
callid
,
db
):
logging
.
debug
(
"Loading call {}"
.
format
(
callid
,))
logging
.
debug
(
"Loading call {}"
.
format
(
callid
,))
...
@@ -103,11 +110,57 @@ class Call(object):
...
@@ -103,11 +110,57 @@ class Call(object):
if
state
in
self
.
actions
:
if
state
in
self
.
actions
:
getattr
(
self
,
self
.
actions
[
state
])(
event
=
event
)
getattr
(
self
,
self
.
actions
[
state
])(
event
=
event
)
def
dtmf
(
self
,
event
):
'''
We received a DTMF sequence
'''
try
:
assert
self
.
state
()
==
'Up'
# The only thing we want to do is to call the callee if we press 1
if
event
[
'digit'
]
==
'1'
:
# Now, we're connectig the other side
# But first we should stop the playback
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
])
logging
.
debug
(
'Stopping the playback currently running'
.
format
(
payload
,))
# We're starting a playback, with the same ID as the channel, to keep track of it
r
=
requests
.
delete
(
'http://185.34.33.12:8088/ari/playbacks/{0}'
.
format
(
self
.
id
),
data
=
payload
)
logging
.
info
(
'Will now connect {} to {}'
.
format
(
self
.
caller
,
self
.
callee
,))
payload
=
{}
payload
[
'app'
]
=
'piphone'
payload
[
'api_key'
]
=
':'
.
join
([
login_ari
,
token_ari
])
payload
[
'endpoint'
]
=
'SIP/forfait-kwaoo/'
+
sanitize_phonenumber
(
self
.
callee
)
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/channels/{}/redirect'
.
format
(
self
.
id
),
data
=
payload
)
except
AssertionError
as
e
:
logging
.
debug
(
"Received a DTMF sequence out le being in a '{}' state, ignoring: {}"
.
format
(
self
.
state
(),
event
[
'digit'
]))
def
change
(
self
,
event
):
def
change
(
self
,
event
):
'''
'''
Let's change the state of the call
Let's change the state of the call
'''
'''
self
.
update
((
event
[
'channel'
][
'state'
],
event
[
'timestamp'
],))
self
.
update
((
event
[
'channel'
][
'state'
],
event
[
'timestamp'
],))
logging
.
info
(
"New state for call {}: {}"
.
format
(
event
[
'channel'
][
'id'
],
event
[
'channel'
][
'state'
]))
# We now need to take action according to our new state
if
event
[
'channel'
][
'state'
]
==
'Up'
:
# Call is being picked up, we want to play a song
payload
=
{}
try
:
results
=
self
.
db
.
execute
(
'SELECT login_ari, pass_ari FROM users WHERE api = ?'
,
(
self
.
owner
,))
login_ari
,
token_ari
=
results
.
fetchone
()
payload
[
'app'
]
=
'piphone'
payload
[
'api_key'
]
=
':'
.
join
([
login_ari
,
token_ari
])
payload
[
'media'
]
=
'sound:mario'
payload
[
'lang'
]
=
'en_US'
logging
.
debug
(
'Preparing to send a request to the ARI with payload {}'
.
format
(
payload
,))
# We're starting a playback, with the same ID as the channel, to keep track of it
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/channels/{0}/play/{0}'
.
format
(
self
.
id
),
data
=
payload
)
logging
.
debug
(
"Now playing a sound on channel {}"
.
format
(
self
.
id
))
except
Exception
as
e
:
logging
.
debug
(
"Exception raised in change(): {} - event was {}"
.
format
(
e
,
event
,))
raise
e
def
call_caller
(
self
,
event
):
def
call_caller
(
self
,
event
):
'''
'''
...
@@ -125,6 +178,7 @@ class Call(object):
...
@@ -125,6 +178,7 @@ class Call(object):
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/channels/{}'
.
format
(
self
.
id
),
data
=
payload
)
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/channels/{}'
.
format
(
self
.
id
),
data
=
payload
)
logging
.
debug
(
'Requests sent, got response: {}'
.
format
(
r
,))
logging
.
debug
(
'Requests sent, got response: {}'
.
format
(
r
,))
except
Exception
as
e
:
except
Exception
as
e
:
logging
.
debug
(
"Exception raised in call_caller(): {}"
.
format
(
e
,))
raise
e
raise
e
def
save
(
self
):
def
save
(
self
):
...
@@ -141,19 +195,24 @@ class Call(object):
...
@@ -141,19 +195,24 @@ class Call(object):
logging
.
debug
(
"Got Exception: {}"
.
format
(
e
,))
logging
.
debug
(
"Got Exception: {}"
.
format
(
e
,))
raise
e
raise
e
# We need a decorator to check if our query is authenticated.
# We will store an API key and SECRET in ur database, the client
# needs to have both of them.
# He must then send us a JWT token with an API claim in the payload.
# The JWT token must be encoded and signed with the SECRET. If the
# token is bad, we return a 403.
def
authenticated
(
f
):
def
authenticated
(
f
):
'''
We need a decorator to check if our query is authenticated.
We will store an API key and SECRET in ur database, the client
needs to have both of them.
He must then send us a JWT token with an API claim in the payload.
The JWT token must be encoded and signed with the SECRET. If the
token is bad, we return a 403.
'''
def
wrapped
(
db
,
*
args
,
**
kwargs
):
def
wrapped
(
db
,
*
args
,
**
kwargs
):
# Let's get the JWT token. It should be a params (from get or post or whatev')
# Let's get the JWT token. It should be a params (from get or post or whatev')
logging
.
debug
(
"Authetication: {}"
.
format
([
':'
.
join
([
key
,
request
.
params
[
key
]])
for
key
in
request
.
params
],))
if
'token'
not
in
request
.
params
:
if
'token'
not
in
request
.
params
:
logging
.
error
(
"No token found in the params"
)
abort
(
401
,
"No token found in the query"
)
abort
(
401
,
"No token found in the query"
)
# We want the api id in the params to.
# We want the api id in the params to.
if
'api'
not
in
request
.
params
:
if
'api'
not
in
request
.
params
:
logging
.
error
(
"No api id found in the params"
)
abort
(
401
,
"No api id found in the params"
)
abort
(
401
,
"No api id found in the params"
)
# Now, let's get the token on our side
# Now, let's get the token on our side
try
:
try
:
...
@@ -165,6 +224,8 @@ def authenticated(f):
...
@@ -165,6 +224,8 @@ def authenticated(f):
for
key
in
auth_token
:
for
key
in
auth_token
:
request
.
params
[
key
]
=
auth_token
[
key
]
request
.
params
[
key
]
=
auth_token
[
key
]
except
(
jwt
.
exceptions
.
InvalidTokenError
,
AssertionError
)
as
e
:
except
(
jwt
.
exceptions
.
InvalidTokenError
,
AssertionError
)
as
e
:
logging
.
error
(
"Access refused"
)
logging
.
debug
(
"InvalidTokenError: {}"
.
format
(
e
))
abort
(
403
,
e
)
abort
(
403
,
e
)
except
Exception
as
e
:
except
Exception
as
e
:
abort
(
500
,
e
)
abort
(
500
,
e
)
...
@@ -185,11 +246,10 @@ class Server(ServerAdapter):
...
@@ -185,11 +246,10 @@ class Server(ServerAdapter):
"""
"""
We're starting the logger subsytem, create the Thread Pool and stuff
We're starting the logger subsytem, create the Thread Pool and stuff
"""
"""
self
.
logger
=
logging
.
getLogger
(
__name__
)
self
.
logger
=
logging
.
getLogger
(
'server'
)
if
debug
:
if
debug
:
self
.
logger
.
setLevel
(
logging
.
DEBUG
)
self
.
logger
.
setLevel
(
logging
.
DEBUG
)
self
.
log_handler
=
logging
.
FileHandler
(
'app.log'
)
self
.
logger
.
addHandler
(
logging
.
FileHandler
(
'app.log'
))
self
.
logger
.
addHandler
(
self
.
log_handler
)
self
.
threads
=
concurrent
.
futures
.
ThreadPoolExecutor
()
self
.
threads
=
concurrent
.
futures
.
ThreadPoolExecutor
()
self
.
loop
=
asyncio
.
get_event_loop
()
self
.
loop
=
asyncio
.
get_event_loop
()
self
.
db
=
sqlite3
.
connect
(
'call.db'
)
self
.
db
=
sqlite3
.
connect
(
'call.db'
)
...
@@ -197,12 +257,12 @@ class Server(ServerAdapter):
...
@@ -197,12 +257,12 @@ class Server(ServerAdapter):
super
(
Server
,
self
).
__init__
(
*
args
,
**
kwargs
)
super
(
Server
,
self
).
__init__
(
*
args
,
**
kwargs
)
async
def
__listen
(
self
):
async
def
__listen
(
self
):
logger
=
logging
.
getLogger
(
'websockets'
)
ws_
logger
=
logging
.
getLogger
(
'websockets'
)
logger
.
setLevel
(
logging
.
DEBUG
)
ws_
logger
.
setLevel
(
logging
.
DEBUG
)
logger
.
addHandler
(
self
.
log_handler
)
ws_
logger
.
addHandler
(
logging
.
FileHandler
(
'app.log'
)
)
async
with
websockets
.
connect
(
'ws://185.34.33.12:8088/ari/events?app=piphone&api_key=piphone:passpiphone'
)
as
websocket
:
async
with
websockets
.
connect
(
'ws://185.34.33.12:8088/ari/events?app=piphone&api_key=piphone:passpiphone'
)
as
websocket
:
while
self
.
running
==
True
:
while
self
.
running
==
True
:
logger
.
debug
(
"Waiting for events"
)
ws_
logger
.
info
(
"Waiting for events
on websocket
"
)
event
=
await
websocket
.
recv
()
event
=
await
websocket
.
recv
()
# Let's call the applications function
# Let's call the applications function
await
self
.
dispatch
(
json
.
loads
(
event
))
await
self
.
dispatch
(
json
.
loads
(
event
))
...
@@ -269,7 +329,7 @@ def calls(db, callid=None):
...
@@ -269,7 +329,7 @@ def calls(db, callid=None):
head
=
{
'call'
:
call
.
url
(),
'user'
:
request
.
params
[
'api'
],
'hits'
:
1
}
head
=
{
'call'
:
call
.
url
(),
'user'
:
request
.
params
[
'api'
],
'hits'
:
1
}
return
{
'head'
:
head
,
'data'
:
call
}
return
{
'head'
:
head
,
'data'
:
call
}
except
AssertionError
as
e
:
except
AssertionError
as
e
:
logging
.
debg
(
"Not exactly one results found, this is an issue"
)
logging
.
deb
u
g
(
"Not exactly one results found, this is an issue"
)
logging
.
error
(
"Unauthorized access to call {} from user {}"
.
format
(
callid
,
request
.
params
[
'api'
]))
logging
.
error
(
"Unauthorized access to call {} from user {}"
.
format
(
callid
,
request
.
params
[
'api'
]))
abort
(
403
,
"You do not have the authorization to get this call"
)
abort
(
403
,
"You do not have the authorization to get this call"
)
except
Exception
as
e
:
except
Exception
as
e
:
...
@@ -305,7 +365,6 @@ if __name__ == '__main__':
...
@@ -305,7 +365,6 @@ if __name__ == '__main__':
try
:
try
:
app
.
run
(
server
=
server
)
app
.
run
(
server
=
server
)
except
Exception
as
e
:
except
Exception
as
e
:
print
(
e
)
logging
.
error
(
e
)
logging
.
error
(
e
)
server
.
stop
()
server
.
stop
()
raise
e
raise
e
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment