From 59a74854675a802376402b9d6d96dce1eee3a444 Mon Sep 17 00:00:00 2001 From: TJ Porter Date: Wed, 10 Jan 2024 13:18:45 -0600 Subject: [PATCH] [CHANGE] Completely updated Examples.rst [FIX] Fixed incorrect import paths in VoIP.rst [FIX] Fixed warnings in docs/conf.py --- docs/Examples.rst | 78 +++++++++++++++++++++++++++++++---------------- docs/VoIP.rst | 10 +++--- docs/conf.py | 2 +- 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/docs/Examples.rst b/docs/Examples.rst index f39b7d6..b63acf5 100644 --- a/docs/Examples.rst +++ b/docs/Examples.rst @@ -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`. VoIPPhone is the main class for our `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`. :ref:`CredentialsManager` stores and retreives passwords for authentication with registrars. :ref:`VoIPPhone` is the main class for our `softphone `_. :ref:`VoIPPhoneParameter` is the settings for our :ref:`VoIPPhone`. :ref:`VoIPCall` will be used to create our custom answering class. An :ref:`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): @@ -26,7 +29,10 @@ The following will create a phone that answers and automatically hangs up: pass if __name__ == "__main__": - phone = VoIPPhone(, , , , bind_ip=, callClass=Call) + cm = CredentialsManager() + cm.add(, ) + params = VoIPPhoneParamter(, , , cm, bind_ip=, call_class=Call) + phone = VoIPPhone(params) phone.start() input('Press enter to disable the phone') phone.stop() @@ -34,11 +40,14 @@ The following will create a phone that answers and automatically hangs up: 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 `_, `audioop `_, `time `_, and by importing :ref:`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 `_, `audioop `_, `time `_, and by importing :ref:`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 @@ -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(, , , , bind_ip=, callClass=Call) + cm = CredentialsManager() + cm.add(, ) + params = VoIPPhoneParamter(, , , cm, bind_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 @@ -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 `_. *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 `_. *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 `_. 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 `_. 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 @@ -109,11 +124,11 @@ We can use the following code to create `IVR Menus , , , , bind_ip=, callClass=Call) + cm = CredentialsManager() + cm.add(, ) + params = VoIPPhoneParamter(, , , cm, bind_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): @@ -158,7 +179,10 @@ We can use the following code to handle various states for the outgoing calls: super().bye() if __name__ == '__main__': - phone = VoIPPhone(, , , , bind_ip=, callClass=Call) + cm = CredentialsManager() + cm.add(, ) + params = VoIPPhoneParamter(, , , cm, bind_ip=, call_class=Call) + phone = VoIPPhone(params) phone.start() phone.call() input('Press enter to disable the phone\n') diff --git a/docs/VoIP.rst b/docs/VoIP.rst index 2aefd2d..9374adf 100644 --- a/docs/VoIP.rst +++ b/docs/VoIP.rst @@ -24,7 +24,7 @@ Enums .. _callstate: -*enum* pyVoIP.VoIP.\ **CallState** +*enum* pyVoIP.VoIP.call.\ **CallState** CallState is an Enum with six attributes. CallState.\ **DIALING** @@ -55,7 +55,7 @@ Enums .. _PhoneStatus: -*enum* pyVoIP.VoIP.\ **PhoneStatus** +*enum* pyVoIP.VoIP.status.\ **PhoneStatus** PhoneStatus is an Enum with five attributes. PhoneStatus.\ **INACTIVE** @@ -83,7 +83,7 @@ VoIPCall The VoIPCall class is used to represent a single VoIP session, which may be to multiple :term:`clients`. -*class* pyVoIP.VoIP.\ **VoIPCall**\ (phone: :ref:`VoIPPhone`, callstate: :ref:`CallState `, request: :ref:`SIPMessage`, session_id: int, bind_ip: str, conn: :ref:`VoIPConnection`, ms: Optional[Dict[int, :ref:`PayloadType`]] = None, sendmode="sendonly") +*class* pyVoIP.VoIP.call.\ **VoIPCall**\ (phone: :ref:`VoIPPhone`, callstate: :ref:`CallState `, request: :ref:`SIPMessage`, session_id: int, bind_ip: str, conn: :ref:`VoIPConnection`, ms: Optional[Dict[int, :ref:`PayloadType`]] = None, sendmode="sendonly") The *phone* argument is the initating instance of :ref:`VoIPPhone`. The *callstate* arguement is the initiating :ref:`CallState`. @@ -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`.UDP, cert_file: Optional[str] = None, key_file: Optional[str] = None, key_password: :ref:`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`.UDP, cert_file: Optional[str] = None, key_file: Optional[str] = None, key_password: :ref:`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. @@ -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 ` This method returns the phone's current status. diff --git a/docs/conf.py b/docs/conf.py index 07b45d7..b4cc551 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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 = [""]