Skip to content

Commit

Permalink
[CHANGE] Completely updated Examples.rst
Browse files Browse the repository at this point in the history
[FIX] Fixed incorrect import paths in VoIP.rst
[FIX] Fixed warnings in docs/conf.py
  • Loading branch information
tayler6000 committed Jan 10, 2024
1 parent 8c4894b commit 59a7485
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 33 deletions.
78 changes: 51 additions & 27 deletions docs/Examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ Here we will go over a few basic phone setups.
Setup
*****

PyVoIP uses :ref:`VoIPPhone` child class to initiate phone calls. In the example below, our ringing function is named ``Call.ringing``.
PyVoIP uses a :ref:`VoIPPhone` class to receive and initiate phone calls. The settings for our phone are passed via the :ref:`VoIPPhoneParameter` dataclass. When a call is received, a new instance of a :ref:`VoIPCall` is initialized. You can overwrite this class in initialization of VoIPPhone.

We are also importing :ref:`VoIPPhone` and :ref:`InvalidStateError<invalidstateerror>`. VoIPPhone is the main class for our `softphone <https://en.wikipedia.org/wiki/Softphone>`_. An InvalidStateError is thrown when you try to perform an impossible command. For example, denying the call when the phone is already answered, answering when it's already answered, etc.
In this example, we are importing :ref:`CredentialsManager`, :ref:`VoIPPhone`, :ref:`VoIPPhoneParameter`, :ref:`VoIPCall`, and :ref:`InvalidStateError<InvalidStateError>`. :ref:`CredentialsManager` stores and retreives passwords for authentication with registrars. :ref:`VoIPPhone` is the main class for our `softphone <https://en.wikipedia.org/wiki/Softphone>`_. :ref:`VoIPPhoneParameter` is the settings for our :ref:`VoIPPhone`. :ref:`VoIPCall` will be used to create our custom answering class. An :ref:`InvalidStateError<InvalidStateError>` is thrown when you try to perform an impossible command. For example, denying the call when the phone is already answered, answering when it's already answered, etc.

The following will create a phone that answers and automatically hangs up:

.. code-block:: python
from pyVoIP.VoIP import VoIPPhone, VoIPCall, InvalidStateError
from pyVoIP.credentials import CredentialsManager
from pyVoIP.VoIP.call import VoIPCall
from pyVoIP.VoIP.error import InvalidStateError
from pyVoIP.VoIP.phone import VoIPPhone, VoIPPhoneParamter
class Call(VoIPCall):
Expand All @@ -26,19 +29,25 @@ The following will create a phone that answers and automatically hangs up:
pass
if __name__ == "__main__":
phone = VoIPPhone(<SIP server IP>, <SIP server port>, <SIP server username>, <SIP server password>, bind_ip=<Your computer's local IP>, callClass=Call)
cm = CredentialsManager()
cm.add(<SIP server username>, <SIP server password>)
params = VoIPPhoneParamter(<SIP server IP>, <SIP server port>, <SIP server user>, cm, bind_ip=<Your computer's local IP>, call_class=Call)
phone = VoIPPhone(params)
phone.start()
input('Press enter to disable the phone')
phone.stop()
Announcement Board
******************
Let's say you want to make a phone that when you call it, it plays an announcement message, then hangs up. We can accomplish this with the builtin libraries `wave <https://docs.python.org/3/library/wave.html>`_, `audioop <https://docs.python.org/3/library/audioop.html>`_, `time <https://docs.python.org/3/library/time.html>`_, and by importing :ref:`CallState<callstate>`.
Let's say you want to make a phone that when you call it, it plays an announcement message, then hangs up. We can accomplish this with the builtin libraries `wave <https://docs.python.org/3/library/wave.html>`_, `audioop <https://docs.python.org/3/library/audioop.html>`_, `time <https://docs.python.org/3/library/time.html>`_, and by importing :ref:`CallState<callstate>`.
.. code-block:: python
from pyVoIP.VoIP import VoIPPhone, VoIPCall, InvalidStateError, CallState
from pyVoIP.credentials import CredentialsManager
from pyVoIP.VoIP.call import VoIPCall
from pyVoIP.VoIP.error import InvalidStateError
from pyVoIP.VoIP.phone import VoIPPhone, VoIPPhoneParamter
import time
import wave
Expand All @@ -65,12 +74,15 @@ Let's say you want to make a phone that when you call it, it plays an announceme
call.hangup()
if __name__ == "__main__":
phone = VoIPPhone(<SIP Server IP>, <SIP Server Port>, <SIP Server Username>, <SIP Server Password>, bind_ip=<Your computers local IP>, callClass=Call)
cm = CredentialsManager()
cm.add(<SIP server username>, <SIP server password>)
params = VoIPPhoneParamter(<SIP server IP>, <SIP server port>, <SIP server user>, cm, bind_ip=<Your computer's local IP>, call_class=Call)
phone = VoIPPhone(params)
phone.start()
input('Press enter to disable the phone')
phone.stop()
Something important to note is our wait function. We are currently using:
Something important to note is our wait function. We are currently using:
.. code-block:: python
Expand All @@ -79,18 +91,21 @@ Something important to note is our wait function. We are currently using:
while time.time() <= stop and call.state == CallState.ANSWERED:
time.sleep(0.1)
This could be replaced with ``time.sleep(frames / 8000)``. However, doing so will not cause the thread to automatically close if the user hangs up, or if ``VoIPPhone().stop()`` is called; using the while loop method will fix this issue. The ``time.sleep(0.1)`` inside the while loop is also important. Supplementing ``time.sleep(0.1)`` for ``pass`` will cause your CPU to ramp up while running the loop, making the RTP (audio being sent out and received) lag. This can make the voice audibly slow or choppy.
This could be replaced with ``time.sleep(frames / 8000)``. However, doing so will not cause the thread to automatically close if the user hangs up, or if ``VoIPPhone().stop()`` is called. Using the while loop method will fix this issue. The ``time.sleep(0.1)`` inside the while loop is also important. Supplementing ``time.sleep(0.1)`` for ``pass`` will cause your CPU to ramp up while running the loop, making the RTP (audio being sent out and received) lag. This can make the voice audibly slow or choppy.
*Note: Audio must be 8 bit, 8000Hz, and Mono/1 channel. You can accomplish this in a free program called* `Audacity <https://www.audacityteam.org/>`_. *To make an audio recording Mono, go to Tracks > Mix > Mix Stereo Down to Mono. To make an audio recording 8000 Hz, go to Tracks > Resample... and select 8000, then ensure that your 'Project Rate' in the bottom left is also set to 8000. To make an audio recording 8 bit, go to File > Export > Export as WAV, then change 'Save as type:' to 'Other uncompressed files', then set 'Header:' to 'WAV (Microsoft)', then set the 'Encoding:' to 'Unsigned 8-bit PCM'*
**Important Note:** *Audio must be 8 bit, 8000Hz, and Mono/1 channel. You can accomplish this in a free program called* `Audacity <https://www.audacityteam.org/>`_. *To make an audio recording Mono, go to Tracks > Mix > Mix Stereo Down to Mono. To make an audio recording 8000 Hz, go to Tracks > Resample... and select 8000, then ensure that your 'Project Rate' in the bottom left is also set to 8000. To make an audio recording 8 bit, go to File > Export > Export as WAV, then change 'Save as type:' to 'Other uncompressed files', then set 'Header:' to 'WAV (Microsoft)', then set the 'Encoding:' to 'Unsigned 8-bit PCM'*
IVR/Phone Menus
****************
We can use the following code to create `IVR Menus <https://en.wikipedia.org/wiki/Interactive_voice_response>`_. Currently, we cannot make 'breaking' IVR menus. Breaking IVR menus in this context means, a user selecting an option mid-prompt will cancel the prompt, and start the next action. Support for breaking IVR's will be made in the future. For now, here is the code for a non-breaking IVR:
We can use the following code to create `IVR Menus <https://en.wikipedia.org/wiki/Interactive_voice_response>`_. Currently, we cannot make 'breaking' IVR menus. Breaking IVR menus in this context means, a user selecting an option mid-prompt will cancel the prompt, and start the next action. Support for breaking IVR's will be made in the future. For now, here is the code for a non-breaking IVR:
.. code-block:: python
from pyVoIP.VoIP import VoIPPhone, VoIPCall, InvalidStateError, CallState
from pyVoIP.credentials import CredentialsManager
from pyVoIP.VoIP.call import VoIPCall
from pyVoIP.VoIP.error import InvalidStateError
from pyVoIP.VoIP.phone import VoIPPhone, VoIPPhoneParamter
import time
import wave
Expand All @@ -109,56 +124,65 @@ We can use the following code to create `IVR Menus <https://en.wikipedia.org/wik
while call.state == CallState.ANSWERED:
dtmf = call.get_dtmf()
if dtmf == "1":
# Do something
call.hangup()
if call.transfer("sales") # Transfer to same registrar
return
elif dtmf == "2":
# Do something else
call.hangup()
if call.transfer(uri="<100@different_regisrar.com>")
return
time.sleep(0.1)
except InvalidStateError:
pass
except:
call.hangup()
if __name__ == '__main__':
phone = VoIPPhone(<SIP Server IP>, <SIP Server Port>, <SIP Server Username>, <SIP Server Password>, bind_ip=<Your computers local IP>, callClass=Call)
cm = CredentialsManager()
cm.add(<SIP server username>, <SIP server password>)
params = VoIPPhoneParamter(<SIP server IP>, <SIP server port>, <SIP server user>, cm, bind_ip=<Your computer's local IP>, call_class=Call)
phone = VoIPPhone(params)
phone.start()
input('Press enter to disable the phone')
phone.stop()
Please note that ``get_dtmf()`` is actually ``get_dtmf(length=1)``, and as it is technically an ``io.StringBuffer()``, it will return ``""`` instead of ``None``. This may be important if you wanted an 'if anything else, do that' clause. Lastly, VoIPCall stores all DTMF keys pressed since the call was established; meaning, users can press any key they want before the prompt even finishes, or may press a wrong key before the prompt even starts.
Please note that ``get_dtmf()`` is actually ``get_dtmf(length=1)``, and as it is technically an ``io.StringBuffer()``, it will return ``""`` instead of ``None``. This may be important if you wanted an 'if anything else, do that' clause. Lastly, VoIPCall stores all DTMF keys pressed since the call was established; meaning, users can press any key they want before the prompt even finishes, or may press a wrong key before the prompt even starts.
Call state handling for outgoing calls
**************************************
Call State Handling
*******************
We can use the following code to handle various states for the outgoing calls:
We can use the following code to handle various states for calls:
.. code-block:: python
from pyVoIP.VoIP import VoIPPhone, VoIPCall, InvalidStateError, CallState
from pyVoIP.credentials import CredentialsManager
from pyVoIP.VoIP.call import VoIPCall
from pyVoIP.VoIP.error import InvalidStateError
from pyVoIP.VoIP.phone import VoIPPhone, VoIPPhoneParamter
import time
import wave
class Call(VoIPCall):
def progress(self, request):
print('Progress')
super().progress(request)
print('Progress')
super().progress(request)
def busy(self, request):
print('Call ended - callee is busy')
super().progress(request)
super().busy(request)
def answered(self, request):
print('Answered')
print('Answered')
super().answered()
def bye(self):
print('Bye')
super().bye()
if __name__ == '__main__':
phone = VoIPPhone(<SIP Server IP>, <SIP Server Port>, <SIP Server Username>, <SIP Server Password>, bind_ip=<Your computers local IP>, callClass=Call)
cm = CredentialsManager()
cm.add(<SIP server username>, <SIP server password>)
params = VoIPPhoneParamter(<SIP server IP>, <SIP server port>, <SIP server user>, cm, bind_ip=<Your computer's local IP>, call_class=Call)
phone = VoIPPhone(params)
phone.start()
phone.call(<Phone Number>)
input('Press enter to disable the phone\n')
Expand Down
10 changes: 5 additions & 5 deletions docs/VoIP.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Enums

.. _callstate:

*enum* pyVoIP.VoIP.\ **CallState**
*enum* pyVoIP.VoIP.call.\ **CallState**
CallState is an Enum with six attributes.

CallState.\ **DIALING**
Expand Down Expand Up @@ -55,7 +55,7 @@ Enums

.. _PhoneStatus:

*enum* pyVoIP.VoIP.\ **PhoneStatus**
*enum* pyVoIP.VoIP.status.\ **PhoneStatus**
PhoneStatus is an Enum with five attributes.

PhoneStatus.\ **INACTIVE**
Expand Down Expand Up @@ -83,7 +83,7 @@ VoIPCall

The VoIPCall class is used to represent a single VoIP session, which may be to multiple :term:`clients<client>`.

*class* pyVoIP.VoIP.\ **VoIPCall**\ (phone: :ref:`VoIPPhone`, callstate: :ref:`CallState <CallState>`, request: :ref:`SIPMessage`, session_id: int, bind_ip: str, conn: :ref:`VoIPConnection`, ms: Optional[Dict[int, :ref:`PayloadType<payload-type>`]] = None, sendmode="sendonly")
*class* pyVoIP.VoIP.call.\ **VoIPCall**\ (phone: :ref:`VoIPPhone`, callstate: :ref:`CallState <CallState>`, request: :ref:`SIPMessage`, session_id: int, bind_ip: str, conn: :ref:`VoIPConnection`, ms: Optional[Dict[int, :ref:`PayloadType<payload-type>`]] = None, sendmode="sendonly")
The *phone* argument is the initating instance of :ref:`VoIPPhone`.

The *callstate* arguement is the initiating :ref:`CallState<callstate>`.
Expand Down Expand Up @@ -141,7 +141,7 @@ The VoIPCall class is used to represent a single VoIP session, which may be to m
VoIPPhoneParameter
==================

*class* pyVoIP.VoIP.\ **VoIPPhoneParameter**\ (server: str, port: int, user: str, credentials_manager: Optional[:ref:`CredentialsManager`], bind_ip="0.0.0.0", bind_port=5060, bind_network="0.0.0.0/0", hostname: Optional[str] = None, remote_hostname: Optional[str] = None, transport_mode=\ :ref:`TransportMode<TransportMode>`.UDP, cert_file: Optional[str] = None, key_file: Optional[str] = None, key_password: :ref:`KEY_PASSWORD<KEY_PASSWORD>` = None, rtp_port_low=10000, rtp_port_high=20000, call_class: Type[VoIPCall] = None, sip_class: Type[SIP.SIPClient] = None)
*class* pyVoIP.VoIP.phone.\ **VoIPPhoneParameter**\ (server: str, port: int, user: str, credentials_manager: Optional[:ref:`CredentialsManager`], bind_ip="0.0.0.0", bind_port=5060, bind_network="0.0.0.0/0", hostname: Optional[str] = None, remote_hostname: Optional[str] = None, transport_mode=\ :ref:`TransportMode<TransportMode>`.UDP, cert_file: Optional[str] = None, key_file: Optional[str] = None, key_password: :ref:`KEY_PASSWORD<KEY_PASSWORD>` = None, rtp_port_low=10000, rtp_port_high=20000, call_class: Type[VoIPCall] = None, sip_class: Type[SIP.SIPClient] = None)
The *server* argument is your PBX/VoIP server's IP.

The *port* argument is your PBX/VoIP server's port.
Expand Down Expand Up @@ -177,7 +177,7 @@ VoIPPhone

The VoIPPhone class is used to manage the :ref:`SIPClient` class and create :ref:`VoIPCall`'s when there is an incoming call or a :term:`user` makes a call. It then uses the VoIPCall class to handle the call's states.

*class* pyVoIP.VoIP.\ **VoIPPhone**\ (voip_phone_parameter: :ref:`VoIPPhoneParameter`)
*class* pyVoIP.VoIP.phone.\ **VoIPPhone**\ (voip_phone_parameter: :ref:`VoIPPhoneParameter`)
**get_status**\ () -> :ref:`PhoneStatus <PhoneStatus>`
This method returns the phone's current status.

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
html_static_path = [""]

0 comments on commit 59a7485

Please sign in to comment.