Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
piphone-sip
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
4
Issues
4
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
La Quadrature du Net
piphone
piphone-sip
Commits
f1b7815a
Commit
f1b7815a
authored
Apr 27, 2016
by
okhin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Let's try to play with bridges and understand how they work
parent
696f9e55
Pipeline
#22
skipped
Changes
1
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
86 additions
and
42 deletions
+86
-42
app.py
app.py
+86
-42
No files found.
app.py
View file @
f1b7815a
...
...
@@ -7,6 +7,7 @@ import json
import
logging
import
concurrent.futures
import
asyncio
import
re
from
wsgiref.simple_server
import
make_server
from
operator
import
itemgetter
...
...
@@ -65,11 +66,11 @@ class Call(object):
if
callid
==
None
:
self
.
id
=
str
(
uuid
.
uuid4
())
self
.
db
=
db
self
.
event_handler
({
'type'
:
'Created'
,
'timestamp'
:
datetime
.
datetime
.
now
().
isoformat
()})
self
.
event_handler
({
'type'
:
'Created'
,
'timestamp'
:
datetime
.
datetime
.
now
().
isoformat
()
,
'channel'
:
{
'id'
:
'Init'
}
})
else
:
self
.
id
=
callid
except
Exception
as
e
:
logging
.
debug
(
"Exception catched: {}"
.
format
(
e
,)
)
logging
.
exception
(
e
)
raise
e
def
url
(
self
):
...
...
@@ -91,7 +92,7 @@ class Call(object):
object
.
db
=
db
return
object
except
Exception
as
e
:
logging
.
debug
(
"Exception catched: {}"
.
format
(
e
,)
)
logging
.
exception
(
e
)
raise
e
def
update
(
self
,
new_state
):
...
...
@@ -115,36 +116,49 @@ class Call(object):
We received a DTMF sequence
'''
try
:
assert
self
.
state
()
==
'Up'
assert
self
.
state
()
.
startswith
(
'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
)
if
event
[
'digit'
]
!=
'1'
:
return
# 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 stoping the 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
(
event
[
'channel'
][
'id'
]),
data
=
payload
)
# Next we need to originate a call to the other side
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/'
+
sanitize_phonenumber
(
self
.
callee
)
+
'@forfait-kwaoo'
logging
.
debug
(
'Preparing to send a request to the ARI with payload {}'
.
format
(
payload
,))
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/channels/{}-{}'
.
format
(
self
.
id
,
sanitize_phonenumber
(
self
.
callee
),),
data
=
payload
)
# And now, just need to add the channel to the bridge
payload
=
{}
payload
[
'app'
]
=
'piphone'
payload
[
'api_key'
]
=
':'
.
join
([
login_ari
,
token_ari
])
payload
[
'channel'
]
=
'{}-{}'
.
format
(
self
.
id
,
sanitize_phonenumber
(
self
.
callee
),)
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/bridges/{}/addChannel'
.
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'
]))
logging
.
error
(
"Received a DTMF sequence out le being in a '{}' state, ignoring: {}"
.
format
(
self
.
state
(),
event
[
'digit'
]))
def
change
(
self
,
event
):
'''
Let's change the state of the call
'''
self
.
update
((
event
[
'channel'
][
'state'
]
,
event
[
'timestamp'
],))
self
.
update
((
':'
.
join
([
event
[
'channel'
][
'state'
],
event
[
'channel'
][
'id'
].
split
(
'-'
)[
-
1
]])
,
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'
:
# Are we the caller orthe callee?
if
event
[
'channel'
][
'id'
].
endswith
(
self
.
callee
):
# Callee side, let's just move
return
# Call is being picked up, we want to play a song
payload
=
{}
try
:
...
...
@@ -156,29 +170,58 @@ class Call(object):
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
)
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/channels/{0}/play/{0}'
.
format
(
event
[
'channel'
][
'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
,)
)
logging
.
exception
(
e
)
raise
e
def
call_caller
(
self
,
event
):
'''
Let's do a request on the ARI system
We want to call the caller. Which needs to be done via a bridge.
Fisrt we set-up a bridge, then we originate a channel, postfixing the bridge id
with the caller number.
And then we connect the caller to the bridge.
The bridge is a mixed one.
'''
self
.
update
((
event
[
'type'
],
event
[
'timestamp'
],))
self
.
update
((
':'
.
join
([
event
[
'type'
],
event
[
'channel'
][
'id'
].
split
(
'-'
)[
-
1
]]),
event
[
'timestamp'
],))
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
])
# Let's establish a bridge over the Kwai River
try
:
payload
[
'type'
]
=
'mixing'
logging
.
debug
(
"Initiating a bridge for call {}"
.
format
(
self
.
id
,))
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/bridges/{}'
.
format
(
self
.
id
,),
data
=
payload
)
except
Exception
as
e
:
logging
.
exception
(
e
)
raise
e
# Now, let's create the channel
payload
=
{}
payload
[
'app'
]
=
'piphone'
payload
[
'api_key'
]
=
':'
.
join
([
login_ari
,
token_ari
])
try
:
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
=
requests
.
post
(
'http://185.34.33.12:8088/ari/channels/{}'
.
format
(
self
.
id
),
data
=
payload
)
logging
.
debug
(
'Requests sent, got response: {}'
.
format
(
r
,))
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/channels/{}-{}'
.
format
(
self
.
id
,
sanitize_phonenumber
(
self
.
caller
),),
data
=
payload
)
except
Exception
as
e
:
logging
.
debug
(
"Exception raised in call_caller(): {}"
.
format
(
e
,))
logging
.
exception
(
e
)
raise
e
# Let's connect the channel to the bridge.
payload
=
{}
payload
[
'app'
]
=
'piphone'
payload
[
'api_key'
]
=
':'
.
join
([
login_ari
,
token_ari
])
try
:
payload
[
'channel'
]
=
'{}-{}'
.
format
(
self
.
id
,
sanitize_phonenumber
(
self
.
caller
),)
logging
.
debug
(
"Connection the channel to the bridge for call {}"
.
format
(
self
.
id
,))
r
=
requests
.
post
(
'http://185.34.33.12:8088/ari/bridges/{}/addChannel'
.
format
(
self
.
id
,),
data
=
payload
)
except
Exception
as
e
:
logging
.
exception
(
e
)
raise
e
def
save
(
self
):
...
...
@@ -192,7 +235,7 @@ class Call(object):
,
(
self
.
caller
,
self
.
callee
,
self
.
owner
,
self
.
id
,
json
.
dumps
(
self
.
history
)))
self
.
db
.
commit
()
except
Exception
as
e
:
logging
.
debug
(
"Got Exception: {}"
.
format
(
e
,)
)
logging
.
exception
(
e
)
raise
e
def
authenticated
(
f
):
...
...
@@ -225,7 +268,7 @@ def authenticated(f):
request
.
params
[
key
]
=
auth_token
[
key
]
except
(
jwt
.
exceptions
.
InvalidTokenError
,
AssertionError
)
as
e
:
logging
.
error
(
"Access refused"
)
logging
.
debug
(
"InvalidTokenError: {}"
.
format
(
e
)
)
logging
.
exception
(
e
)
abort
(
403
,
e
)
except
Exception
as
e
:
abort
(
500
,
e
)
...
...
@@ -274,11 +317,12 @@ class Server(ServerAdapter):
a dict loaded from JSON.
"""
self
.
logger
.
debug
(
'Event received: {}'
.
format
(
event
,))
# Let's get the call ID
# Let's get the call ID
, the call id isthe channel id minus the last -part.
if
'channel'
not
in
event
:
self
.
logger
.
debug
(
'Not a channel event, skip'
)
return
call
=
Call
.
load
(
event
[
'channel'
][
'id'
],
self
.
db
)
call_id
=
re
.
sub
(
'-\d+$'
,
''
,
event
[
'channel'
][
'id'
])
call
=
Call
.
load
(
call_id
,
self
.
db
)
call
.
event_handler
(
event
)
def
run
(
self
,
handler
):
...
...
@@ -316,7 +360,7 @@ def calls(db, callid=None):
head
=
{
'call'
:
request
.
fullpath
,
'user'
:
request
.
params
[
'api'
],
'hits'
:
len
(
calls
)}
return
{
'head'
:
head
,
'data'
:
calls
}
except
Exception
as
e
:
logging
.
e
rror
(
"Got exception: {}"
.
format
(
e
,)
)
logging
.
e
xception
(
e
)
abort
(
500
,
"Exception"
)
# We first need to check if we can access the callid we asked for
try
:
...
...
@@ -354,7 +398,7 @@ def originate(db, callid=None):
return
{
'header'
:
head
,
'data'
:
call
}
except
Exception
as
e
:
logging
.
debug
(
"Missing params : {}"
.
format
([
p
for
p
in
request
.
params
],))
logging
.
debug
(
"Exception catched: {}"
.
format
(
e
,)
)
logging
.
exception
(
e
)
abort
(
400
,
"Missing or incorrect fields, the call cannot be processed"
)
if
__name__
==
'__main__'
:
...
...
@@ -365,6 +409,6 @@ if __name__ == '__main__':
try
:
app
.
run
(
server
=
server
)
except
Exception
as
e
:
logging
.
e
rror
(
e
)
logging
.
e
xception
(
e
)
server
.
stop
()
raise
e
Write
Preview
Markdown
is supported
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