├── call-recording ├── __init__.py ├── requirements.txt ├── config.ini ├── Logger.py ├── App.py ├── ConfigurationManager.py ├── Readme.mD ├── data │ └── Recordings API.postman_collection.json └── Controller │ └── RecordingsController.py ├── jobrouter-quickstart ├── .gitignore ├── readme.md └── router-quickstart.py ├── send-email-advanced ├── send-email-attachments │ ├── attachment.txt │ ├── attachment.pdf │ └── send-email-attachments.py ├── send-email-inline-attachments │ ├── inline-attachment.jpg │ ├── inline-attachment.png │ └── send-email-inline-attachments.py ├── send-email-multiple-recipients │ └── send-email-multiple-recipients.py ├── send-email-continuation-token │ └── send-email-continuation-token.py └── README.md ├── callautomation-outboundcalling ├── requirements.txt ├── data │ └── OutboundCallDesign.png ├── template │ └── index.html ├── readme.md └── main.py ├── callautomation-openai-sample ├── requirements.txt └── readme.md ├── callautomation-live-transcription ├── requirements.txt ├── template │ └── index.html ├── transcriptionDataHandler.py └── readme.md ├── callautomation-connect-room ├── data │ ├── roomId.png │ ├── tokens.png │ ├── connectCall.png │ ├── createRoom.png │ ├── joinRoomCall.png │ └── callingRoomQuickstart.png ├── requirements.txt ├── config.py ├── templates │ └── index.html ├── readme.md └── main.py ├── callautomation-azure-openai-voice ├── requirements.txt ├── readme.md ├── main.py └── azureOpenAIService.py ├── CHANGELOG.md ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── send-sms-quickstart ├── send-sms.py └── README.md ├── LICENSE.md ├── direct-routing-quickstart ├── direct_routing_sample.py └── README.md ├── send-email ├── send-email.py └── README .md ├── phone-numbers-quickstart ├── README.md └── phone_numbers_sample.py ├── chat-insights-openai ├── README.md └── chatInsights.py ├── use-managed-Identity ├── managed-identity.py └── README.md ├── manage-teams-identity-mobile-and-desktop ├── exchange-communication-access-tokens.py └── README.md ├── access-tokens-quickstart ├── README.md └── issue-access-tokens.py ├── messages-quickstart ├── get_templates_list.py ├── get_templates_list_async.py ├── send_text_notification_messages.py ├── send_image_notification_messages.py ├── readme.md ├── send_text_notification_messages_async.py ├── send_image_notification_messages_async.py ├── send_template_notification_messages.py ├── send_template_notification_messages_async.py └── send_text_notification_messages_with_token_credentials.py ├── rooms-quickstart ├── README.md └── rooms.py ├── lookup-phone-numbers-quickstart ├── number-lookup-sample.py └── README.md ├── add-chat ├── README.md └── start-chat.py ├── README.md ├── CONTRIBUTING.md └── .gitignore /call-recording/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jobrouter-quickstart/.gitignore: -------------------------------------------------------------------------------- 1 | Lib 2 | Scripts 3 | Include 4 | pyvenv.cfg -------------------------------------------------------------------------------- /send-email-advanced/send-email-attachments/attachment.txt: -------------------------------------------------------------------------------- 1 | Testing of email attachments. -------------------------------------------------------------------------------- /call-recording/requirements.txt: -------------------------------------------------------------------------------- 1 | azure-communication-callautomation 2 | aiohttp 3 | azure-storage-blob 4 | azure-eventgrid 5 | azure-common -------------------------------------------------------------------------------- /callautomation-outboundcalling/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>=2.3.2 2 | azure-eventgrid==4.11.0 3 | azure-communication-callautomation==1.1.0 4 | -------------------------------------------------------------------------------- /callautomation-openai-sample/requirements.txt: -------------------------------------------------------------------------------- 1 | Quart>=0.19.6 2 | azure-eventgrid==4.11.0 3 | azure-communication-callautomation==1.1.0 4 | openai==0.28.1 -------------------------------------------------------------------------------- /callautomation-live-transcription/requirements.txt: -------------------------------------------------------------------------------- 1 | Quart>=0.19.6 2 | azure-eventgrid==4.11.0 3 | aiohttp>= 3.11.9 4 | azure-communication-callautomation==1.4.0 5 | -------------------------------------------------------------------------------- /call-recording/config.ini: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | ACSResourceConnectionString=%ACSResourceConnectionString% 3 | ACSAcquiredPhoneNumber=%ACSAcquiredPhoneNumber% 4 | CallbackUri=%CallbackUri% -------------------------------------------------------------------------------- /callautomation-connect-room/data/roomId.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/callautomation-connect-room/data/roomId.png -------------------------------------------------------------------------------- /callautomation-connect-room/data/tokens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/callautomation-connect-room/data/tokens.png -------------------------------------------------------------------------------- /callautomation-azure-openai-voice/requirements.txt: -------------------------------------------------------------------------------- 1 | Quart>=0.19.6 2 | azure-eventgrid==4.11.0 3 | aiohttp>= 3.11.9 4 | azure-communication-callautomation==1.4.0 5 | openai[realtime] -------------------------------------------------------------------------------- /callautomation-connect-room/data/connectCall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/callautomation-connect-room/data/connectCall.png -------------------------------------------------------------------------------- /callautomation-connect-room/data/createRoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/callautomation-connect-room/data/createRoom.png -------------------------------------------------------------------------------- /callautomation-connect-room/data/joinRoomCall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/callautomation-connect-room/data/joinRoomCall.png -------------------------------------------------------------------------------- /callautomation-connect-room/data/callingRoomQuickstart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/callautomation-connect-room/data/callingRoomQuickstart.png -------------------------------------------------------------------------------- /callautomation-outboundcalling/data/OutboundCallDesign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/callautomation-outboundcalling/data/OutboundCallDesign.png -------------------------------------------------------------------------------- /send-email-advanced/send-email-attachments/attachment.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/send-email-advanced/send-email-attachments/attachment.pdf -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [project-title] Changelog 2 | 3 | 4 | # x.y.z (yyyy-mm-dd) 5 | 6 | *Features* 7 | * ... 8 | 9 | *Bug Fixes* 10 | * ... 11 | 12 | *Breaking Changes* 13 | * ... 14 | -------------------------------------------------------------------------------- /send-email-advanced/send-email-inline-attachments/inline-attachment.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/send-email-advanced/send-email-inline-attachments/inline-attachment.jpg -------------------------------------------------------------------------------- /send-email-advanced/send-email-inline-attachments/inline-attachment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-python-quickstarts/HEAD/send-email-advanced/send-email-inline-attachments/inline-attachment.png -------------------------------------------------------------------------------- /callautomation-connect-room/requirements.txt: -------------------------------------------------------------------------------- 1 | Quart>=0.19.6 2 | python-dotenv==0.21.0 3 | azure-eventgrid==4.11.0 4 | aiohttp>= 3.11.9 5 | azure-communication-callautomation==1.3.0 6 | azure-communication-identity==1.3.1 7 | azure-communication-rooms==1.1.1 8 | 9 | -------------------------------------------------------------------------------- /call-recording/Logger.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | class Logger(enum.Enum): 4 | INFORMATION = 1 5 | ERROR = 2 6 | 7 | @staticmethod 8 | def log_message(message_type: object, message: str): 9 | log_message = message_type.name + " : " + message 10 | print(log_message) -------------------------------------------------------------------------------- /call-recording/App.py: -------------------------------------------------------------------------------- 1 | from Logger import Logger 2 | from Controller.RecordingsController import RecordingsController 3 | 4 | class App(): 5 | 6 | def __init__(): 7 | Logger.log_message(Logger.INFORMATION,"Starting call-recording App... ") 8 | 9 | if __name__ == "__main__": 10 | RecordingsController() -------------------------------------------------------------------------------- /callautomation-connect-room/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | PORT = 8080 3 | CONNECTION_STRING = "" 4 | ACS_RESOURCE_PHONE_NUMBER = "" 5 | TARGET_PHONE_NUMBER = "<+1XXXXXXXXXX>" 6 | CALLBACK_URI = "" 7 | COGNITIVE_SERVICES_ENDPOINT = "" 8 | -------------------------------------------------------------------------------- /callautomation-outboundcalling/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Azure Communication Services Quickstart 5 | 6 | 7 |

Azure Communication Services

8 |

Outbound Calling Quickstart

9 |
10 |
11 | 12 |
13 |
14 | 15 | -------------------------------------------------------------------------------- /callautomation-live-transcription/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Azure Communication Services Quickstart 6 | 7 | 8 | 9 |

Azure Communication Services

10 |

Live Transcription Quickstart

11 |
12 |
13 | 14 |
15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /call-recording/ConfigurationManager.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | 3 | class ConfigurationManager: 4 | __configuration = None 5 | __instance = None 6 | 7 | def __init__(self): 8 | if (self.__configuration == None): 9 | self.__configuration = configparser.ConfigParser() 10 | self.__configuration.read('config.ini') 11 | 12 | @staticmethod 13 | def get_instance(): 14 | if (ConfigurationManager.__instance == None): 15 | ConfigurationManager.__instance = ConfigurationManager() 16 | 17 | return ConfigurationManager.__instance 18 | 19 | def get_app_settings(self, key): 20 | if (key != None): 21 | return self.__configuration.get('DEFAULT', key) 22 | return None -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ### This issue is for a: (mark with an `x`) 8 | ``` 9 | - [ ] bug report -> please search issues before submitting 10 | - [ ] feature request 11 | - [ ] documentation issue or request 12 | - [ ] regression (a behavior that used to work and stopped in a new release) 13 | ``` 14 | 15 | ### Minimal steps to reproduce 16 | > 17 | 18 | ### Any log messages given by the failure 19 | > 20 | 21 | ### Expected/desired behavior 22 | > 23 | 24 | ### OS and Version? 25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 26 | 27 | ### Versions 28 | > 29 | 30 | ### Mention any other details that might be useful 31 | 32 | > --------------------------------------------------------------- 33 | > Thanks! We'll be in touch soon. 34 | -------------------------------------------------------------------------------- /send-sms-quickstart/send-sms.py: -------------------------------------------------------------------------------- 1 | import os 2 | from azure.communication.sms import SmsClient 3 | 4 | try: 5 | # Quickstart code goes here 6 | # Create the SmsClient object which will be used to send SMS messages 7 | sms_client = SmsClient.from_connection_string("") 8 | 9 | ## Send a 1:1 SMS Message 10 | # calling send() with sms values 11 | # sms_responses = sms_client.send( 12 | # from_="", 13 | # to="", 14 | # message="Hello World via SMS", 15 | # enable_delivery_report=True, # optional property 16 | # tag="custom-tag") # optional property 17 | 18 | # Send a 1:N SMS Message 19 | # calling send() with sms values 20 | sms_responses = sms_client.send( 21 | from_="", 22 | to=["", ""], 23 | message="Hello World via SMS Python", 24 | enable_delivery_report=True, # optional property 25 | tag="custom-tag") # optional property 26 | except Exception as ex: 27 | print('Exception:') 28 | print(ex) 29 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | * ... 4 | 5 | ## Does this introduce a breaking change? 6 | 7 | ``` 8 | [ ] Yes 9 | [ ] No 10 | ``` 11 | 12 | ## Pull Request Type 13 | What kind of change does this Pull Request introduce? 14 | 15 | 16 | ``` 17 | [ ] Bugfix 18 | [ ] Feature 19 | [ ] Code style update (formatting, local variables) 20 | [ ] Refactoring (no functional changes, no api changes) 21 | [ ] Documentation content changes 22 | [ ] Other... Please describe: 23 | ``` 24 | 25 | ## How to Test 26 | * Get the code 27 | 28 | ``` 29 | git clone [repo-address] 30 | cd [repo-name] 31 | git checkout [branch-name] 32 | npm install 33 | ``` 34 | 35 | * Test the code 36 | 37 | ``` 38 | ``` 39 | 40 | ## What to Check 41 | Verify that the following are valid 42 | * ... 43 | 44 | ## Other Information 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /direct-routing-quickstart/direct_routing_sample.py: -------------------------------------------------------------------------------- 1 | import os 2 | from azure.communication.phonenumbers.siprouting import SipRoutingClient, SipTrunk, SipTrunkRoute 3 | 4 | # You can find your connection string from your resource in the Azure portal 5 | connection_string = 'endpoint=https://.communication.azure.com/;accesskey=' 6 | try: 7 | print('Azure Communication Services - Direct Routing Quickstart') 8 | sip_routing_client = SipRoutingClient.from_connection_string(connection_string) 9 | 10 | print('Set Trunks') 11 | new_trunks = [SipTrunk(fqdn="sbc.us.contoso.com", sip_signaling_port=1234), SipTrunk(fqdn="sbc.eu.contoso.com", sip_signaling_port=1234)] 12 | sip_routing_client.set_trunks(new_trunks) 13 | 14 | print('Set Routes') 15 | us_route = SipTrunkRoute(name="UsRoute", description="Handle US numbers '+1'", number_pattern="^\\+1(\\d{10})$", trunks=["sbc.us.contoso.com"]) 16 | def_route = SipTrunkRoute(name="DefaultRoute", description="Handle all numbers", number_pattern="^\\+\\d+$", trunks=["sbc.us.contoso.com","sbc.eu.contoso.com"]) 17 | new_routes = [us_route, def_route] 18 | sip_routing_client.set_routes(new_routes) 19 | 20 | print('Finish') 21 | 22 | except Exception as ex: 23 | print('Exception:') 24 | print(ex) 25 | -------------------------------------------------------------------------------- /send-email/send-email.py: -------------------------------------------------------------------------------- 1 | from azure.communication.email import EmailClient 2 | 3 | connection_string = "" 4 | sender_address = "" 5 | recipient_address = "" 6 | 7 | POLLER_WAIT_TIME = 10 8 | 9 | message = { 10 | "senderAddress": sender_address, 11 | "recipients": { 12 | "to": [{"address": recipient_address}], 13 | }, 14 | "content": { 15 | "subject": "Test email from Python Sample", 16 | "plainText": "This is plaintext body of test email.", 17 | "html": "

This is the html body of test email.

", 18 | } 19 | } 20 | 21 | try: 22 | client = EmailClient.from_connection_string(connection_string) 23 | 24 | poller = client.begin_send(message); 25 | 26 | time_elapsed = 0 27 | while not poller.done(): 28 | print("Email send poller status: " + poller.status()) 29 | 30 | poller.wait(POLLER_WAIT_TIME) 31 | time_elapsed += POLLER_WAIT_TIME 32 | 33 | if time_elapsed > 18 * POLLER_WAIT_TIME: 34 | raise RuntimeError("Polling timed out.") 35 | 36 | if poller.result()["status"] == "Succeeded": 37 | print(f"Successfully sent the email (operation id: {poller.result()['id']})") 38 | else: 39 | raise RuntimeError(str(poller.result()["error"])) 40 | 41 | except Exception as ex: 42 | print(ex) 43 | -------------------------------------------------------------------------------- /callautomation-connect-room/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Azure Communication Services Quickstart 6 | 7 | 8 | 9 |

Azure Communication Services

10 |

Connect Rooms Call Quickstart

11 | 12 |
13 |
14 | 15 | 16 | 17 | 18 |
19 | 20 |

Room Details

21 | 22 |
23 |

Room

24 |

Room Id: {{ details.room_id }}

25 |
26 | 27 |
28 |

Presenter

29 |

User Id: {{details.presenter_id}}

30 |

Token: {{ details.presenter_token }}

31 |
32 | 33 |
34 |

Attendee

35 |

User Id: {{ details.attendee_id }}

36 |

Token: {{ details.attendee_token }}

37 |
38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /send-email-advanced/send-email-multiple-recipients/send-email-multiple-recipients.py: -------------------------------------------------------------------------------- 1 | from azure.communication.email import EmailClient 2 | 3 | connection_string = "" 4 | sender_address = "" 5 | recipient_address = "" 6 | replyto_address = "" 7 | 8 | POLLER_WAIT_TIME = 10 9 | 10 | message = { 11 | "senderAddress": sender_address, 12 | "recipients": { 13 | "to": [{ "address": recipient_address }, { "address": recipient_address }], 14 | "cc": [{ "address": recipient_address }], 15 | "bcc": [{ "address": recipient_address }] 16 | }, 17 | "content": { 18 | "subject": "Test email from Python Sample", 19 | "plainText": "This is plaintext body of test email.", 20 | "html": "

This is the html body of test email.

", 21 | }, 22 | "replyTo": [{ "address": replyto_address }] 23 | } 24 | 25 | try: 26 | client = EmailClient.from_connection_string(connection_string) 27 | 28 | poller = client.begin_send(message); 29 | 30 | time_elapsed = 0 31 | while not poller.done(): 32 | print("Email send poller status: " + poller.status()) 33 | 34 | poller.wait(POLLER_WAIT_TIME) 35 | time_elapsed += POLLER_WAIT_TIME 36 | 37 | if time_elapsed > 18 * POLLER_WAIT_TIME: 38 | raise RuntimeError("Polling timed out.") 39 | 40 | if poller.result()["status"] == "Succeeded": 41 | print(f"Successfully sent the email (operation id: {poller.result()['id']})") 42 | else: 43 | raise RuntimeError(str(poller.result()["error"])) 44 | 45 | except Exception as ex: 46 | print(ex) 47 | -------------------------------------------------------------------------------- /phone-numbers-quickstart/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-services 8 | --- 9 | 10 | 11 | # Manage Phone Numbers 12 | 13 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Manage Phone Numbers](https://docs.microsoft.com/azure/communication-services/quickstarts/telephony-sms/get-phone-number?pivots=programming-language-python) 14 | 15 | ## Prerequisites 16 | 17 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 18 | - [Python](https://www.python.org/downloads/) 3.7 or above. 19 | - A deployed Communication Services resource and connection string. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 20 | 21 | ## Code Structure 22 | 23 | - **./phone-numbers-quickstart/phone_numbers_sample.py:** contains code for managing phone numbers. 24 | 25 | ## Install the packages 26 | 27 | pip install azure-communication-phonenumbers 28 | 29 | pip install azure-identity 30 | 31 | ## Before running sample code 32 | 33 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 34 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 35 | 3. With the Communication Services procured in pre-requisites, add connection string in the code. 36 | 37 | ## Run Locally 38 | 39 | From a console prompt, navigate to the directory containing the phone_numbers_sample.py file, then execute the following command to run the app. 40 | 41 | python ./phone_numbers_sample.py 42 | 43 | -------------------------------------------------------------------------------- /chat-insights-openai/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-services 8 | --- 9 | 10 | 11 | # Generate chat insights using Azure OpenAI 12 | 13 | ## Prerequisites 14 | 15 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 16 | - An active Communication Services resource. You will need its connection string and endpoint. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 17 | - Create an Azure OpenAI resource. See [instructions](https://aka.ms/acs-sms-open-ai-create-open).    18 | - Deploy an Azure OpenAI model (Can use GPT-3, ChatGPT or GPT-4 models). See [instructions](https://aka.ms/acs-sms-open-ai-deploy-model).  19 | - [Python](https://www.python.org/downloads/) 3.11.2 or above. 20 | 21 | ## Install the packages 22 | 23 | ```bash 24 | 25 | pip install openai azure.communication.chat azure.communication.identity 26 | 27 | ``` 28 | 29 | ## Before running sample code 30 | 31 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 32 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git`. 33 | 3. With the Communication Services procured in pre-requisites, add connection string, endpoint, Azure OpenAI key and Azure OpenAI endpoint to **chatInsights.py** file. 34 | 35 | ## Run Locally 36 | 37 | From a console prompt, navigate to the directory containing the chatInsights.py file, then execute the following command to run the app. 38 | 39 | ```bash 40 | 41 | python ./chatInsights.py 42 | 43 | ``` -------------------------------------------------------------------------------- /direct-routing-quickstart/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-services 8 | --- 9 | 10 | 11 | # Direct Routing Configuration 12 | 13 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Direct Routing](https://docs.microsoft.com/azure/communication-services/quickstarts/telephony-sms/voice-routing-sdk-config?pivots=programming-language-python) 14 | 15 | ## Prerequisites 16 | 17 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 18 | - [Python](https://www.python.org/downloads/) 3.7 or above. 19 | - A deployed Communication Services resource and connection string. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 20 | 21 | ## Code Structure 22 | 23 | - *./direct-routing-quickstart/direct_routing_sample.py* contains code for Direct Routing configuration. 24 | 25 | ## Install the packages 26 | 27 | pip install azure-communication-phonenumbers==1.1.0b3 28 | 29 | pip install azure-identity 30 | 31 | ## Before running sample code 32 | 33 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 34 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 35 | 3. With the Communication Services procured in pre-requisites, add connection string in the code. 36 | 37 | ## Run Locally 38 | 39 | From a console prompt, navigate to the directory containing the *direct_routing_sample.py* file, then execute the following command to run the app. 40 | 41 | ```console 42 | python ./direct_routing_sample.py 43 | ``` 44 | -------------------------------------------------------------------------------- /use-managed-Identity/managed-identity.py: -------------------------------------------------------------------------------- 1 | from azure.identity import DefaultAzureCredential 2 | from azure.communication.identity import CommunicationIdentityClient 3 | from azure.communication.sms import SmsClient 4 | 5 | credential = DefaultAzureCredential() 6 | # You can find your endpoint and access key from your resource in the Azure portal 7 | # e.g. "https://.communication.azure.com"; 8 | def create_identity_and_get_token(resource_endpoint): 9 | client = CommunicationIdentityClient(resource_endpoint, credential) 10 | 11 | user = client.create_user() 12 | token_response = client.get_token(user, scopes=["voip"]) 13 | 14 | return token_response 15 | 16 | def send_sms(resource_endpoint, from_phone_number, to_phone_number, message_content): 17 | sms_client = SmsClient(resource_endpoint, credential) 18 | 19 | response = sms_client.send( 20 | from_=from_phone_number, 21 | to=[to_phone_number], 22 | message=message_content, 23 | enable_delivery_report=True # optional property 24 | ) 25 | return response 26 | 27 | # You can find your endpoint and access key from your resource in the Azure portal 28 | # e.g. "https://.communication.azure.com"; 29 | endpoint = "https://.communication.azure.com/" 30 | print("Retrieving new Access Token, using Managed Identities") 31 | result = create_identity_and_get_token(endpoint) 32 | print(f'Retrieved Access Token: {result.token}') 33 | 34 | print("Sending SMS using Managed Identities") 35 | # You will need a phone number from your resource to send an SMS. 36 | sms_result = send_sms(endpoint, "", "", "Hello from Managed Identities") 37 | print(f'SMS ID: {sms_result[0].message_id}') 38 | print(f'Send Result Successful: {sms_result[0].successful}') -------------------------------------------------------------------------------- /callautomation-live-transcription/transcriptionDataHandler.py: -------------------------------------------------------------------------------- 1 | import json 2 | from azure.communication.callautomation._shared.models import identifier_from_raw_id 3 | 4 | async def process_websocket_message_async(message): 5 | print("Client connected") 6 | json_object = json.loads(message) 7 | kind = json_object['kind'] 8 | print(kind) 9 | if kind == 'TranscriptionMetadata': 10 | print("Transcription metadata") 11 | print("-------------------------") 12 | print("Subscription ID:", json_object['transcriptionMetadata']['subscriptionId']) 13 | print("Locale:", json_object['transcriptionMetadata']['locale']) 14 | print("Call Connection ID:", json_object['transcriptionMetadata']['callConnectionId']) 15 | print("Correlation ID:", json_object['transcriptionMetadata']['correlationId']) 16 | if kind == 'TranscriptionData': 17 | participant = identifier_from_raw_id(json_object['transcriptionData']['participantRawID']) 18 | word_data_list = json_object['transcriptionData']['words'] 19 | print("Transcription data") 20 | print("-------------------------") 21 | print("Text:", json_object['transcriptionData']['text']) 22 | print("Format:", json_object['transcriptionData']['format']) 23 | print("Confidence:", json_object['transcriptionData']['confidence']) 24 | print("Offset:", json_object['transcriptionData']['offset']) 25 | print("Duration:", json_object['transcriptionData']['duration']) 26 | print("Participant:", participant.raw_id) 27 | print("Result Status:", json_object['transcriptionData']['resultStatus']) 28 | for word in word_data_list: 29 | print("Word:", word['text']) 30 | print("Offset:", word['offset']) 31 | print("Duration:", word['duration']) 32 | -------------------------------------------------------------------------------- /manage-teams-identity-mobile-and-desktop/exchange-communication-access-tokens.py: -------------------------------------------------------------------------------- 1 | import os 2 | from azure.communication.identity import CommunicationIdentityClient, CommunicationUserIdentifier 3 | from msal.application import PublicClientApplication 4 | 5 | try: 6 | print("Azure Communication Services - Access Tokens Quickstart") 7 | # Quickstart code goes here 8 | 9 | # This code demonstrates how to fetch your Azure AD client ID and tenant ID 10 | # from an environment variable. 11 | client_id = os.environ["AAD_CLIENT_ID"] 12 | tenant_id = os.environ["AAD_TENANT_ID"] 13 | authority = "https://login.microsoftonline.com/%s" % tenant_id 14 | 15 | # Create an instance of PublicClientApplication 16 | app = PublicClientApplication(client_id, authority=authority) 17 | 18 | scopes = [ 19 | "https://auth.msft.communication.azure.com/Teams.ManageCalls", 20 | "https://auth.msft.communication.azure.com/Teams.ManageChats" 21 | ] 22 | 23 | # Retrieve the AAD token and object ID of a Teams user 24 | result = app.acquire_token_interactive(scopes) 25 | aad_token = result["access_token"] 26 | user_object_id = result["id_token_claims"]["oid"] 27 | print(f"Teams token:{aad_token}") 28 | 29 | # This code demonstrates how to fetch your connection string 30 | # from an environment variable. 31 | connection_string = os.environ["COMMUNICATION_SERVICES_CONNECTION_STRING"] 32 | 33 | # Instantiate the identity client 34 | client = CommunicationIdentityClient.from_connection_string(connection_string) 35 | 36 | # Exchange the Azure AD access token of the Teams User for a Communication Identity access token 37 | token_result = client.get_token_for_teams_user(aad_token, client_id, user_object_id) 38 | print("Token: " + token_result.token) 39 | 40 | except Exception as ex: 41 | print(f"Exception: {ex}") -------------------------------------------------------------------------------- /send-email-advanced/send-email-continuation-token/send-email-continuation-token.py: -------------------------------------------------------------------------------- 1 | from azure.communication.email import EmailClient 2 | 3 | connection_string = "" 4 | sender_address = "" 5 | recipient_address = "" 6 | 7 | POLLER_WAIT_TIME = 10 8 | MAX_POLLS = 18 9 | 10 | message = { 11 | "senderAddress": sender_address, 12 | "recipients": { 13 | "to": [{"address": recipient_address}], 14 | }, 15 | "content": { 16 | "subject": "Test email from Python Sample", 17 | "plainText": "This is plaintext body of test email.", 18 | "html": "

This is the html body of test email.

", 19 | } 20 | } 21 | 22 | try: 23 | client = EmailClient.from_connection_string(connection_string) 24 | poller = client.begin_send(message); 25 | 26 | # Pauses operation and saves state that can be used later to resume operation 27 | token = poller.continuation_token() 28 | 29 | # Additional processing can be done here between pausing and resuming the operation 30 | 31 | new_client = EmailClient.from_connection_string(connection_string); 32 | new_poller = new_client.begin_send(message, continuation_token=token); 33 | 34 | time_elapsed = 0 35 | while not new_poller.done(): 36 | print("Email send poller status: " + new_poller.status()) 37 | 38 | new_poller.wait(POLLER_WAIT_TIME) 39 | time_elapsed += POLLER_WAIT_TIME 40 | 41 | if time_elapsed > MAX_POLLS * POLLER_WAIT_TIME: 42 | raise RuntimeError("Polling timed out.") 43 | 44 | if new_poller.result()["status"] == "Succeeded": 45 | print(f"Successfully sent the email (operation id: {new_poller.result()['id']})") 46 | else: 47 | raise RuntimeError(str(new_poller.result()["error"])) 48 | 49 | except Exception as ex: 50 | print(ex) 51 | -------------------------------------------------------------------------------- /access-tokens-quickstart/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-services 8 | --- 9 | 10 | 11 | # Create and manage access tokens 12 | 13 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Create and manage access tokens](https://docs.microsoft.com/azure/communication-services/quickstarts/access-tokens?pivots=programming-language-python) 14 | 15 | ## Prerequisites 16 | 17 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 18 | - [Python](https://www.python.org/downloads/) 3.7, or above. 19 | - An active Communication Services resource and connection string.. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 20 | 21 | ## Code Structure 22 | 23 | - **./access-tokens-quickstart/issue-access-tokens.py:** contains code for creating and managing access tokens. 24 | 25 | ## Install the package 26 | pip install azure-communication-identity 27 | 28 | ## Before running sample code 29 | 30 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 31 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 32 | 3. With the Communication Services procured in pre-requisites, add connection string to environment variable using below command 33 | 34 | setx COMMUNICATION_SERVICES_CONNECTION_STRING 35 | 36 | 4. Add pip installation to PATH variables. 37 | 38 | ## Run Locally 39 | 40 | From a console prompt, navigate to the directory containing the issue-access-tokens.py file, then execute the following python command to run the app. 41 | 42 | python ./issue-access-tokens.py 43 | -------------------------------------------------------------------------------- /messages-quickstart/get_templates_list.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: get_templates_list.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates fetching WhatsApp templates created in your WhatsApp Business account. The NotificationMessageClient is 14 | authenticated using a connection string. 15 | USAGE: 16 | python get_templates_list.py 17 | 18 | Set the environment variable with your own value before running the sample: 19 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 20 | 2) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 21 | """ 22 | 23 | import os 24 | import sys 25 | 26 | sys.path.append("..") 27 | 28 | class GetTemplatesSample(object): 29 | 30 | connection_string = os.getenv("COMMUNICATION_SAMPLES_CONNECTION_STRING") 31 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 32 | 33 | def get_templates_list(self): 34 | 35 | from azure.communication.messages import MessageTemplateClient 36 | from azure.communication.messages.models import ( TextNotificationContent ) 37 | 38 | message_template_client = MessageTemplateClient.from_connection_string(self.connection_string) 39 | 40 | # calling send() with whatsapp message details 41 | template_list = message_template_client.list_templates(self.channel_id) 42 | 43 | count_templates = len(list(template_list)) 44 | print("Successfully retrieved {} templates from channel_id {}." 45 | .format(count_templates, self.channel_id)) 46 | 47 | if __name__ == '__main__': 48 | sample = GetTemplatesSample() 49 | sample.get_templates_list() 50 | -------------------------------------------------------------------------------- /jobrouter-quickstart/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | 6 | Products: 7 | - azure 8 | - azure-communication-jobrouter 9 | --- 10 | 11 | # Job Router quick start 12 | 13 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Submit a job for queuing and routing](https://learn.microsoft.com/azure/communication-services/quickstarts/router/get-started-router?pivots=programming-language-python) 14 | 15 | ## Prerequisites 16 | 17 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 18 | - Install [Python](https://www.python.org/downloads/) 3.7 or above. 19 | - Create an Azure Communication Services resource. For details, see [Quickstart: Create and manage Communication Services resources](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource endpoint for this quickstart. 20 | 21 | ## Code Structure 22 | 23 | - **./jobrouter-quickstart/router-quickstart.py:** contains sample code. 24 | 25 | ## Install the packages 26 | 27 | From a console prompt, navigate to the directory containing the router-quickstart.py file, then execute the following command: 28 | 29 | - pip install azure-communication-jobrouter 30 | 31 | ## Before running sample code 32 | 33 | 1. Open an instance of PowerShell/Windows Terminal/Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 34 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 35 | 3. `cd communication-services-python-quickstarts/jobrouter-quickstart` 36 | 4. With the Communication Services procured in pre-requisites, add connection string in **router-quickstart.py** file ```connection_string = ''```. 37 | 38 | ## Run Locally 39 | 40 | From a console prompt, navigate to the directory containing the router-quickstart.py file, then execute the following command to run the app. 41 | 42 | python ./router-quickstart.py 43 | -------------------------------------------------------------------------------- /send-email-advanced/send-email-attachments/send-email-attachments.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from azure.communication.email import EmailClient 3 | 4 | with open("./attachment.pdf", "rb") as file: 5 | pdf_b64encoded = base64.b64encode(file.read()) 6 | 7 | with open("./attachment.txt", "rb") as file: 8 | txt_b64encoded = base64.b64encode(file.read()) 9 | 10 | connection_string = "" 11 | sender_address = "" 12 | recipient_address = "" 13 | 14 | POLLER_WAIT_TIME = 10 15 | 16 | message = { 17 | "senderAddress": sender_address, 18 | "recipients": { 19 | "to": [{ "address": recipient_address }] 20 | }, 21 | "content": { 22 | "subject": "Test email from Python Sample", 23 | "plainText": "This is plaintext body of test email.", 24 | "html": "

This is the html body of test email.

" 25 | }, 26 | "attachments": [ 27 | { 28 | "name": "attachment.pdf", 29 | "contentType": "application/pdf", 30 | "contentInBase64": pdf_b64encoded.decode() 31 | }, 32 | { 33 | "name": "attachment.txt", 34 | "contentType": "text/plain", 35 | "contentInBase64": txt_b64encoded.decode() 36 | } 37 | ] 38 | } 39 | 40 | try: 41 | client = EmailClient.from_connection_string(connection_string) 42 | 43 | poller = client.begin_send(message); 44 | 45 | time_elapsed = 0 46 | while not poller.done(): 47 | print("Email send poller status: " + poller.status()) 48 | 49 | poller.wait(POLLER_WAIT_TIME) 50 | time_elapsed += POLLER_WAIT_TIME 51 | 52 | if time_elapsed > 18 * POLLER_WAIT_TIME: 53 | raise RuntimeError("Polling timed out.") 54 | 55 | if poller.result()["status"] == "Succeeded": 56 | print(f"Successfully sent the email (operation id: {poller.result()['id']})") 57 | else: 58 | raise RuntimeError(str(poller.result()["error"])) 59 | 60 | except Exception as ex: 61 | print(ex) 62 | -------------------------------------------------------------------------------- /rooms-quickstart/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | 6 | Products: 7 | - azure 8 | - azure-communication-rooms 9 | --- 10 | 11 | 12 | # Manage Rooms 13 | 14 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Create a room] 15 | 16 | ## Prerequisites 17 | 18 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 19 | - Install [Python](https://www.python.org/downloads/) 3.7 or above. 20 | - Create an Azure Communication Services resource. For details, see [Quickstart: Create and manage Communication Services resources](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource endpoint for this quickstart. 21 | - Create a [user access token](https://docs.microsoft.com/azure/communication-services/quickstarts/access-tokens?pivots=programming-language-python). Be sure to set the scope to **voip**, and note the **token** string as well as the **userId** string. 22 | 23 | ## Code Structure 24 | 25 | - **./rooms-python-quickstart/rooms.py:** contains code for managing rooms. 26 | 27 | ## Install the packages 28 | 29 | From a console prompt, navigate to the directory containing the rooms.py file, then execute the following command: 30 | 31 | - pip install azure-communication-rooms==1.1.0 32 | - pip install azure-communication-identity 33 | 34 | ## Before running sample code 35 | 36 | 1. Open an instance of PowerShell/Windows Terminal/Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 37 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 38 | 3. With the Communication Services procured in pre-requisites, add connection string in **rooms.py** file at line 14 ```connection_string = ''```. 39 | 40 | ## Run Locally 41 | 42 | From a console prompt, navigate to the directory containing the rooms.py file, then execute the following command to run the app. 43 | 44 | python ./rooms.py 45 | -------------------------------------------------------------------------------- /send-sms-quickstart/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-services 8 | --- 9 | 10 | 11 | # Manage Phone Numbers 12 | 13 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Send an SMS message](https://docs.microsoft.com/azure/communication-services/quickstarts/telephony-sms/send?pivots=programming-language-python) 14 | 15 | ## Prerequisites 16 | 17 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 18 | - [Python](https://www.python.org/downloads/) 3.7 or above. 19 | - An active Communication Services resource and connection string. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 20 | - An SMS enabled telephone number. [Get a phone number](https://docs.microsoft.com/azure/communication-services/quickstarts/telephony-sms/get-phone-number?pivots=programming-language-python). 21 | 22 | ## Code Structure 23 | 24 | - **./send-sms-quickstart/send-sms.py:** contains code for sending message. 25 | 26 | ## Install the packages 27 | 28 | - pip install azure-communication-sms 29 | 30 | ## Before running sample code 31 | 32 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 33 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git`. 34 | 3. With the Communication Services procured in pre-requisites, add connection string to **send-sms.py** file at line no:8 ```sms_client = SmsClient.from_connection_string("")```. 35 | 4. With the SMS enabled telephone number procured in pre-requisites, add it to the **send-sms.py** file. Assign your ACS telephone number and sender numbers at line no 22 & 23. 36 | 37 | 38 | ## Run Locally 39 | 40 | From a console prompt, navigate to the directory containing the send-sms.py file, then execute the following command to run the app. 41 | 42 | python ./send-sms.py 43 | 44 | -------------------------------------------------------------------------------- /lookup-phone-numbers-quickstart/number-lookup-sample.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from azure.communication.phonenumbers import PhoneNumbersClient 4 | 5 | try: 6 | print('Azure Communication Services - Number Lookup Quickstart') 7 | 8 | if len(sys.argv) < 2: 9 | sys.exit("Missing a phone number parameter") 10 | phoneNumber = sys.argv[1] 11 | 12 | # This code retrieves your connection string from an environment variable 13 | connection_string = os.getenv('COMMUNICATION_SERVICES_CONNECTION_STRING') 14 | try: 15 | phone_numbers_client = PhoneNumbersClient.from_connection_string(connection_string) 16 | except Exception as ex: 17 | print('Exception:') 18 | print(ex) 19 | 20 | # Use the free number lookup functionality to get number formatting information 21 | formatting_results = phone_numbers_client.search_operator_information(phoneNumber) 22 | formatting_info = formatting_results.values[0] 23 | print(str.format("{0} is formatted {1} internationally, and {2} nationally", formatting_info.phone_number, formatting_info.international_format, formatting_info.national_format)) 24 | 25 | # Use the paid number lookup functionality to get operator specific details 26 | # IMPORTANT NOTE: Invoking the method below will incur a charge to your account 27 | options = { "include_additional_operator_details": True } 28 | operator_results = phone_numbers_client.search_operator_information([ phoneNumber ], options=options) 29 | operator_information = operator_results.values[0] 30 | 31 | number_type = operator_information.number_type if operator_information.number_type else "unknown" 32 | if operator_information.operator_details is None or operator_information.operator_details.name is None: 33 | operator_name = "an unknown operator" 34 | else: 35 | operator_name = operator_information.operator_details.name 36 | 37 | print(str.format("{0} is a {1} number, operated in {2} by {3}", operator_information.phone_number, number_type, operator_information.iso_country_code, operator_name)) 38 | except Exception as ex: 39 | print('Exception:') 40 | print(ex) 41 | -------------------------------------------------------------------------------- /messages-quickstart/get_templates_list_async.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: get_templates_list_async.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates fetching WhatsApp templates created in your WhatsApp Business account. The NotificationMessageClient is 14 | authenticated using a connection string. 15 | USAGE: 16 | python get_templates_list_async.py 17 | 18 | Set the environment variable with your own value before running the sample: 19 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 20 | 2) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 21 | """ 22 | 23 | import asyncio 24 | import os 25 | import sys 26 | 27 | sys.path.append("..") 28 | 29 | class GetTemplatesSampleAsync(object): 30 | 31 | connection_string = os.getenv("COMMUNICATION_SAMPLES_CONNECTION_STRING") 32 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 33 | 34 | async def get_templates_list_async(self): 35 | 36 | from azure.communication.messages.aio import MessageTemplateClient 37 | 38 | message_template_client = MessageTemplateClient.from_connection_string(self.connection_string) 39 | 40 | # calling send() with whatsapp message details 41 | async with message_template_client: 42 | template_list = message_template_client.list_templates(self.channel_id) 43 | async_list_data = [x async for x in template_list] 44 | count_templates = len(list(async_list_data)) 45 | print("Successfully retrieved {} templates from channel_id {}." 46 | .format(count_templates, self.channel_id)) 47 | 48 | async def main(): 49 | sample = GetTemplatesSampleAsync() 50 | await sample.get_templates_list_async() 51 | 52 | if __name__ == '__main__': 53 | asyncio.run(main()) 54 | -------------------------------------------------------------------------------- /lookup-phone-numbers-quickstart/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-services 8 | --- 9 | 10 | 11 | # Manage Phone Numbers 12 | 13 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Look Up Phone Numbers](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/number-lookup?pivots=programming-language-python) 14 | 15 | ## Prerequisites 16 | 17 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 18 | - [Python](https://www.python.org/downloads/) 3.7 or above. 19 | - A deployed Communication Services resource and connection string. [Create a Communication Services resource](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/create-communication-resource). 20 | 21 | ## Code Structure 22 | 23 | - **./lookup-phone-numbers-quickstart/number-lookup-sample.py:** contains code for looking up phone numbers. 24 | 25 | ## Install the packages 26 | 27 | pip install azure-communication-phonenumbers==1.2.0 28 | 29 | pip install azure-identity 30 | 31 | ## Before running sample code 32 | 33 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 34 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 35 | 3. With the Communication Services procured in pre-requisites, add connection string as an environment variable named `COMMUNICATION_SERVICES_CONNECTION_STRING` 36 | 4. Decide which lookup you would like to perform, and keep in mind that looking up all the operator details incurs a cost, while looking up only number formatting is free. 37 | 38 | > [!WARNING] 39 | > If you want to avoid incurring a charge, comment out lines 27-37 40 | > 41 | ## Run Locally 42 | 43 | From a console prompt, navigate to the directory containing the number-lookup-sample.py file, then execute the following command to run the app, replacing ` 46 | 47 | -------------------------------------------------------------------------------- /send-email-advanced/send-email-inline-attachments/send-email-inline-attachments.py: -------------------------------------------------------------------------------- 1 | import base64 2 | from azure.communication.email import EmailClient 3 | 4 | with open("./inline-attachment.jpg", "rb") as file: 5 | jpg_b64encoded = base64.b64encode(file.read()) 6 | 7 | with open("./inline-attachment.png", "rb") as file: 8 | png_b64encoded = base64.b64encode(file.read()) 9 | 10 | connection_string = "" 11 | sender_address = "" 12 | recipient_address = "" 13 | 14 | POLLER_WAIT_TIME = 10 15 | 16 | message = { 17 | "senderAddress": sender_address, 18 | "recipients": { 19 | "to": [{ "address": recipient_address }] 20 | }, 21 | "content": { 22 | "subject": "Test email from Python Sample", 23 | "plainText": "This is plaintext body of test email.", 24 | "html": "

HTML body inline images:

" 25 | }, 26 | "attachments": [ 27 | { 28 | "name": "inline-attachments.jpg", 29 | "contentId": "kittens-1", 30 | "contentType": "image/jpeg", 31 | "contentInBase64": jpg_b64encoded.decode() 32 | }, 33 | { 34 | "name": "inline-attachments.png", 35 | "contentId": "kittens-2", 36 | "contentType": "image/png", 37 | "contentInBase64": png_b64encoded.decode() 38 | } 39 | ] 40 | } 41 | 42 | try: 43 | client = EmailClient.from_connection_string(connection_string) 44 | 45 | poller = client.begin_send(message); 46 | 47 | time_elapsed = 0 48 | while not poller.done(): 49 | print("Email send poller status: " + poller.status()) 50 | 51 | poller.wait(POLLER_WAIT_TIME) 52 | time_elapsed += POLLER_WAIT_TIME 53 | 54 | if time_elapsed > 18 * POLLER_WAIT_TIME: 55 | raise RuntimeError("Polling timed out.") 56 | 57 | if poller.result()["status"] == "Succeeded": 58 | print(f"Successfully sent the email (operation id: {poller.result()['id']})") 59 | else: 60 | raise RuntimeError(str(poller.result()["error"])) 61 | 62 | except Exception as ex: 63 | print(ex) 64 | -------------------------------------------------------------------------------- /messages-quickstart/send_text_notification_messages.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: send_text_notification_messages.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates sending an Whatsapp message from business phone number to a single user. The NotificationMessageClient is 14 | authenticated using a connection string. 15 | USAGE: 16 | python send_text_notification_messages.py 17 | 18 | Set the environment variable with your own value before running the sample: 19 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 20 | 2) RECIPIENT_PHONE_NUMBER - a phone number with Whatsapp capabilities. Use list for recipient phone number. 21 | 3) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 22 | """ 23 | 24 | import os 25 | import sys 26 | 27 | sys.path.append("..") 28 | 29 | class SendWhatsAppMessageSample(object): 30 | 31 | connection_string = os.getenv("COMMUNICATION_SAMPLES_CONNECTION_STRING") 32 | phone_number = os.getenv("RECIPIENT_PHONE_NUMBER") 33 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 34 | 35 | def send_text_send_message(self): 36 | 37 | from azure.communication.messages import NotificationMessagesClient 38 | from azure.communication.messages.models import TextNotificationContent 39 | 40 | messaging_client = NotificationMessagesClient.from_connection_string(self.connection_string) 41 | 42 | text_options = TextNotificationContent( 43 | channel_registration_id=self.channel_id, 44 | to=[self.phone_number], 45 | content="Hello World via Notification Messaging SDK.", 46 | ) 47 | 48 | # calling send() with whatsapp message details 49 | message_responses = messaging_client.send(text_options) 50 | response = message_responses.receipts[0] 51 | print("Message with message id {} was successful sent to {}" 52 | .format(response.message_id, response.to)) 53 | 54 | if __name__ == '__main__': 55 | sample = SendWhatsAppMessageSample() 56 | sample.send_text_send_message() 57 | -------------------------------------------------------------------------------- /messages-quickstart/send_image_notification_messages.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: send_image_notification_messages.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates sending an Whatsapp message with image from business phone number to a single user. The NotificationMessageClient is 14 | authenticated using a connection string. 15 | USAGE: 16 | python send_image_notification_messages.py 17 | 18 | Set the environment variable with your own value before running the sample: 19 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 20 | 2) RECIPIENT_PHONE_NUMBER - a phone number with Whatsapp capabilities. Use list for recipient phone number. 21 | 3) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 22 | """ 23 | 24 | import os 25 | import sys 26 | 27 | sys.path.append("..") 28 | 29 | class SendWhatsAppMessageSample(object): 30 | 31 | connection_string = os.getenv("COMMUNICATION_SAMPLES_CONNECTION_STRING") 32 | phone_number = os.getenv("RECIPIENT_PHONE_NUMBER") 33 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 34 | 35 | def send_image_send_message(self): 36 | 37 | from azure.communication.messages import NotificationMessagesClient 38 | from azure.communication.messages.models import ImageNotificationContent 39 | 40 | messaging_client = NotificationMessagesClient.from_connection_string(self.connection_string) 41 | 42 | image_options = ImageNotificationContent( 43 | channel_registration_id=self.channel_id, 44 | to=[self.phone_number], 45 | content="Hello World via Notification Messaging SDK.", 46 | media_uri="https://aka.ms/acsicon1" 47 | ) 48 | 49 | # calling send() with whatsapp message details 50 | message_responses = messaging_client.send(image_options) 51 | response = message_responses.receipts[0] 52 | print("Message with message id {} was successful sent to {}" 53 | .format(response.message_id, response.to)) 54 | 55 | if __name__ == '__main__': 56 | sample = SendWhatsAppMessageSample() 57 | sample.send_image_send_message() 58 | -------------------------------------------------------------------------------- /access-tokens-quickstart/issue-access-tokens.py: -------------------------------------------------------------------------------- 1 | import os 2 | from datetime import timedelta 3 | from azure.communication.identity import CommunicationIdentityClient, CommunicationUserIdentifier 4 | 5 | try: 6 | print("Azure Communication Services - Access Tokens Quickstart") 7 | 8 | # This code demonstrates how to fetch your connection string from an environment variable. 9 | connection_string = os.environ["COMMUNICATION_SERVICES_CONNECTION_STRING"] 10 | 11 | # Instantiate the identity client 12 | client = CommunicationIdentityClient.from_connection_string(connection_string) 13 | 14 | # Create an identity 15 | identity = client.create_user() 16 | print("\nCreated an identity with ID: " + identity.properties['id']) 17 | 18 | #Store the identity to issue access tokens later 19 | existingIdentity = identity 20 | 21 | # Issue an access token with a validity of 24 hours and the "voip" scope for an identity 22 | token_result = client.get_token(identity, ["voip"]) 23 | print("\nIssued an access token with 'voip' scope that expires at " + token_result.expires_on + ":") 24 | print(token_result.token) 25 | 26 | # Issue an access token with a validity of an hour and the "voip" scope for an identity 27 | token_expires_in = timedelta(hours=1) 28 | token_result = client.get_token(identity, ["voip"], token_expires_in=token_expires_in) 29 | 30 | # Create an identity and issue an access token with a validity of 24 hours within the same request 31 | identity_token_result = client.create_user_and_token(["voip"]) 32 | # Get the token details from the response 33 | identity = identity_token_result[0] 34 | token = identity_token_result[1].token 35 | expires_on = identity_token_result[1].expires_on 36 | print("\nCreated an identity with ID: " + identity.properties['id']) 37 | print("\nIssued an access token with 'voip' scope that expires at " + expires_on + ":") 38 | print(token) 39 | 40 | # Refresh access tokens - existingIdentity represents identity of Azure Communication Services stored during identity creation 41 | identity = CommunicationUserIdentifier(existingIdentity.properties['id']) 42 | token_result = client.get_token( identity, ["voip"]) 43 | 44 | # Revoke access tokens 45 | client.revoke_tokens(identity) 46 | print("\nSuccessfully revoked all access tokens for identity with ID: " + identity.properties['id']) 47 | 48 | # Delete an identity 49 | client.delete_user(identity) 50 | print("\nDeleted the identity with ID: " + identity.properties['id']) 51 | 52 | except Exception as ex: 53 | print("Exception:") 54 | print(ex) -------------------------------------------------------------------------------- /use-managed-Identity/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-services 8 | - azure-communication-sms 9 | --- 10 | 11 | 12 | # Use managed identities 13 | 14 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Use managed identities](https://docs.microsoft.com/azure/communication-services/quickstarts/managed-identity?pivots=programming-language-python) 15 | 16 | ## Prerequisites 17 | 18 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 19 | - [Python](https://www.python.org/downloads/) 3.7 or above. 20 | - A deployed Communication Services resource and connection string. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 21 | - To send an SMS you will need a [Phone Number](https://docs.microsoft.com/azure/communication-services/quickstarts/telephony-sms/get-phone-number?pivots=programming-language-python). 22 | - A setup managed identity for a development environment, see [Authorize access with managed identity](https://docs.microsoft.com/azure/communication-services/quickstarts/managed-identity-from-cli). 23 | ## Code Structure 24 | 25 | - **./use-managed-Identity/managed-identity.py:** contains code to use managed identities. 26 | 27 | ## Install the packages 28 | 29 | - pip install azure-identity 30 | - pip install azure-communication-identity 31 | - pip install azure-communication-sms 32 | 33 | ## Before running sample code 34 | 35 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 36 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 37 | 3. With the Communication Services procured in pre-requisites, add endpoint to the **managed-identity.py** file at line no 29: ```endpoint = "https://.communication.azure.com/"```. 38 | 4. With the SMS enabled telephone number procured in pre-requisites, add it to the **managed-identity.py** file. Assign your ACS telephone number and sender number at line 36: ```sms_result = send_sms(endpoint, "", "", "Hello from Managed Identities");``` 39 | 40 | ## Run Locally 41 | 42 | From a console prompt, navigate to the directory containing the managed-identity.py file, then execute the following command to run the app. 43 | 44 | python ./managed-identity.py 45 | 46 | -------------------------------------------------------------------------------- /add-chat/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-chat 8 | - azure-communication-identity 9 | --- 10 | 11 | 12 | # Add Chat to your App 13 | 14 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Add Chat to your App](https://docs.microsoft.com/azure/communication-services/quickstarts/chat/get-started?pivots=programming-language-python) 15 | 16 | ## Prerequisites 17 | 18 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 19 | - Install [Python](https://www.python.org/downloads/) 3.7 or above. 20 | - Create an Azure Communication Services resource. For details, see [Quickstart: Create and manage Communication Services resources](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource endpoint for this quickstart. 21 | - A [user access token](https://docs.microsoft.com/azure/communication-services/quickstarts/access-tokens?pivots=programming-language-python). Be sure to set the scope to **chat**, and note the **token** string as well as the **userId** string. 22 | 23 | ## Code Structure 24 | 25 | - **./add-chat/start-chat.py:** contains code for chat. 26 | 27 | ## Install the packages 28 | 29 | pip install azure-communication-chat 30 | 31 | pip install azure-communication-identity 32 | 33 | ## Before running sample code 34 | 35 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 36 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 37 | 3. With the Communication Services procured in pre-requisites, add endpoint to **start-chat.py** file at line no:11 ```endpoint = "https://.communication.azure.com"```. 38 | 4. With the access token procured in pre-requisites, add it to the **start-chat.py** file. Assign token at line no:12 ```chat_client = ChatClient(endpoint, CommunicationTokenCredential(""))```. 39 | 5. With the Communication Services procured in pre-requisites, add connection string to **start-chat.py** file at line no:58 ```identity_client = CommunicationIdentityClient.from_connection_string('')```. 40 | 41 | 42 | ## Run Locally 43 | 44 | From a console prompt, navigate to the directory containing the start-chat.py file, then execute the following command to run the app. 45 | 46 | python ./start-chat.py 47 | 48 | -------------------------------------------------------------------------------- /messages-quickstart/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | 6 | Products: 7 | - azure 8 | - azure-communication-messages 9 | --- 10 | 11 | # Advanced Messages quick start 12 | 13 | For full instructions on how to build this code sample from scratch, look at [Quickstart: Send WhatsApp Messages](https://learn.microsoft.com/azure/communication-services/quickstarts/advanced-messaging/whatsapp/get-started?pivots=programming-language-python) 14 | 15 | ## Prerequisites 16 | 17 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 18 | - Install [Python](https://www.python.org/downloads/) 3.7 or above. 19 | - Create an Azure Communication Services resource. For details, see [Quickstart: Create and manage Communication Services resources](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource endpoint for this quickstart. 20 | - Active WhatsApp phone number to receive messages. 21 | - [WhatsApp Business Account registered with your Azure Communication Services resource](/azure/communication-services/quickstarts/advanced-messaging/whatsapp/connect-whatsapp-business-account) 22 | 23 | ## Code Structure 24 | To run any sample, please select the respective python script. 25 | - **./messages-quickstart/send_text_notification_messages.py:** contains sample code for sending whatsapp messages. 26 | 27 | ## Install the packages 28 | 29 | From a console prompt, navigate to the directory containing the messages-quickstart.py file, then execute the following command: 30 | 31 | ```console 32 | pip install azure-communication-messages 33 | ``` 34 | 35 | ## Before running sample code 36 | 37 | 1. Open an instance of PowerShell/Windows Terminal/Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 38 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 39 | 3. `cd communication-services-python-quickstarts/messages-quickstart` 40 | 4. With the Communication Services procured in pre-requisites, set environment variables as needed in Sample file and update the same in **send_text_notification_messages.py** file. 41 | 42 | ## Run Locally 43 | 44 | From a console prompt, navigate to the directory containing the send_text_notification_messages.py file, then execute the following command to run the app. 45 | 46 | ```python 47 | python ./send_text_notification_messages.py 48 | ``` 49 | 50 | Note: Please follow the same approach for running any Sample. 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure Communication Services Python SDK 2 | 3 | Azure Communication Services enable developers to add communication capabilities to their applications. 4 | 5 | ## Prerequisites 6 | 7 | - Create an Azure account with an active subscription. For details, see [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 8 | - Install [Python](https://www.python.org/downloads/) 3.7 or above. 9 | - Create an Azure Communication Services resource. For details, see [Create an Azure Communication Resource](https://docs.microsoft.com/en-us/azure/communication-services/quickstarts/create-communication-resource?tabs=windows&pivots=platform-azp). 10 | 11 | ## Quickstarts 12 | 13 | 1. [Send SMS](https://docs.microsoft.com/en-us/azure/communication-services/quickstarts/telephony-sms/send?pivots=programming-language-python) 14 | 15 | 2. [Add Chat to your app](https://docs.microsoft.com/en-us/azure/communication-services/quickstarts/chat/get-started?pivots=programming-language-python) 16 | 17 | 3. [Create and manage access tokens](https://docs.microsoft.com/azure/communication-services/quickstarts/access-tokens?pivots=programming-language-python) 18 | 19 | 4. [Set up and manage access tokens for Teams users](https://docs.microsoft.com/azure/communication-services/quickstarts/manage-teams-identity?pivots=programming-language-python) 20 | 21 | 5. [Manage Phone Numbers](https://docs.microsoft.com/azure/communication-services/quickstarts/telephony-sms/get-phone-number?pivots=programming-language-python) 22 | 23 | 6. [Use managed identities](https://docs.microsoft.com/azure/communication-services/quickstarts/managed-identity?pivots=programming-language-python) 24 | 25 | 7. [Get started with Rooms](https://docs.microsoft.com/en-us/azure/communication-services/quickstarts/rooms/get-started-rooms?pivots=programming-language-python) 26 | 27 | ## Data CollectionAdd commentMore actions 28 | 29 | The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. Some Quickstart samples collect information about users and their use of the software that cannot be opted out of. Do not use Quickstart samples that collect information if you wish to avoid telemetry. You can learn more about data collection and use in the help documentation and Microsoft’s [privacy statement](https://go.microsoft.com/fwlink/?LinkID=824704). For more information on the data collected by the Azure SDK, please visit the [Telemetry Policy](https://learn.microsoft.com/azure/communication-services/concepts/privacy) page. 30 | -------------------------------------------------------------------------------- /messages-quickstart/send_text_notification_messages_async.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: send_text_notification_messages_async.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates sending an Whatsapp message from business phone number to a single user. The NotificationMessageClient is 14 | authenticated using a connection string. 15 | 16 | USAGE: 17 | python send_text_notification_messages_async.py 18 | 19 | Set the environment variable with your own value before running the sample: 20 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 21 | 2) RECIPIENT_PHONE_NUMBER - a phone number with Whatsapp capabilities 22 | 3) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 23 | """ 24 | 25 | import asyncio 26 | import os 27 | import sys 28 | 29 | sys.path.append("..") 30 | 31 | class SendWhatsAppMessageSampleAsync(object): 32 | 33 | connection_string = os.getenv("COMMUNICATION_SAMPLES_CONNECTION_STRING") 34 | phone_number = os.getenv("RECIPIENT_PHONE_NUMBER") 35 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 36 | 37 | async def send_text_send_message_async(self): 38 | from azure.communication.messages.aio import NotificationMessagesClient 39 | from azure.communication.messages.models import TextNotificationContent 40 | 41 | # client creation 42 | messaging_client = NotificationMessagesClient.from_connection_string(self.connection_string) 43 | 44 | text_options = TextNotificationContent( 45 | channel_registration_id=self.channel_id, 46 | to=[self.phone_number], 47 | content="Hello World via Notification Messaging SDK.", 48 | ) 49 | 50 | # calling send() with whatsapp message details 51 | async with messaging_client: 52 | message_responses = await messaging_client.send(text_options) 53 | response = message_responses.receipts[0] 54 | print("Message with message id {} was successful sent to {}" 55 | .format(response.message_id, response.to)) 56 | 57 | 58 | async def main(): 59 | sample = SendWhatsAppMessageSampleAsync() 60 | await sample.send_text_send_message_async() 61 | 62 | 63 | if __name__ == '__main__': 64 | asyncio.run(main()) 65 | -------------------------------------------------------------------------------- /messages-quickstart/send_image_notification_messages_async.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: send_image_notification_messages_async.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates sending an Whatsapp message with image from business phone number to a single user. The NotificationMessageClient is 14 | authenticated using a connection string. 15 | 16 | USAGE: 17 | python send_image_notification_messages_async.py 18 | 19 | Set the environment variable with your own value before running the sample: 20 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 21 | 2) RECIPIENT_PHONE_NUMBER - a phone number with Whatsapp capabilities 22 | 3) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 23 | """ 24 | 25 | import asyncio 26 | import os 27 | import sys 28 | 29 | sys.path.append("..") 30 | 31 | class SendWhatsAppMessageSampleAsync(object): 32 | 33 | connection_string = os.getenv("COMMUNICATION_SAMPLES_CONNECTION_STRING") 34 | phone_number = os.getenv("RECIPIENT_PHONE_NUMBER") 35 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 36 | 37 | async def send_image_send_message_async(self): 38 | from azure.communication.messages.aio import NotificationMessagesClient 39 | from azure.communication.messages.models import ImageNotificationContent 40 | 41 | # client creation 42 | messaging_client = NotificationMessagesClient.from_connection_string(self.connection_string) 43 | 44 | image_options = ImageNotificationContent( 45 | channel_registration_id=self.channel_id, 46 | to= [self.phone_number], 47 | content="Hello World via Notification Messaging SDK.", 48 | media_uri="https://aka.ms/acsicon1" 49 | ) 50 | 51 | # calling send() with whatsapp message details 52 | async with messaging_client: 53 | message_responses = await messaging_client.send(image_options) 54 | response = message_responses.receipts[0] 55 | print("Message with message id {} was successful sent to {}" 56 | .format(response.message_id, response.to)) 57 | 58 | async def main(): 59 | sample = SendWhatsAppMessageSampleAsync() 60 | await sample.send_image_send_message_async() 61 | 62 | 63 | if __name__ == '__main__': 64 | asyncio.run(main()) 65 | -------------------------------------------------------------------------------- /phone-numbers-quickstart/phone_numbers_sample.py: -------------------------------------------------------------------------------- 1 | import os 2 | from azure.communication.phonenumbers import PhoneNumbersClient,PhoneNumberCapabilityType, PhoneNumberAssignmentType, PhoneNumberType, PhoneNumberCapabilities 3 | 4 | connection_string = 'https://.communication.azure.com/;accesskey=' 5 | try: 6 | print('Azure Communication Services - Phone Numbers Quickstart') 7 | 8 | #Initializing phone number client 9 | phone_numbers_client = PhoneNumbersClient.from_connection_string(connection_string) 10 | 11 | capabilities = PhoneNumberCapabilities( 12 | calling = PhoneNumberCapabilityType.INBOUND, 13 | sms = PhoneNumberCapabilityType.INBOUND_OUTBOUND 14 | ) 15 | 16 | #Search available phone numbers 17 | search_poller = phone_numbers_client.begin_search_available_phone_numbers( 18 | "US", 19 | PhoneNumberType.TOLL_FREE, 20 | PhoneNumberAssignmentType.APPLICATION, 21 | capabilities, 22 | polling = True 23 | ) 24 | search_result = search_poller.result() 25 | print ('Search id: ' + search_result.search_id) 26 | 27 | phone_number_list = search_result.phone_numbers 28 | phone_number = phone_number_list[0:1] 29 | 30 | print('Reserved phone numbers:') 31 | for phone_number in phone_number_list: 32 | print(phone_number) 33 | 34 | #Purchase available phone number 35 | purchase_poller = phone_numbers_client.begin_purchase_phone_numbers(search_result.search_id, polling = True) 36 | purchase_poller.result() 37 | print("The status of the purchase operation was: " + purchase_poller.status()) 38 | 39 | #Get purchased phone number 40 | purchased_phone_number_information = phone_numbers_client.get_purchased_phone_number(phone_number) 41 | print('Phone number: ' + purchased_phone_number_information.phone_number) 42 | print('Country code: ' + purchased_phone_number_information.country_code) 43 | 44 | #Get all purchased phone numbers 45 | purchased_phone_numbers = phone_numbers_client.list_purchased_phone_numbers() 46 | print('Purchased phone numbers:') 47 | for purchased_phone_number in purchased_phone_numbers: 48 | print(purchased_phone_number.phone_number) 49 | 50 | #Update the capabilities of the purchased phone number 51 | update_poller = phone_numbers_client.begin_update_phone_number_capabilities( 52 | phone_number, 53 | PhoneNumberCapabilityType.OUTBOUND, 54 | PhoneNumberCapabilityType.OUTBOUND, 55 | polling = True 56 | ) 57 | update_poller.result() 58 | print('Status of the operation: ' + update_poller.status()) 59 | 60 | #Release the purchased phone number 61 | release_poller = phone_numbers_client.begin_release_phone_number(phone_number) 62 | release_poller.result() 63 | print('Status of the operation: ' + release_poller.status()) 64 | 65 | 66 | except Exception as ex: 67 | print('Exception:') 68 | print(ex) -------------------------------------------------------------------------------- /messages-quickstart/send_template_notification_messages.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: send_template_notification_messages.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates sending an Whatsapp template message from business phone number to a single user. 14 | Template to be used in the sample needs to be created in WhatsApp Business account first. 15 | Follow the instructions in the Meta Business Help Center at https://www.facebook.com/business/help/2055875911147364?id=2129163877102343. 16 | The NotificationMessageClient is authenticated using a connection string. 17 | USAGE: 18 | python send_template_notification_messages.py 19 | 20 | Set the environment variable with your own value before running the sample: 21 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 22 | 2) RECIPIENT_PHONE_NUMBER - a phone number with Whatsapp capabilities. Use list for recipient phone number. 23 | 3) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 24 | """ 25 | 26 | import os 27 | import sys 28 | 29 | sys.path.append("..") 30 | 31 | class SendWhatsAppTemplateMessageSample(object): 32 | 33 | connection_string = os.getenv("COMMUNICATION_SAMPLES_CONNECTION_STRING") 34 | phone_number = os.getenv("RECIPIENT_PHONE_NUMBER") 35 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 36 | 37 | def send_template_send_message(self): 38 | 39 | from azure.communication.messages import NotificationMessagesClient 40 | from azure.communication.messages.models import TemplateNotificationContent, MessageTemplate 41 | 42 | messaging_client = NotificationMessagesClient.from_connection_string(self.connection_string) 43 | 44 | # Setting template options 45 | input_template: MessageTemplate = MessageTemplate( 46 | name="gathering_invitation", # Name of the WhatsApp Template 47 | language="ca") # Language of the WhatsApp Template 48 | template_options = TemplateNotificationContent( 49 | channel_registration_id= self.channel_id, 50 | to=[self.phone_number], 51 | template=input_template) 52 | 53 | # calling send() with whatsapp message details 54 | message_responses = messaging_client.send(template_options) 55 | response = message_responses.receipts[0] 56 | print("Message with message id {} was successful sent to {}" 57 | .format(response.message_id, response.to)) 58 | 59 | if __name__ == '__main__': 60 | sample = SendWhatsAppTemplateMessageSample() 61 | sample.send_template_send_message() 62 | -------------------------------------------------------------------------------- /jobrouter-quickstart/router-quickstart.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from azure.communication.jobrouter import ( 4 | JobRouterClient, 5 | JobRouterAdministrationClient 6 | ) 7 | 8 | from azure.communication.jobrouter.models import ( 9 | LongestIdleMode, 10 | RouterWorkerSelector, 11 | LabelOperator, 12 | RouterChannel, 13 | CloseJobOptions 14 | ) 15 | 16 | class RouterQuickstart(object): 17 | print("Azure Communication Services - Job Router Quickstart") 18 | 19 | # Get a connection string to our Azure Communication Services resource. 20 | connection_string = "conn_str" 21 | router_admin_client = JobRouterAdministrationClient.from_connection_string(conn_str = connection_string) 22 | router_client = JobRouterClient.from_connection_string(conn_str = connection_string) 23 | 24 | distribution_policy = router_admin_client.upsert_distribution_policy( 25 | distribution_policy_id ="distribution-policy-1", 26 | offer_expires_after_seconds = 60, 27 | mode = LongestIdleMode(), 28 | name = "My distribution policy") 29 | 30 | queue = router_admin_client.upsert_queue( 31 | queue_id = "queue-1", 32 | name = "My Queue", 33 | distribution_policy_id = distribution_policy.id) 34 | 35 | job = router_client.upsert_job( 36 | job_id = "job-1", 37 | channel_id = "voice", 38 | queue_id = queue.id, 39 | priority = 1, 40 | requested_worker_selectors = [ 41 | RouterWorkerSelector( 42 | key = "Some-Skill", 43 | label_operator = LabelOperator.GREATER_THAN, 44 | value = 10 45 | ) 46 | ]) 47 | 48 | worker = router_client.upsert_worker( 49 | worker_id = "worker-1", 50 | capacity = 1, 51 | queues = ["queue-1"], 52 | labels = { "Some-Skill": 11 }, 53 | channels = [RouterChannel(channel_id = "voice", capacity_cost_per_job = 1)], 54 | available_for_offers = True) 55 | 56 | time.sleep(10) 57 | worker = router_client.get_worker(worker_id = worker.id) 58 | for offer in worker.offers: 59 | print(f"Worker {worker.id} has an active offer for job {offer.job_id}") 60 | 61 | accept = router_client.accept_job_offer(worker_id = worker.id, offer_id = worker.offers[0].offer_id) 62 | print(f"Worker {worker.id} is assigned job {accept.job_id}") 63 | 64 | router_client.complete_job(job_id = job.id, assignment_id = accept.assignment_id) 65 | print(f"Worker {worker.id} has completed job {accept.job_id}") 66 | 67 | router_client.close_job(job_id = job.id, assignment_id = accept.assignment_id, options = CloseJobOptions(disposition_code = "Resolved")) 68 | print(f"Worker {worker.id} has closed job {accept.job_id}") 69 | 70 | router_client.delete_job(accept.job_id) 71 | print(f"Deleting {accept.job_id}") 72 | 73 | 74 | if __name__ == '__main__': 75 | router = RouterQuickstart() -------------------------------------------------------------------------------- /messages-quickstart/send_template_notification_messages_async.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: send_template_notification_messages_async.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates sending an Whatsapp template message from business phone number to a single user. 14 | Template to be used in the sample needs to be created in WhatsApp Business account first. 15 | Follow the instructions in the Meta Business Help Center at https://www.facebook.com/business/help/2055875911147364?id=2129163877102343. 16 | The NotificationMessageClient is authenticated using a connection string. 17 | USAGE: 18 | python send_template_notification_messages_async.py 19 | 20 | Set the environment variable with your own value before running the sample: 21 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 22 | 2) RECIPIENT_PHONE_NUMBER - a phone number with Whatsapp capabilities. Use list for recipient phone number. 23 | 3) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 24 | """ 25 | 26 | import asyncio 27 | import os 28 | import sys 29 | 30 | sys.path.append("..") 31 | 32 | class SendWhatsAppTemplateMessageSampleAsync(object): 33 | 34 | connection_string = os.getenv("COMMUNICATION_SAMPLES_CONNECTION_STRING") 35 | phone_number = os.getenv("RECIPIENT_PHONE_NUMBER") 36 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 37 | 38 | async def send_template_send_message_async(self): 39 | from azure.communication.messages.aio import NotificationMessagesClient 40 | from azure.communication.messages.models import TemplateNotificationContent, MessageTemplate 41 | 42 | messaging_client = NotificationMessagesClient.from_connection_string(self.connection_string) 43 | 44 | # Setting template options 45 | input_template: MessageTemplate = MessageTemplate( 46 | name="gathering_invitation", # Name of the WhatsApp Template 47 | language="ca") # Language of the WhatsApp Template 48 | template_options = TemplateNotificationContent( 49 | channel_registration_id= self.channel_id, 50 | to=[self.phone_number], 51 | template=input_template) 52 | 53 | # calling send() with whatsapp message details 54 | async with messaging_client: 55 | message_responses =await messaging_client.send(template_options) 56 | response = message_responses.receipts[0] 57 | print("Message with message id {} was successful sent to {}" 58 | .format(response.message_id, response.to)) 59 | 60 | 61 | async def main(): 62 | sample = SendWhatsAppTemplateMessageSampleAsync() 63 | await sample.send_template_send_message_async() 64 | 65 | if __name__ == '__main__': 66 | asyncio.run(main()) 67 | -------------------------------------------------------------------------------- /messages-quickstart/send_text_notification_messages_with_token_credentials.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # ------------------------------------------------------------------------- 4 | # Copyright (c) Microsoft Corporation. All rights reserved. 5 | # Licensed under the MIT License. See License.txt in the project root for 6 | # license information. 7 | # -------------------------------------------------------------------------- 8 | 9 | """ 10 | FILE: send_text_notification_messages_with_token_credentials.py 11 | 12 | DESCRIPTION: 13 | This sample demonstrates sending an Whatsapp message from business phone number to a single user. The NotificationMessageClient is 14 | authenticated using Bearer TokenCredentials with azureidentity. 15 | More information here: https://learn.microsoft.com/en-us/python/api/overview/azure/identity-readme?view=azure-python 16 | USAGE: 17 | python send_text_notification_messages_with_token_credentials.py 18 | 19 | Set the environment variable with your own value before running the sample: 20 | 1) COMMUNICATION_SAMPLES_CONNECTION_STRING - the connection string in your ACS resource 21 | 2) RECIPIENT_PHONE_NUMBER - a phone number with Whatsapp capabilities. Use list for recipient phone number. 22 | 3) WHATSAPP_CHANNEL_ID - Channel created in Azure portal for Advanced Messaging. 23 | 4) Follow defining environment variables for DefaultAzureCredentials as give here: 24 | https://learn.microsoft.com/en-us/python/api/overview/azure/identity-readme?view=azure-python#environment-variables 25 | https://learn.microsoft.com/en-us/entra/identity-platform/howto-create-service-principal-portal 26 | Variable name Value 27 | AZURE_CLIENT_ID ID of a Microsoft Entra application 28 | AZURE_TENANT_ID ID of the application's Microsoft Entra tenant 29 | AZURE_CLIENT_SECRET one of the application's client secrets 30 | """ 31 | 32 | import os 33 | import sys 34 | 35 | sys.path.append("..") 36 | 37 | class SendWhatsAppMessageSample(object): 38 | 39 | endpoint_string = os.getenv("COMMUNICATION_SAMPLES_ENDPOINT_STRING") 40 | phone_number = os.getenv("RECIPIENT_PHONE_NUMBER") 41 | channel_id = os.getenv("WHATSAPP_CHANNEL_ID") 42 | 43 | def send_text_send_message(self): 44 | 45 | from azure.communication.messages import NotificationMessagesClient 46 | from azure.communication.messages.models import TextNotificationContent 47 | from azure.identity import DefaultAzureCredential 48 | 49 | messaging_client = NotificationMessagesClient(endpoint=self.endpoint_string, 50 | credential=DefaultAzureCredential()) 51 | 52 | text_options = TextNotificationContent( 53 | channel_registration_id=self.channel_id, 54 | to=[self.phone_number], 55 | content="Hello World via Notification Messaging SDK using Token credentials.", 56 | ) 57 | 58 | # calling send() with whatsapp message details 59 | message_responses = messaging_client.send(text_options) 60 | response = message_responses.receipts[0] 61 | print("Message with message id {} was successful sent to {}" 62 | .format(response.message_id, response.to)) 63 | 64 | if __name__ == '__main__': 65 | sample = SendWhatsAppMessageSample() 66 | sample.send_text_send_message() 67 | -------------------------------------------------------------------------------- /manage-teams-identity-mobile-and-desktop/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - python 5 | products: 6 | - azure 7 | - azure-communication-services 8 | --- 9 | 10 | # Create and manage Communication access tokens for Teams users in mobile and desktop applications 11 | 12 | This code sample walks you through the process of acquiring a Communication Token Credential by exchanging an Azure AD token of a user with a Teams license for a valid Communication access token. 13 | 14 | This sample application utilizes the [MSAL Python](https://github.com/AzureAD/microsoft-authentication-library-for-python) library for authentication against the Azure AD and acquisition of a token with delegated permissions. The token exchange itself is facilitated by the `azure-communication-identity` package. 15 | 16 | To be able to use the token for Calling, use it to initialize the `CommunicationTokenCredential` from the `azure-communication-chat` library. 17 | 18 | ## Prerequisites 19 | 20 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 21 | - Python 3.7 or later version. 22 | - An active Communication Services resource and connection string. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource/). 23 | - Azure Active Directory tenant with users that have a Teams license. 24 | 25 | ## Before running sample code 26 | 27 | 1. Complete the [Administrator actions](https://docs.microsoft.com/azure/communication-services/quickstarts/manage-teams-identity?pivots=programming-language-javascript#administrator-actions) from the [Manage access tokens for Teams users quickstart](https://docs.microsoft.com/azure/communication-services/quickstarts/manage-teams-identity). 28 | - Take a note of Fabrikam's Azure AD Tenant ID and Contoso's Azure AD App Client ID. You'll need the values in the following steps. 29 | 1. On the Authentication pane of your Azure AD App, add a new platform of the mobile and desktop application type with the Redirect URI of `http://localhost`. 30 | 1. Open an instance of Windows Terminal, PowerShell, or an equivalent command line and navigate to the directory that you'd like to clone the sample to. 31 | 1. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 32 | 1. Navigate to the `manage-teams-identity-mobile-and-desktop` directory. 33 | 1. With the Communication Services procured in pre-requisites, add connection string, an Azure AD client ID and tenant ID to environment variable using below commands: 34 | 35 | ```console 36 | setx COMMUNICATION_SERVICES_CONNECTION_STRING 37 | setx AAD_CLIENT_ID 38 | setx AAD_TENANT_ID 39 | ``` 40 | 41 | ## Run the code 42 | 43 | From a console prompt, navigate to the directory containing the `exchange-communication-access-tokens.py` file, then execute the following node commands to run the app. 44 | 45 | 1. `pip install azure-communication-identity` and `pip install msal` to install the dependencies 46 | 2. `python exchange-communication-access-tokens.py` 47 | 48 | You should be presented with a browser window and navigated to the Azure AD login form. If the authentication is successful, the application receives an Azure AD access token and exchanges it for a Communication access token. 49 | -------------------------------------------------------------------------------- /send-email/README .md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-email 8 | --- 9 | 10 | # Email Sample 11 | 12 | ## Overview 13 | 14 | This is a sample email application to show how we can use the `azure-communication-email` package to build an email experience. 15 | This sample sends an email to the selected recipients of any domain using an [Email Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/email/create-email-communication-resource). 16 | This is a console application built using python 3.10.6. 17 | 18 | Additional documentation for this sample can be found on [Microsoft Docs](https://pypi.org/project/azure-communication-email). 19 | 20 | ## Prerequisites 21 | 22 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 23 | - [Python](https://www.python.org/downloads/) 3.7+. 24 | - An Azure Email Communication Services resource created and ready with a provisioned domain. [Get started with creating an Email Communication Resource](../create-email-communication-resource.md). 25 | - An active Azure Communication Services resource connected to an Email Domain and its connection string. [Get started by connecting an Email Communication Resource with a Azure Communication Resource](../connect-email-communication-resource.md). 26 | 27 | > Note: We can also send an email from our own verified domain [Add custom verified domains to Email Communication Service](https://docs.microsoft.com/azure/communication-services/quickstarts/email/add-custom-verified-domains). 28 | 29 | ### Prerequisite check 30 | 31 | - In a terminal or command window, run the `python --version` command to check that Python is installed. 32 | - To view the domains verified with your Email Communication Services resource, sign in to the [Azure portal](https://portal.azure.com/). Locate your Email Communication Services resource and open the **Provision domains** tab from the left navigation pane. 33 | 34 | ## Code Structure 35 | 36 | - **./send-email/send-email.py:** contains code for sending emails. 37 | 38 | ## Before running the sample for the first time 39 | 40 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent program and navigate to the directory that you'd like to clone the sample to. 41 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 42 | 43 | ## Create virtual environment 44 | 45 | Navigate to the `send-email` directory in the console. Create a virtual environment and activate it using the following commands. 46 | 47 | ```cmd 48 | python -m venv venv 49 | .\venv\Scripts\activate 50 | ``` 51 | 52 | ## Install the packages 53 | 54 | Execute the following command to install the SDK. 55 | 56 | ```cmd 57 | pip install azure-communication-email 58 | ``` 59 | 60 | ### Locally configuring the application 61 | 62 | Open the `send-email.py` file and configure the following settings: 63 | 64 | - `connection_string`: Replace `` with the connection string found within the 'Keys' blade of the Azure Communication Service resource. 65 | - `sender_address`: Replace `` with the sender email address obtained from the linked domain resource. 66 | - `recipient_address`: Replace `` with the recipient email address. 67 | 68 | ## Run Locally 69 | 70 | Execute the following command to run the app. 71 | 72 | ```cmd 73 | python ./send-email.py 74 | ``` 75 | 76 | ## ❤️ Feedback 77 | 78 | We appreciate your feedback and energy in helping us improve our services. [Please let us know if you are satisfied with ACS through this survey](https://microsoft.qualtrics.com/jfe/form/SV_5dtYL81xwHnUVue). 79 | -------------------------------------------------------------------------------- /callautomation-azure-openai-voice/readme.md: -------------------------------------------------------------------------------- 1 | |page_type| languages |products 2 | |---|-----------------------------------------|---| 3 | |sample|
Python
|
azureazure-communication-services
| 4 | 5 | # Call Automation - Quick Start Sample 6 | 7 | This is a sample application demonstrated during Microsoft Ignite 2024. It highlights an integration of Azure Communication Services with Azure OpenAI Service to enable intelligent conversational agents. 8 | 9 | ## Prerequisites 10 | 11 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 12 | - A deployed Communication Services resource. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 13 | - A [phone number](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/get-phone-number) in your Azure Communication Services resource that can get inbound calls. NB: phone numbers are not available in free subscriptions. 14 | - [Python](https://www.python.org/downloads/) 3.7 or above. 15 | - An Azure OpenAI Resource and Deployed Model. See [instructions](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource?pivots=web-portal). 16 | 17 | ## Before running the sample for the first time 18 | 19 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you would like to clone the sample to. 20 | 2. git clone `https://github.com/Azure-Samples/communication-services-python-quickstarts.git`. 21 | 3. Navigate to `callautomation-azure-openai-voice` folder and open `main.py` file. 22 | 23 | ### Setup the Python environment 24 | 25 | Create and activate python virtual environment and install required packages using following command 26 | ``` 27 | pip install -r requirements.txt 28 | ``` 29 | 30 | ### Setup and host your Azure DevTunnel 31 | 32 | [Azure DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) is an Azure service that enables you to share local web services hosted on the internet. Use the commands below to connect your local development environment to the public internet. This creates a tunnel with a persistent endpoint URL and which allows anonymous access. We will then use this endpoint to notify your application of calling events from the ACS Call Automation service. 33 | 34 | ```bash 35 | devtunnel create --allow-anonymous 36 | devtunnel port create -p 8000 37 | devtunnel host 38 | ``` 39 | 40 | ### Configuring application 41 | 42 | Open `main.py` file to configure the following settings 43 | 44 | 1. `ACS_CONNECTION_STRING`: Azure Communication Service resource's connection string. 45 | 2. `CALLBACK_URI_HOST`: Base url of the app. (For local development use dev tunnel url) 46 | 47 | Open `azureOpenAIService.py` file to configure the following settings 48 | 49 | 1. `AZURE_OPENAI_SERVICE_ENDPOINT`: Azure Open AI service endpoint 50 | 2. `AZURE_OPENAI_SERVICE_KEY`: Azure Open AI service key 51 | 3. `AZURE_OPENAI_DEPLOYMENT_MODEL_NAME`: Azure Open AI deployment name 52 | 53 | ## Run app locally 54 | 55 | 1. Navigate to `callautomation-azure-openai-voice` folder and run `main.py` in debug mode or use command `python ./main.py` to run it from PowerShell, Command Prompt or Unix Terminal 56 | 2. Browser should pop up with the below page. If not navigate it to `http://localhost:8000/`or your dev tunnel url. 57 | 3. Register an EventGrid Webhook for the IncomingCall(`https:///api/incomingCall`) event that points to your devtunnel URI. Instructions [here](https://learn.microsoft.com/en-us/azure/communication-services/concepts/call-automation/incoming-call-notification). 58 | 59 | Once that's completed you should have a running application. The best way to test this is to place a call to your ACS phone number and talk to your intelligent agent. 60 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to [project-title] 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | - [Code of Conduct](#coc) 16 | - [Issues and Bugs](#issue) 17 | - [Feature Requests](#feature) 18 | - [Submission Guidelines](#submit) 19 | 20 | ## Code of Conduct 21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 22 | 23 | ## Found an Issue? 24 | If you find a bug in the source code or a mistake in the documentation, you can help us by 25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can 26 | [submit a Pull Request](#submit-pr) with a fix. 27 | 28 | ## Want a Feature? 29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub 30 | Repository. If you would like to *implement* a new feature, please submit an issue with 31 | a proposal for your work first, to be sure that we can use it. 32 | 33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 34 | 35 | ## Submission Guidelines 36 | 37 | ### Submitting an Issue 38 | Before you submit an issue, search the archive, maybe your question was already answered. 39 | 40 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 41 | Help us to maximize the effort we can spend fixing issues and adding new 42 | features, by not reporting duplicate issues. Providing the following information will increase the 43 | chances of your issue being dealt with quickly: 44 | 45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 46 | * **Version** - what version is affected (e.g. 0.1.2) 47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 48 | * **Browsers and Operating System** - is this a problem with all browsers? 49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps 50 | * **Related Issues** - has a similar issue been reported before? 51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 52 | causing the problem (line of code or commit) 53 | 54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new]. 55 | 56 | ### Submitting a Pull Request (PR) 57 | Before you submit your Pull Request (PR) consider the following guidelines: 58 | 59 | * Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR 60 | that relates to your submission. You don't want to duplicate effort. 61 | 62 | * Make your changes in a new git fork: 63 | 64 | * Commit your changes using a descriptive commit message 65 | * Push your fork to GitHub: 66 | * In GitHub, create a pull request 67 | * If we suggest changes then: 68 | * Make the required updates. 69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 70 | 71 | ```shell 72 | git rebase master -i 73 | git push -f 74 | ``` 75 | 76 | That's it! Thank you for your contribution! 77 | -------------------------------------------------------------------------------- /send-email-advanced/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - Python 5 | products: 6 | - azure 7 | - azure-communication-email 8 | --- 9 | 10 | # Email Sample 11 | 12 | ## Overview 13 | 14 | This is a sample email application to show how we can use the `azure-communication-email` package to build an email experience. 15 | This sample sends an email to the selected recipients of any domain using an [Email Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/email/create-email-communication-resource). 16 | This is a console application built using python 3.10.6. 17 | 18 | Additional documentation for this sample can be found on [Microsoft Docs](https://docs.microsoft.com/azure/communication-services/concepts/email/email-overview). 19 | 20 | ## Prerequisites 21 | 22 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 23 | - [Python](https://www.python.org/downloads/) 3.7+. 24 | - An Azure Email Communication Services resource created and ready with a provisioned domain. [Get started with creating an Email Communication Resource](../create-email-communication-resource.md). 25 | - An active Azure Communication Services resource connected to an Email Domain and its connection string. [Get started by connecting an Email Communication Resource with a Azure Communication Resource](../connect-email-communication-resource.md). 26 | 27 | > Note: We can also send an email from our own verified domain [Add custom verified domains to Email Communication Service](https://docs.microsoft.com/azure/communication-services/quickstarts/email/add-custom-verified-domains). 28 | 29 | ### Prerequisite check 30 | 31 | - In a terminal or command window, run the `python --version` command to check that Python is installed. 32 | - To view the domains verified with your Email Communication Services resource, sign in to the [Azure portal](https://portal.azure.com/). Locate your Email Communication Services resource and open the **Provision domains** tab from the left navigation pane. 33 | 34 | ## Code structure 35 | 36 | The advanced version of send-email includes the following sub samples. 37 | 38 | ### Send email with attachments 39 | 40 | - ./send-email-advanced/send-email-attachments/send-email-attachments.py 41 | 42 | ### Send email with inline attachments 43 | 44 | - ./send-email-advanced/send-email-inline-attachments/send-email-inline.attachments.py 45 | 46 | ### Send email to multiple recipients 47 | 48 | - ./send-email-advanced/send-email-multiple-recipients/send-email-multiple-recipients.py 49 | 50 | ### Resume send email with continuation token 51 | 52 | - ./send-email-advanced/send-email-continuation-token/send-email-continuation-token.py 53 | 54 | ## Before running the sample for the first time 55 | 56 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent program and navigate to the directory that you'd like to clone the sample to. 57 | 2. `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 58 | 59 | ## Create virtual environment 60 | 61 | Navigate to the `send-email` directory in the console. Create a virtual environment and activate it using the following commands. 62 | 63 | ```cmd 64 | python -m venv venv 65 | .\venv\Scripts\activate 66 | ``` 67 | 68 | ## Install the packages 69 | 70 | Execute the following command to install the SDK. 71 | 72 | ```cmd 73 | pip install azure-communication-email 74 | ``` 75 | 76 | ### Locally configuring the application 77 | 78 | Open the corresponding py file of the sample to configure the following settings: 79 | 80 | - `connection_string`: Replace `` with the connection string found within the 'Keys' blade of the Azure Communication Service resource. 81 | - `sender_address`: Replace `` with the sender email address obtained from the linked domain resource. 82 | - `recipient_address`: Replace `` with the recipient email address. 83 | 84 | ## Run Locally 85 | 86 | Execute the following command to run the app. 87 | 88 | ```cmd 89 | python ./.py 90 | ``` 91 | 92 | ## ❤️ Feedback 93 | 94 | We appreciate your feedback and energy in helping us improve our services. [Please let us know if you are satisfied with ACS through this survey](https://microsoft.qualtrics.com/jfe/form/SV_5dtYL81xwHnUVue). 95 | -------------------------------------------------------------------------------- /add-chat/start-chat.py: -------------------------------------------------------------------------------- 1 | import os 2 | from azure.communication.chat import ChatClient, CommunicationTokenCredential 3 | from datetime import datetime, timedelta 4 | from azure.communication.chat import ChatMessageType 5 | from azure.communication.identity import CommunicationIdentityClient 6 | from azure.communication.chat import ChatParticipant 7 | 8 | try: 9 | print('Azure Communication Services - Chat Quickstart') 10 | # Create a chat client 11 | endpoint = "https://.communication.azure.com" 12 | chat_client = ChatClient(endpoint, CommunicationTokenCredential("")) 13 | 14 | # Start a chat thread 15 | topic="test topic" 16 | 17 | create_chat_thread_result = chat_client.create_chat_thread(topic) 18 | chat_thread_client = chat_client.get_chat_thread_client(create_chat_thread_result.chat_thread.id) 19 | 20 | # Get a chat thread client 21 | thread_id = create_chat_thread_result.chat_thread.id 22 | chat_thread_client = chat_client.get_chat_thread_client(thread_id) 23 | 24 | # List all chat threads 25 | start_time = datetime.utcnow() - timedelta(days=2) 26 | 27 | chat_threads = chat_client.list_chat_threads(results_per_page=5, start_time=start_time) 28 | for chat_thread_item_page in chat_threads.by_page(): 29 | for chat_thread_item in chat_thread_item_page: 30 | print(chat_thread_item) 31 | print('Chat Thread Id: ', chat_thread_item.id) 32 | 33 | 34 | # Send a message to a chat thread 35 | content='hello world' 36 | sender_display_name='sender name' 37 | 38 | # specify chat message type with pre-built enumerations 39 | send_message_result_w_enum = chat_thread_client.send_message(content=content, sender_display_name=sender_display_name, chat_message_type=ChatMessageType.TEXT) 40 | print("Message sent: id: ", send_message_result_w_enum.id) 41 | 42 | # Receive chat messages from a chat thread 43 | start_time_receive = datetime.utcnow() - timedelta(days=1) 44 | 45 | chat_messages = chat_thread_client.list_messages(results_per_page=1, start_time=start_time_receive) 46 | for chat_message_page in chat_messages.by_page(): 47 | for chat_message in chat_message_page: 48 | print("ChatMessage: Id=", chat_message.id, "; Content=", chat_message.content.message) 49 | 50 | # Send read receipt 51 | content='read receipt' 52 | 53 | send_message_result = chat_thread_client.send_message(content) 54 | chat_thread_client.send_read_receipt(message_id=send_message_result.id) 55 | 56 | # Add a user as a participant to the chat thread 57 | # create 2 users 58 | identity_client = CommunicationIdentityClient.from_connection_string('') 59 | new_users = [identity_client.create_user() for i in range(2)] 60 | 61 | 62 | # # conversely, you can also add an existing user to a chat thread; provided the user_id is known 63 | # from azure.communication.identity import CommunicationUserIdentifier 64 | # 65 | # user_id = 'some user id' 66 | # user_display_name = "Wilma Flinstone" 67 | # new_user = CommunicationUserIdentifier(user_id) 68 | # participant = ChatParticipant( 69 | # user=new_user, 70 | # display_name=user_display_name, 71 | # share_history_time=datetime.utcnow()) 72 | 73 | 74 | participants = [] 75 | for _user in new_users: 76 | chat_thread_participant = ChatParticipant( 77 | identifier=_user, 78 | display_name='Fred Flinstone', 79 | share_history_time=datetime.utcnow() 80 | ) 81 | participants.append(chat_thread_participant) 82 | 83 | response = chat_thread_client.add_participants(participants) 84 | 85 | def decide_to_retry(error, **kwargs): 86 | """ 87 | Insert some custom logic to decide if retry is applicable based on error 88 | """ 89 | return True 90 | 91 | # verify if all users has been successfully added or not 92 | # in case of partial failures, you can retry to add all the failed participants 93 | retry = [p for p, e in response if decide_to_retry(e)] 94 | if retry: 95 | chat_thread_client.add_participants(retry) 96 | 97 | chat_thread_participants = chat_thread_client.list_participants() 98 | for chat_thread_participant_page in chat_thread_participants.by_page(): 99 | for chat_thread_participant in chat_thread_participant_page: 100 | print("ChatParticipant: ", chat_thread_participant) 101 | 102 | except Exception as ex: 103 | print('Exception:') 104 | print(ex) -------------------------------------------------------------------------------- /callautomation-openai-sample/readme.md: -------------------------------------------------------------------------------- 1 | |page_type| languages |products 2 | |---|-----------------------------------------|---| 3 | |sample|
Python
|
azureazure-communication-services
| 4 | 5 | # Call Automation - Quick Start Sample 6 | 7 | This is a sample application demonstrated during Microsoft Build 2023. It highlights an integration of Azure Communication Services with Azure OpenAI Service to enable intelligent conversational agents. 8 | 9 | ## Prerequisites 10 | 11 | - Create an Azure account with an active subscription. For details, see [Create an account for free](https://azure.microsoft.com/free/) 12 | - Create an Azure Communication Services resource. For details, see [Create an Azure Communication Resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource **connection string** for this sample. 13 | - An Calling-enabled telephone number. [Get a phone number](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/get-phone-number?tabs=windows&pivots=platform-azp). 14 | - Azure Dev Tunnels CLI. For details, see [Enable dev tunnel](https://docs.tunnels.api.visualstudio.com/cli) 15 | - Create an Azure Cognitive Services resource. For details, see [Create an Azure Cognitive Services Resource](https://learn.microsoft.com/en-us/azure/cognitive-services/cognitive-services-apis-create-account) 16 | - An Azure OpenAI Resource and Deployed Model. See [instructions](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal). 17 | - Create and host a Azure Dev Tunnel. Instructions [here](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started) 18 | - [Python](https://www.python.org/downloads/) 3.7 or above. 19 | 20 | ## Before running the sample for the first time 21 | 22 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you would like to clone the sample to. 23 | 2. git clone `https://github.com/Azure-Samples/communication-services-python-quickstarts.git`. 24 | 3. Navigate to `callautomation-openai-sample` folder and open `main.py` file. 25 | 26 | ### Setup the Python environment 27 | 28 | Create and activate python virtual environment and install required packages using following command 29 | ``` 30 | pip install -r requirements.txt 31 | ``` 32 | 33 | ### Setup and host your Azure DevTunnel 34 | 35 | [Azure DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) is an Azure service that enables you to share local web services hosted on the internet. Use the commands below to connect your local development environment to the public internet. This creates a tunnel with a persistent endpoint URL and which allows anonymous access. We will then use this endpoint to notify your application of calling events from the ACS Call Automation service. 36 | 37 | ```bash 38 | devtunnel create --allow-anonymous 39 | devtunnel port create -p 8080 40 | devtunnel host 41 | ``` 42 | 43 | ### Configuring application 44 | 45 | Open `main.py` file to configure the following settings 46 | 47 | 1. - `CALLBACK_URI_HOST`: your dev tunnel endpoint 48 | 2. - `COGNITIVE_SERVICE_ENDPOINT`: The Cognitive Services endpoint 49 | 3. - `ACS_CONNECTION_STRING`: Azure Communication Service resource's connection string. 50 | 4. - `AZURE_OPENAI_SERVICE_KEY`: Open AI's Service Key 51 | 5. - `AZURE_OPENAI_SERVICE_ENDPOINT`: Open AI's Service Endpoint 52 | 6. - `AZURE_OPENAI_DEPLOYMENT_MODEL_NAME`: Open AI's Model name 53 | 6. - `AGENT_PHONE_NUMBER`: Agent Phone Number to transfer call 54 | 55 | ## Run app locally 56 | 57 | 1. Navigate to `callautomation-openai-sample` folder and run `main.py` in debug mode or use command `python ./main.py` to run it from PowerShell, Command Prompt or Unix Terminal 58 | 2. Browser should pop up with the below page. If not navigate it to `http://localhost:8080/` or your dev tunnel url. 59 | 3. Register an EventGrid Webhook for the IncomingCall Event that points to your DevTunnel URI. Instructions [here](https://learn.microsoft.com/en-us/azure/communication-services/concepts/call-automation/incoming-call-notification). 60 | 61 | Once that's completed you should have a running application. The best way to test this is to place a call to your ACS phone number and talk to your intelligent agent. 62 | -------------------------------------------------------------------------------- /callautomation-live-transcription/readme.md: -------------------------------------------------------------------------------- 1 | |page_type| languages |products 2 | |---|-----------------------------------------|---| 3 | |sample|
Python
|
azureazure-communication-services
| 4 | 5 | # Call Automation - Quick Start Sample 6 | 7 | This sample application shows how the Azure Communication Services - Call Automation SDK can be used generate the live transcription between PSTN calls. 8 | It accepts an incoming call from a phone number, performs DTMF recognition, and transfer the call to agent. You can see the live transcription in websocket during the conversation between agent and user 9 | This sample application is also capable of making multiple concurrent inbound calls. The application is a web-based application built on Python. 10 | 11 | ## Prerequisites 12 | 13 | - Create an Azure account with an active subscription. For details, see [Create an account for free](https://azure.microsoft.com/free/) 14 | - Create an Azure Communication Services resource. For details, see [Create an Azure Communication Resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource **connection string** for this sample. 15 | - An Calling-enabled telephone number. [Get a phone number](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/get-phone-number?tabs=windows&pivots=platform-azp). 16 | - Create Azure AI Multi Service resource. For details, see [Create an Azure AI Multi service](https://learn.microsoft.com/en-us/azure/cognitive-services/cognitive-services-apis-create-account). 17 | - An Azure OpenAI Resource and Deployed Model. See [instructions](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal). 18 | - [Python](https://www.python.org/downloads/) 3.7 or above. 19 | 20 | ## Before running the sample for the first time 21 | 22 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you would like to clone the sample to. 23 | 2. git clone `https://github.com/Azure-Samples/communication-services-python-quickstarts.git`. 24 | 3. Navigate to `callautomation-openai-sample` folder and open `main.py` file. 25 | 26 | ### Setup the Python environment 27 | 28 | [Optional] Create and activate python virtual environment and install required packages using following command 29 | ``` 30 | python -m venv venv 31 | venv\Scripts\activate 32 | ``` 33 | Install the required packages using the following command 34 | ``` 35 | pip install -r requirements.txt 36 | ``` 37 | 38 | ### Setup and host your Azure DevTunnel 39 | 40 | [Azure DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) is an Azure service that enables you to share local web services hosted on the internet. Use the commands below to connect your local development environment to the public internet. This creates a tunnel with a persistent endpoint URL and which allows anonymous access. We will then use this endpoint to notify your application of calling events from the ACS Call Automation service. 41 | 42 | ```bash 43 | devtunnel create --allow-anonymous 44 | devtunnel port create -p 8080 45 | devtunnel host 46 | ``` 47 | 48 | ### Configuring application 49 | 50 | Open `main.py` file to configure the following settings 51 | 52 | 1. - `CALLBACK_URI_HOST`: Ngrok url for the server port (in this example port 8080) 53 | 2. - `COGNITIVE_SERVICE_ENDPOINT`: The Cognitive Services endpoint 54 | 3. - `ACS_CONNECTION_STRING`: Azure Communication Service resource's connection string. 55 | 4. - `ACS_PHONE_NUMBER`: Acs Phone Number 56 | 5. - `LOCALE`: Transcription locale 57 | 6. - `AGENT_PHONE_NUMBER`: Agent Phone Number to add into the call 58 | 59 | ## Run app locally 60 | 61 | 1. Navigate to `callautomation-live-transcription` folder and run `main.py` in debug mode or use command `python ./main.py` to run it from PowerShell, Command Prompt or Unix Terminal 62 | 2. Browser should pop up with the below page. If not navigate it to `http://localhost:8080/` or your ngrok url which points to 8080 port. 63 | 4. Register an EventGrid Webhook for the IncomingCall(`https:///api/incomingCall`) and for Recording File Status(`https:///api/recordingFileStatus`) Event that points to your devtunnel URI. Instructions [here](https://learn.microsoft.com/en-us/azure/communication-services/concepts/call-automation/incoming-call-notification). 64 | 65 | Once that's completed you should have a running application. The best way to test this is to place a call to your ACS phone number and talk to your intelligent agent. 66 | -------------------------------------------------------------------------------- /callautomation-connect-room/readme.md: -------------------------------------------------------------------------------- 1 | |page_type| languages |products 2 | |---|-----------------------------------------|---| 3 | |sample|
Python
|
azureazure-communication-services
| 4 | 5 | # Connect to a room call using Call Automation SDK 6 | 7 | In this quickstart sample, we cover how you can use Call Automation SDK to connect to an active Azure Communication Services (ACS) Rooms call with a connect endpoint. 8 | This involves creating a room call with room id and users and enabling PSTN dial out to add PSTN participant(s). 9 | 10 | ## Prerequisites 11 | 12 | - Create an Azure account with an active subscription. For details, see [Create an account for free](https://azure.microsoft.com/free/) 13 | - Create an Azure Communication Services resource. For details, see [Create an Azure Communication Resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource **connection string** for this sample. 14 | - An Calling-enabled telephone number. [Get a phone number](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/get-phone-number?tabs=windows&pivots=platform-azp). 15 | - Create Azure AI Multi Service resource. For details, see [Create an Azure AI Multi service](https://learn.microsoft.com/en-us/azure/cognitive-services/cognitive-services-apis-create-account). 16 | - [Python](https://www.python.org/downloads/) 3.7 or above. 17 | 18 | ## Before running the sample for the first time 19 | 20 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you would like to clone the sample to. 21 | 2. git clone `https://github.com/Azure-Samples/communication-services-python-quickstarts.git`. 22 | 3. Navigate to `callautomation-connect-room` folder and open `main.py` file. 23 | 24 | ## Before running calling rooms quickstart 25 | 1. To initiate rooms call with room id https://github.com/Azure-Samples/communication-services-javascript-quickstarts/tree/main/calling-rooms-quickstart 26 | 2. cd into the `calling-rooms-quickstart` folder. 27 | 3. From the root of the above folder, and with node installed, run `npm install` 28 | 4. to run sample `npx webpack serve --config webpack.config.js` 29 | 30 | ### Setup the Python environment 31 | 32 | [Optional] Create and activate python virtual environment and install required packages using following command 33 | ``` 34 | python -m venv venv 35 | venv\Scripts\activate 36 | ``` 37 | Install the required packages using the following command 38 | ``` 39 | pip install -r requirements.txt 40 | ``` 41 | 42 | ### Setup and host your Azure DevTunnel 43 | 44 | [Azure DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) is an Azure service that enables you to share local web services hosted on the internet. Use the commands below to connect your local development environment to the public internet. This creates a tunnel with a persistent endpoint URL and which allows anonymous access. We will then use this endpoint to notify your application of calling events from the ACS Call Automation service. 45 | 46 | ```bash 47 | devtunnel create --allow-anonymous 48 | devtunnel port create -p 8080 49 | devtunnel host 50 | ``` 51 | 52 | ### Configuring application 53 | 54 | Open `config.py` file to configure the following settings 55 | 56 | 1. - `CALLBACK_URI`: Ngrok url for the server port (in this example port 8080) 57 | 2. - `CONNECTION_STRING`: Azure Communication Service resource's connection string. 58 | 3. - `ACS_RESOURCE_PHONE_NUMBER`: Acs Phone Number 59 | 4. - `TARGET_PHONE_NUMBER`: Agent Phone Number to add into the call 60 | 5. - `COGNITIVE_SERVICES_ENDPOINT`: Cognitive service endpoint. 61 | 62 | ## Run app locally 63 | 64 | 1. Navigate to `callautomation-connect-room` folder and run `main.py` in debug mode or use command `python ./main.py` to run it from PowerShell, Command Prompt or Unix Terminal 65 | 2. Browser should pop up with the below page. If not navigate it to `http://localhost:8080/` or your ngrok url which points to 8080 port. 66 | 3. To connect rooms call, click on the `Connect a call!` button or make a Http get request to https:///connectCall 67 | 68 | ### Creating and connecting to room call. 69 | 70 | 1. Navigate to `http://localhost:8080/` or devtunnel url to create users and room id ![create room with user](./data/createRoom.png) 71 | 2. Open two tabs for Presenter and attendee ![calling room quickstart](./data/callingRoomQuickstart.png) 72 | 3. Copy tokens for presenter and attendee from ![tokens](./data/tokens.png) 73 | 4. Initialize call agent with tokens for both presenter and attendee. 74 | 5. Take room id ![room id](./data/roomId.png) and initiate rooms call for both users. ![join room call](./data/joinRoomCall.png) 75 | 6. Connect room call with callautomation connect call endpoint. ![connect room call](./data/connectCall.png) 76 | 77 | -------------------------------------------------------------------------------- /chat-insights-openai/chatInsights.py: -------------------------------------------------------------------------------- 1 | from azure.communication.chat import ChatClient, CommunicationTokenCredential, ChatMessageType,ChatParticipant 2 | from azure.communication.identity import CommunicationIdentityClient, CommunicationUserIdentifier 3 | from datetime import datetime 4 | 5 | connection_string = "INSERT AZURE COMMUNICATION SERVICES CONNECTION STRING" 6 | endpoint = "INSERT AZURE COMMUNICATION SERVICES ENDPOINT" 7 | client = CommunicationIdentityClient.from_connection_string(connection_string) 8 | identity1 = client.create_user() 9 | token_result1 = client.get_token(identity1, ["chat"]) 10 | identity2 = client.create_user() 11 | token_result2 = client.get_token(identity2, ["chat"]) 12 | 13 | Agent = ChatParticipant(identifier=identity1, display_name="Agent", share_history_time=datetime.utcnow()) 14 | Customer = ChatParticipant(identifier=identity2, display_name="Customer", share_history_time=datetime.utcnow()) 15 | participants = [Agent, Customer ] 16 | 17 | chat_client1 = ChatClient(endpoint, CommunicationTokenCredential(token_result1.token)) 18 | chat_client2 = ChatClient(endpoint, CommunicationTokenCredential(token_result2.token)) 19 | topic="Support conversation" 20 | create_chat_thread_result = chat_client1.create_chat_thread(topic, thread_participants=participants) 21 | chat_thread_client1 = chat_client1.get_chat_thread_client(create_chat_thread_result.chat_thread.id) 22 | chat_thread_client2 = chat_client2.get_chat_thread_client(create_chat_thread_result.chat_thread.id) 23 | 24 | agentText = [ 25 | "Thank you for calling our customer service hotline. How may I assist you today?", 26 | "I'm sorry to hear that. Can you provide me with your name and account number, so I can look up your account and assist you better?", 27 | "Thank you. Can you please tell me a little more about the problem you're having with your dishwasher?", 28 | "I see. That can be frustrating. Can you please check if the dishwasher is getting enough water supply? Also, please check if the spray arms inside the dishwasher are properly attached and not clogged.", 29 | "Alright. Please check the spray arms and see if they're clogged or damaged in any way. Also, make sure they're attached properly.", 30 | "That could be the reason why the dishes aren't getting cleaned properly. Can you please try to reattach the spray arm and run the dishwasher again?", 31 | "Great to hear that! Is there anything else I can help you with?", 32 | "You're welcome. Don't hesitate to call us back if you have any more issues. Have a nice day!" 33 | ] 34 | 35 | customerText = [ 36 | "Hi, I'm having trouble with my dishwasher. It doesn't seem to be cleaning the dishes properly.", 37 | "Yes, my name is Lisa and my account number is 12345.", 38 | "Well, it seems like the dishwasher isn't spraying enough water to clean the dishes properly. Some of the dishes are coming out dirty even after I run the dishwasher.", 39 | "I've checked the water supply and it seems to be okay. But I haven't checked the spray arms yet.", 40 | "Okay, let me check. Yes, it seems like one of the spray arms is loose and not attached properly.", 41 | "Sure, let me try that. Okay, I reattached the spray arm and ran the dishwasher again. It seems to be working fine now. The dishes are coming out clean.", 42 | "No, that's all. Thank you for your help.", 43 | "Bye." 44 | ] 45 | 46 | for x in range(len(agentText)): 47 | chat_thread_client1.send_message(content= agentText[x], sender_display_name="Agent", chat_message_type=ChatMessageType.TEXT) 48 | chat_thread_client2.send_message(content= customerText[x], sender_display_name="Customer", chat_message_type=ChatMessageType.TEXT) 49 | 50 | from datetime import datetime, timedelta 51 | 52 | start_time = datetime.utcnow() - timedelta(days=1) 53 | messages = [] 54 | 55 | chat_messages = chat_thread_client1.list_messages(results_per_page=1, start_time=start_time) 56 | for chat_message_page in chat_messages.by_page(): 57 | for chat_message in chat_message_page: 58 | if(chat_message.type == ChatMessageType.TEXT): 59 | messages.append(chat_message) 60 | 61 | # didn't know I had to filter out other messages 62 | 63 | prompt = "" 64 | for m in range(len(messages)-1, -1, -1): 65 | prompt = prompt + messages[m].sender_display_name + ": " + messages[m].content.message + "\n" 66 | print(prompt) 67 | 68 | import os 69 | import requests 70 | import json 71 | import openai 72 | 73 | openai.api_key = "INSERT YOUR AZURE OPENAI API KEY" 74 | openai.api_base = "INSERT YOUR AZURE OPENAI ENDPOINT" # your endpoint should look like the following https://YOUR_RESOURCE_NAME.openai.azure.com/ 75 | openai.api_type = 'azure' 76 | openai.api_version = '2022-12-01' # this may change in the future 77 | deployment_name='INSERT YOUR DEPLOyMENT NAME' #This will correspond to the custom name you chose for your deployment when you deployed a model. 78 | 79 | # Send a completion call to generate an answer 80 | start_phrase = 'For the following conversation, extract a topic, summary, highlights (1-3 bullet points of key information) and the sentiment of both of the users.\n\n' + prompt 81 | response = openai.Completion.create(engine=deployment_name, prompt=start_phrase, max_tokens=500) 82 | text = response['choices'][0]['text'].replace('\n', '').replace(' .', '.').strip() 83 | print(start_phrase + '\n' + text) 84 | 85 | -------------------------------------------------------------------------------- /callautomation-outboundcalling/readme.md: -------------------------------------------------------------------------------- 1 | |page_type| languages |products 2 | |---|-----------------------------------------|---| 3 | |sample|
Python
|
azureazure-communication-services
| 4 | 5 | # Call Automation - Quick Start Sample 6 | 7 | In this quickstart, we cover how you can use Call Automation SDK to make an outbound call to a phone number and use the newly announced integration with Azure AI services to play dynamic prompts to participants using Text-to-Speech and recognize user voice input through Speech-to-Text to drive business logic in your application. 8 | 9 | # Design 10 | 11 | ![design](./data/OutboundCallDesign.png) 12 | 13 | ## Prerequisites 14 | 15 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 16 | - A deployed Communication Services resource. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 17 | - A [phone number](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/get-phone-number) in your Azure Communication Services resource that can make outbound calls. NB: phone numbers are not available in free subscriptions. 18 | - Create Azure AI Multi Service resource. For details, see [Create an Azure AI Multi service](https://learn.microsoft.com/en-us/azure/cognitive-services/cognitive-services-apis-create-account). 19 | - Create and host a Azure Dev Tunnel. Instructions [here](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/get-started) 20 | - [Python](https://www.python.org/downloads/) 3.7 or above. 21 | - (Optional) A Microsoft Teams user with a phone license that is `voice` enabled. Teams phone license is required to add Teams users to the call. Learn more about Teams licenses [here](https://www.microsoft.com/microsoft-teams/compare-microsoft-teams-bundle-options). Learn about enabling phone system with `voice` [here](https://learn.microsoft.com/microsoftteams/setting-up-your-phone-system). You also need to complete the prerequisite step [Authorization for your Azure Communication Services Resource](https://learn.microsoft.com/azure/communication-services/how-tos/call-automation/teams-interop-call-automation?pivots=programming-language-javascript#step-1-authorization-for-your-azure-communication-services-resource-to-enable-calling-to-microsoft-teams-users) to enable calling to Microsoft Teams users. 22 | 23 | ## Before running the sample for the first time 24 | 25 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you would like to clone the sample to. 26 | 2. git clone `https://github.com/Azure-Samples/communication-services-python-quickstarts.git`. 27 | 3. Navigate to `callautomation-outboundcalling` folder and open `main.py` file. 28 | 29 | ### Setup the Python environment 30 | 31 | Create and activate python virtual environment and install required packages using following command 32 | ``` 33 | pip install -r requirements.txt 34 | ``` 35 | 36 | ### Setup and host your Azure DevTunnel 37 | 38 | [Azure DevTunnels](https://learn.microsoft.com/en-us/azure/developer/dev-tunnels/overview) is an Azure service that enables you to share local web services hosted on the internet. Use the commands below to connect your local development environment to the public internet. This creates a tunnel with a persistent endpoint URL and which allows anonymous access. We will then use this endpoint to notify your application of calling events from the ACS Call Automation service. 39 | 40 | ```bash 41 | devtunnel create --allow-anonymous 42 | devtunnel port create -p 8080 43 | devtunnel host 44 | ``` 45 | 46 | ### Configuring application 47 | 48 | Open `main.py` file to configure the following settings 49 | 50 | 1. `ACS_CONNECTION_STRING`: Azure Communication Service resource's connection string. 51 | 2. `ACS_PHONE_NUMBER`: Phone number associated with the Azure Communication Service resource. For e.g. "+1425XXXAAAA" 52 | 3. `TARGET_PHONE_NUMBER`: Target phone number to add in the call. For e.g. "+1425XXXAAAA" 53 | 4. `CALLBACK_URI_HOST`: Base url of the app. (For local development use dev tunnel url) 54 | 5. `COGNITIVE_SERVICES_ENDPOINT`: Cognitive Service Endpoint 55 | 6. `TARGET_TEAMS_USER_ID`: (Optional) update field with the Microsoft Teams user Id you would like to add to the call. See [Use Graph API to get Teams user Id](https://learn.microsoft.com/azure/communication-services/how-tos/call-automation/teams-interop-call-automation?pivots=programming-language-python#step-2-use-the-graph-api-to-get-microsoft-entra-object-id-for-teams-users-and-optionally-check-their-presence). Uncomment the below snippet in main.py to enable Teams Interop scenario. 56 | 57 | ```python 58 | call_connection_client.add_participant(target_participant = CallInvite( 59 | target = MicrosoftTeamsUserIdentifier(user_id=TARGET_TEAMS_USER_ID), 60 | source_display_name = "Jack (Contoso Tech Support)")) 61 | ``` 62 | 63 | ## Run app locally 64 | 65 | 1. Navigate to `callautomation-outboundcalling` folder and run `main.py` in debug mode or use command `python ./main.py` to run it from PowerShell, Command Prompt or Unix Terminal 66 | 2. Browser should pop up with the below page. If not navigate it to `http://localhost:8080/` or your dev tunnel url. 67 | 3. To initiate the call, click on the `Place a call!` button or make a Http get request to `https:///outboundCall` 68 | -------------------------------------------------------------------------------- /rooms-quickstart/rooms.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timezone, timedelta 2 | from azure.core.exceptions import HttpResponseError 3 | from azure.communication.rooms import ( 4 | RoomsClient, 5 | RoomParticipant, 6 | ParticipantRole 7 | ) 8 | from azure.communication.identity import ( 9 | CommunicationIdentityClient, 10 | CommunicationUserIdentifier 11 | ) 12 | class RoomsQuickstart(object): 13 | roomsCollection = [] 14 | connection_string = '' 15 | identity_client = CommunicationIdentityClient.from_connection_string(connection_string) 16 | user1 = identity_client.create_user() 17 | user2 = identity_client.create_user() 18 | user3 = identity_client.create_user() 19 | user4 = identity_client.create_user() 20 | user5 = identity_client.create_user() 21 | 22 | def setup(self): 23 | self.rooms_client = RoomsClient.from_connection_string(self.connection_string) 24 | 25 | def teardown(self): 26 | self.delete_all_rooms() 27 | 28 | def create_room(self): 29 | try: 30 | valid_from = datetime.now(timezone.utc) 31 | valid_until = valid_from + timedelta(weeks=7) 32 | participants = [] 33 | participant_1 = RoomParticipant(communication_identifier=self.user1, role=ParticipantRole.PRESENTER) 34 | participant_2 = RoomParticipant(communication_identifier=self.user2, role=ParticipantRole.CONSUMER) 35 | participant_3 = RoomParticipant(communication_identifier=self.user3, role=ParticipantRole.ATTENDEE) 36 | participants = [participant_1, participant_2, participant_3] 37 | pstn_dial_out_enabled=False 38 | created_room = self.rooms_client.create_room( 39 | valid_from=valid_from, 40 | valid_until=valid_until, 41 | participants=participants, 42 | pstn_dial_out_enabled=pstn_dial_out_enabled 43 | ) 44 | print('\nRoom created.') 45 | self.print_room(created_room) 46 | self.roomsCollection.append(created_room.id) 47 | except Exception as ex: 48 | print('Room creation failed.', ex) 49 | 50 | def update_room(self, room_id:str): 51 | valid_from = datetime.now(timezone.utc) 52 | valid_until = valid_from + timedelta(weeks=1) 53 | pstn_dial_out_enabled = True 54 | try: 55 | updated_room = self.rooms_client.update_room(room_id=room_id, valid_from=valid_from, valid_until=valid_until,pstn_dial_out_enabled=pstn_dial_out_enabled) 56 | print('\nRoom updated with new valid_from and valid_until time.') 57 | self.print_room(updated_room) 58 | except HttpResponseError as ex: 59 | print(ex) 60 | 61 | def get_room(self, room_id:str): 62 | try: 63 | get_room = self.rooms_client.get_room(room_id=room_id) 64 | self.print_room(get_room) 65 | except HttpResponseError as ex: 66 | print(ex) 67 | 68 | def add_or_update_participants(self, room_id:str, participants_list:list): 69 | try: 70 | participants = [] 71 | for p in participants_list: 72 | participants.append(RoomParticipant(communication_identifier=CommunicationUserIdentifier(p), role=ParticipantRole.ATTENDEE)) 73 | self.rooms_client.add_or_update_participants(room_id=room_id, participants=participants) 74 | print('\nRoom participants added or updated.') 75 | except HttpResponseError as ex: 76 | print(ex) 77 | 78 | def list_all_rooms(self): 79 | rooms = self.rooms_client.list_rooms() 80 | print('\nList all active rooms') 81 | print('\nPrinting the first two rooms in list') 82 | count = 0 83 | for room in rooms: 84 | if count == 2: 85 | break 86 | print( 87 | "\nRoom Id: " + room.id + 88 | "\nCreated date time: " + str(room.created_at) + 89 | "\nValid From: " + str(room.valid_from) + "\nValid Until: " + str(room.valid_until)+ 90 | "\nPstn dial out enabled: " + str(room.pstn_dial_out_enabled )) 91 | count += 1 92 | 93 | def delete_all_rooms(self): 94 | for room_id in self.roomsCollection: 95 | print("\nDeleting room : ", room_id) 96 | self.rooms_client.delete_room(room_id) 97 | 98 | def print_room(self, room): 99 | print("\nRoom Id: " + room.id + 100 | "\nCreated date time: " + str(room.created_at) + 101 | "\nValid From: " + str(room.valid_from) + "\nValid Until: " + str(room.valid_until) + "\nPstn dial out enabled: " + str(room.pstn_dial_out_enabled )) 102 | 103 | def get_participants_in_room(self, room_id:str): 104 | participants = self.rooms_client.list_participants(room_id) 105 | print('\nParticipants in Room Id :', room_id) 106 | for p in participants: 107 | print(p.communication_identifier.properties['id'], p.role) 108 | 109 | def remove_participants_from_room(self, room_id:str, participants_list:list): 110 | try: 111 | participants = [] 112 | for p in participants_list: 113 | participants.append(CommunicationUserIdentifier(p)) 114 | self.rooms_client.remove_participants(room_id=room_id, participants=participants) 115 | print('\n(' + str(len(participants)) + ') participants removed from the room : ' + str(room_id)) 116 | except Exception as ex: 117 | print(ex) 118 | 119 | if __name__ == '__main__': 120 | print('==== Started : Rooms API Operations - Python Quickstart Sample ====') 121 | rooms = RoomsQuickstart() 122 | rooms.setup() 123 | rooms.create_room() 124 | rooms.update_room(room_id=rooms.roomsCollection[0]) 125 | rooms.get_room(room_id=rooms.roomsCollection[0]) 126 | rooms.add_or_update_participants(room_id=rooms.roomsCollection[0], participants_list=[rooms.user4.raw_id, rooms.user5.raw_id]) 127 | rooms.list_all_rooms() 128 | rooms.get_participants_in_room(room_id=rooms.roomsCollection[0]) 129 | rooms.remove_participants_from_room(room_id=rooms.roomsCollection[0], participants_list=[rooms.user4.raw_id, rooms.user5.raw_id]) 130 | rooms.get_participants_in_room(room_id=rooms.roomsCollection[0]) 131 | rooms.teardown() 132 | 133 | print('==== Completed : Rooms API Operations - Python Quickstart Sample ====') -------------------------------------------------------------------------------- /call-recording/Readme.mD: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - python 5 | products: 6 | - azure 7 | - azure-communication-callautomation 8 | --- 9 | 10 | # Call Recording APIs Sample 11 | This is a sample application to showcase how the Call Automation SDK can be used to add recording features to any application. 12 | The application is a console based application build using Python 3.9 and above. 13 | It harnesses the power of Azure Communication Services to establish connections and enable communication features within the application. A separate branch with end to end implementation is [available](https://github.com/Azure-Samples/communication-services-web-calling-hero/tree/public-preview). It's a public preview branch and uses beta SDKs that are not meant for production use. Please use the main branch sample for any production scenarios. 14 | 15 | ## Getting started 16 | 17 | ### Prerequisites 18 | 19 | - Create an Azure account with an active subscription. For details, see [Create an account for free](https://azure.microsoft.com/free/) 20 | - Create an Azure Communication Services resource. For details, see [Create an Azure Communication Resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). You'll need to record your resource **connection string** for this sample. 21 | - Get a phone number for your new Azure Communication Services resource. For details, see [Get a phone number](https://docs.microsoft.com/azure/communication-services/quickstarts/telephony-sms/get-phone-number?pivots=platform-azp) 22 | - Download and install [VS Code](https://code.visualstudio.com/download) or [Visual Studio (2022 v17.4.0 and above)](https://visualstudio.microsoft.com/vs/) 23 | -[Python311](https://www.python.org/downloads/) (Make sure to install version that corresponds with your visual studio instance, 32 vs 64 bit) 24 | - Download and install [Ngrok](https://www.ngrok.com/download). As the sample is run locally, Ngrok will enable the receiving of all the events. 25 | - Generate Ngrok Url by using below steps. 26 | - Open command prompt or powershell window. 27 | - Navigate to the directory, where Ngrok.exe file is located. Then, run: 28 | - ngrok http {portNumber}(For e.g. ngrok http 58963) 29 | - Get Ngrok Url generated. Ngrok Url will be in the form of e.g. "https://95b6-43-230-212-228.ngrok-free.app" 30 | - Create a webhook and subscribe to the recording events. For details, see [Create webhook](https://docs.microsoft.com/azure/communication-services/quickstarts/voice-video-calling/download-recording-file-sample) 31 | - **Note:** Phone number is required to successfully run this sample. 32 | 33 | ## Clone the code local and update configuration 34 | 35 | 1. Open an instance of PowerShell, Windows Terminal, Command Prompt or equivalent and navigate to the directory that you'd like to clone the sample to. 36 | 2. Run `git clone https://github.com/Azure-Samples/communication-services-python-quickstarts.git` 37 | 3. Once you get the code on local machine, navigate to **call-recording/config.ini** file found under the call-recording folder. 38 | 4. Update the values for below. 39 | 40 | | Key | Value | Description | 41 | | -------- | -------- | -------- | 42 | | `ACSResourceConnectionString` | \ | Input your ACS connection string in the variable | 43 | | `ACSAcquiredPhoneNumber` | \ | Phone number associated with the Azure Communication Service resource | 44 | | `CallbackUri` | \ | Base url of the app, don't add `/` at end. generate ngrok url as mentioned above. | 45 | 46 | 47 | ## Code structure 48 | 49 | - *./call-recording/Controller/RecordingsController.py* : Server app core logic for calling the recording APIs using Azure Communication Services callautomation SDK 50 | - *./call-recording/App.py* : Entry point for the server app program logic 51 | - *./call-recording/requirements.txt* : Contains dependencies for running and deploying the application 52 | 53 | ### Setup the Python environment 54 | 55 | Create and activate python virtual environment and install required packages using following command in `call-recording` folder. 56 | ``` 57 | pip install -r requirements.txt 58 | ``` 59 | 60 | ## Run app locally 61 | 62 | 1. Navigate to `call-recording` folder and run `App.py` in debug mode or use command `python ./App.py` to run it from PowerShell, Command Prompt or Unix Terminal 63 | 2. Import `call-recording\data\Recordings API.postman_collection.json` file into postman, you get `Recordings API` collection. For details, see [Importing and exporting data](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/) 64 | 3. Select `Recordings API`, under Variables, update `HOST_URL` and `TARGET_PHONE_NUMBER` before making calls. 65 | 66 | # Step by step guid for testing recording APIs via postman. 67 | 68 | Once App is running locally, 69 | 1. Create webhook (Follow "Create Webhook for Microsoft.Communication.RecordingFileStatus event" section below ) 70 | 2. Start a call by invoking Qutbound Call request. 71 | - Accept the call on Target PSTN Phone number, Keep call running. 72 | 5. Send Request to StartRecording. 73 | - Recording would be started. 74 | 6. (Optional) PauseRecording, ResumeRecording and GetRecordingState. 75 | 7. Send Request for StopRecording. 76 | 8. Send Request for DownloadRecording from server, only last recorded file will be downloaded. 77 | 9. Send Request for DeleteRecording at server. 78 | 79 | ## Create Webhook for Microsoft.Communication.RecordingFileStatus event 80 | Call Recording enables you to record multiple calling scenarios available in Azure Communication Services by providing you with a set of APIs to start, stop, pause and resume recording. To learn more about it, see [this guide](https://learn.microsoft.com/en-us/azure/communication-services/concepts/voice-video-calling/call-recording). 81 | 1. Navigate to your Communication Service resource on Azure portal and select `Events` from the left side blade. 82 | 2. Click `+ Event Subscription` to create a new subscription, provide `Name` field value. 83 | 3. Under Topic details, choose a System Topic or create new, no changes required if its already have topic name. 84 | 4. Under `Event Types` Filter for `Recording File Status Updated(Preview)` event. 85 | 5. Choose `Endpoint Type` as `Web Hook` and provide the public url generated by Ngrok. It would look like `https://2c0a-49-207-209-111.ngrok-free.app/recordingFileStatus`. 86 | 6. Click `Create` to complete the event grid subscription. The subscription is ready when the provisioning status is marked as succeeded. 87 | **Note:** Application should be running to able to create the `Web Hook` successfully. 88 | -------------------------------------------------------------------------------- /callautomation-azure-openai-voice/main.py: -------------------------------------------------------------------------------- 1 | from quart import Quart, Response, request, json, websocket 2 | from azure.eventgrid import EventGridEvent, SystemEventNames 3 | from urllib.parse import urlencode, urlparse, urlunparse 4 | from logging import INFO 5 | from azure.communication.callautomation import ( 6 | MediaStreamingOptions, 7 | AudioFormat, 8 | MediaStreamingContentType, 9 | MediaStreamingAudioChannelType, 10 | StreamingTransportType 11 | ) 12 | from azure.communication.callautomation.aio import ( 13 | CallAutomationClient 14 | ) 15 | import uuid 16 | 17 | from azureOpenAIService import OpenAIRTHandler 18 | 19 | # Your ACS resource connection string 20 | ACS_CONNECTION_STRING = "ACS_CONNECTION_STRING" 21 | 22 | # Callback events URI to handle callback events. 23 | CALLBACK_URI_HOST = "CALLBACK_URI_HOST" 24 | CALLBACK_EVENTS_URI = CALLBACK_URI_HOST + "/api/callbacks" 25 | 26 | acs_client = CallAutomationClient.from_connection_string(ACS_CONNECTION_STRING) 27 | app = Quart(__name__) 28 | 29 | @app.route("/api/incomingCall", methods=['POST']) 30 | async def incoming_call_handler(): 31 | app.logger.info("incoming event data") 32 | for event_dict in await request.json: 33 | event = EventGridEvent.from_dict(event_dict) 34 | app.logger.info("incoming event data --> %s", event.data) 35 | if event.event_type == SystemEventNames.EventGridSubscriptionValidationEventName: 36 | app.logger.info("Validating subscription") 37 | validation_code = event.data['validationCode'] 38 | validation_response = {'validationResponse': validation_code} 39 | return Response(response=json.dumps(validation_response), status=200) 40 | elif event.event_type =="Microsoft.Communication.IncomingCall": 41 | app.logger.info("Incoming call received: data=%s", 42 | event.data) 43 | if event.data['from']['kind'] =="phoneNumber": 44 | caller_id = event.data['from']["phoneNumber"]["value"] 45 | else : 46 | caller_id = event.data['from']['rawId'] 47 | app.logger.info("incoming call handler caller id: %s", 48 | caller_id) 49 | incoming_call_context=event.data['incomingCallContext'] 50 | guid =uuid.uuid4() 51 | query_parameters = urlencode({"callerId": caller_id}) 52 | callback_uri = f"{CALLBACK_EVENTS_URI}/{guid}?{query_parameters}" 53 | 54 | parsed_url = urlparse(CALLBACK_EVENTS_URI) 55 | websocket_url = urlunparse(('wss',parsed_url.netloc,'/ws','', '', '')) 56 | 57 | app.logger.info("callback url: %s", callback_uri) 58 | app.logger.info("websocket url: %s", websocket_url) 59 | 60 | media_streaming_options = MediaStreamingOptions( 61 | transport_url=websocket_url, 62 | transport_type=StreamingTransportType.WEBSOCKET, 63 | content_type=MediaStreamingContentType.AUDIO, 64 | audio_channel_type=MediaStreamingAudioChannelType.MIXED, 65 | start_media_streaming=True, 66 | enable_bidirectional=True, 67 | audio_format=AudioFormat.PCM24_K_MONO) 68 | 69 | answer_call_result = await acs_client.answer_call(incoming_call_context=incoming_call_context, 70 | operation_context="incomingCall", 71 | callback_url=callback_uri, 72 | media_streaming=media_streaming_options) 73 | app.logger.info("Answered call for connection id: %s", 74 | answer_call_result.call_connection_id) 75 | return Response(status=200) 76 | 77 | @app.route('/api/callbacks/', methods=['POST']) 78 | async def callbacks(contextId): 79 | for event in await request.json: 80 | # Parsing callback events 81 | global call_connection_id 82 | event_data = event['data'] 83 | call_connection_id = event_data["callConnectionId"] 84 | app.logger.info(f"Received Event:-> {event['type']}, Correlation Id:-> {event_data['correlationId']}, CallConnectionId:-> {call_connection_id}") 85 | if event['type'] == "Microsoft.Communication.CallConnected": 86 | call_connection_properties = await acs_client.get_call_connection(call_connection_id).get_call_properties() 87 | media_streaming_subscription = call_connection_properties.media_streaming_subscription 88 | app.logger.info(f"MediaStreamingSubscription:--> {media_streaming_subscription}") 89 | app.logger.info(f"Received CallConnected event for connection id: {call_connection_id}") 90 | app.logger.info("CORRELATION ID:--> %s", event_data["correlationId"]) 91 | app.logger.info("CALL CONNECTION ID:--> %s", event_data["callConnectionId"]) 92 | elif event['type'] == "Microsoft.Communication.MediaStreamingStarted": 93 | app.logger.info(f"Media streaming content type:--> {event_data['mediaStreamingUpdate']['contentType']}") 94 | app.logger.info(f"Media streaming status:--> {event_data['mediaStreamingUpdate']['mediaStreamingStatus']}") 95 | app.logger.info(f"Media streaming status details:--> {event_data['mediaStreamingUpdate']['mediaStreamingStatusDetails']}") 96 | elif event['type'] == "Microsoft.Communication.MediaStreamingStopped": 97 | app.logger.info(f"Media streaming content type:--> {event_data['mediaStreamingUpdate']['contentType']}") 98 | app.logger.info(f"Media streaming status:--> {event_data['mediaStreamingUpdate']['mediaStreamingStatus']}") 99 | app.logger.info(f"Media streaming status details:--> {event_data['mediaStreamingUpdate']['mediaStreamingStatusDetails']}") 100 | elif event['type'] == "Microsoft.Communication.MediaStreamingFailed": 101 | app.logger.info(f"Code:->{event_data['resultInformation']['code']}, Subcode:-> {event_data['resultInformation']['subCode']}") 102 | app.logger.info(f"Message:->{event_data['resultInformation']['message']}") 103 | elif event['type'] == "Microsoft.Communication.CallDisconnected": 104 | pass 105 | return Response(status=200) 106 | 107 | # WebSocket. 108 | @app.websocket('/ws') 109 | async def ws(): 110 | handler = OpenAIRTHandler() 111 | print("Client connected to WebSocket") 112 | await handler.init_incoming_websocket(websocket) 113 | await handler.start_client() 114 | while websocket: 115 | try: 116 | # Receive data from the client 117 | data = await websocket.receive() 118 | await handler.acs_to_oai(data) 119 | await handler.send_welcome() 120 | except Exception as e: 121 | print(f"WebSocket connection closed: {e}") 122 | break 123 | 124 | @app.route('/') 125 | def home(): 126 | return 'Hello ACS CallAutomation!' 127 | 128 | if __name__ == '__main__': 129 | app.logger.setLevel(INFO) 130 | app.run(port=8000) 131 | -------------------------------------------------------------------------------- /call-recording/data/Recordings API.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "be622dd7-585a-4b41-b10e-750a9d1ad2f8", 4 | "name": "Recordings API", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "1437196" 7 | }, 8 | "item": [ 9 | { 10 | "name": "Outbound Call", 11 | "protocolProfileBehavior": { 12 | "disableBodyPruning": true, 13 | "disabledSystemHeaders": {} 14 | }, 15 | "request": { 16 | "method": "GET", 17 | "header": [], 18 | "body": { 19 | "mode": "raw", 20 | "raw": "", 21 | "options": { 22 | "raw": { 23 | "language": "json" 24 | } 25 | } 26 | }, 27 | "url": { 28 | "raw": "{{HOST_URL}}/outboundCall?targetPhoneNumber={{TARGET_PHONE_NUMBER}}", 29 | "host": [ 30 | "{{HOST_URL}}" 31 | ], 32 | "path": [ 33 | "outboundCall" 34 | ], 35 | "query": [ 36 | { 37 | "key": "targetPhoneNumber", 38 | "value": "{{TARGET_PHONE_NUMBER}}" 39 | } 40 | ] 41 | } 42 | }, 43 | "response": [] 44 | }, 45 | { 46 | "name": "Start recording", 47 | "protocolProfileBehavior": { 48 | "disableBodyPruning": true, 49 | "disabledSystemHeaders": {} 50 | }, 51 | "request": { 52 | "method": "GET", 53 | "header": [], 54 | "body": { 55 | "mode": "raw", 56 | "raw": "", 57 | "options": { 58 | "raw": { 59 | "language": "json" 60 | } 61 | } 62 | }, 63 | "url": { 64 | "raw": "{{HOST_URL}}/startRecording?serverCallId=", 65 | "host": [ 66 | "{{HOST_URL}}" 67 | ], 68 | "path": [ 69 | "startRecording" 70 | ], 71 | "query": [ 72 | { 73 | "key": "serverCallId", 74 | "value": "", 75 | "description": "Optional, if not passed, it will use last OutboundCall serverCallId" 76 | } 77 | ] 78 | } 79 | }, 80 | "response": [] 81 | }, 82 | { 83 | "name": "Pause recording", 84 | "protocolProfileBehavior": { 85 | "disableBodyPruning": true, 86 | "disabledSystemHeaders": {} 87 | }, 88 | "request": { 89 | "method": "GET", 90 | "header": [], 91 | "body": { 92 | "mode": "raw", 93 | "raw": "", 94 | "options": { 95 | "raw": { 96 | "language": "json" 97 | } 98 | } 99 | }, 100 | "url": { 101 | "raw": "{{HOST_URL}}/pauseRecording?recordingId=", 102 | "host": [ 103 | "{{HOST_URL}}" 104 | ], 105 | "path": [ 106 | "pauseRecording" 107 | ], 108 | "query": [ 109 | { 110 | "key": "recordingId", 111 | "value": "", 112 | "description": "Optional, if not passed, it will use current active recording Id" 113 | } 114 | ] 115 | } 116 | }, 117 | "response": [] 118 | }, 119 | { 120 | "name": "Get recording state", 121 | "protocolProfileBehavior": { 122 | "disableBodyPruning": true, 123 | "disabledSystemHeaders": {} 124 | }, 125 | "request": { 126 | "method": "GET", 127 | "header": [], 128 | "body": { 129 | "mode": "raw", 130 | "raw": "", 131 | "options": { 132 | "raw": { 133 | "language": "json" 134 | } 135 | } 136 | }, 137 | "url": { 138 | "raw": "{{HOST_URL}}/getRecordingState?recordingId=", 139 | "host": [ 140 | "{{HOST_URL}}" 141 | ], 142 | "path": [ 143 | "getRecordingState" 144 | ], 145 | "query": [ 146 | { 147 | "key": "recordingId", 148 | "value": "", 149 | "description": "Optional, if not passed, it will use current active recording Id" 150 | } 151 | ] 152 | } 153 | }, 154 | "response": [] 155 | }, 156 | { 157 | "name": "Resume recording", 158 | "protocolProfileBehavior": { 159 | "disableBodyPruning": true, 160 | "disabledSystemHeaders": {} 161 | }, 162 | "request": { 163 | "method": "GET", 164 | "header": [], 165 | "body": { 166 | "mode": "raw", 167 | "raw": "", 168 | "options": { 169 | "raw": { 170 | "language": "json" 171 | } 172 | } 173 | }, 174 | "url": { 175 | "raw": "{{HOST_URL}}/resumeRecording?recordingId=", 176 | "host": [ 177 | "{{HOST_URL}}" 178 | ], 179 | "path": [ 180 | "resumeRecording" 181 | ], 182 | "query": [ 183 | { 184 | "key": "recordingId", 185 | "value": "", 186 | "description": "Optional, if not passed, it will use current active recording Id" 187 | } 188 | ] 189 | } 190 | }, 191 | "response": [] 192 | }, 193 | { 194 | "name": "Stop recording", 195 | "protocolProfileBehavior": { 196 | "disabledSystemHeaders": {} 197 | }, 198 | "request": { 199 | "method": "DELETE", 200 | "header": [], 201 | "body": { 202 | "mode": "raw", 203 | "raw": "", 204 | "options": { 205 | "raw": { 206 | "language": "json" 207 | } 208 | } 209 | }, 210 | "url": { 211 | "raw": "{{HOST_URL}}/stopRecording?recordingId=", 212 | "host": [ 213 | "{{HOST_URL}}" 214 | ], 215 | "path": [ 216 | "stopRecording" 217 | ], 218 | "query": [ 219 | { 220 | "key": "recordingId", 221 | "value": "", 222 | "description": "Optional, if not passed, it will use current active recording Id" 223 | } 224 | ] 225 | } 226 | }, 227 | "response": [] 228 | }, 229 | { 230 | "name": "Download recording", 231 | "protocolProfileBehavior": { 232 | "disableBodyPruning": true, 233 | "disabledSystemHeaders": {} 234 | }, 235 | "request": { 236 | "method": "GET", 237 | "header": [], 238 | "body": { 239 | "mode": "raw", 240 | "raw": "", 241 | "options": { 242 | "raw": { 243 | "language": "json" 244 | } 245 | } 246 | }, 247 | "url": { 248 | "raw": "{{HOST_URL}}/downloadRecording", 249 | "host": [ 250 | "{{HOST_URL}}" 251 | ], 252 | "path": [ 253 | "downloadRecording" 254 | ] 255 | } 256 | }, 257 | "response": [] 258 | }, 259 | { 260 | "name": "Delete recording", 261 | "protocolProfileBehavior": { 262 | "disabledSystemHeaders": {} 263 | }, 264 | "request": { 265 | "method": "DELETE", 266 | "header": [], 267 | "body": { 268 | "mode": "raw", 269 | "raw": "", 270 | "options": { 271 | "raw": { 272 | "language": "json" 273 | } 274 | } 275 | }, 276 | "url": { 277 | "raw": "{{HOST_URL}}/deleteRecording", 278 | "host": [ 279 | "{{HOST_URL}}" 280 | ], 281 | "path": [ 282 | "deleteRecording" 283 | ] 284 | } 285 | }, 286 | "response": [] 287 | } 288 | ], 289 | "event": [ 290 | { 291 | "listen": "prerequest", 292 | "script": { 293 | "type": "text/javascript", 294 | "exec": [ 295 | "" 296 | ] 297 | } 298 | }, 299 | { 300 | "listen": "test", 301 | "script": { 302 | "type": "text/javascript", 303 | "exec": [ 304 | "" 305 | ] 306 | } 307 | } 308 | ], 309 | "variable": [ 310 | { 311 | "key": "HOST_URL", 312 | "value": "", 313 | "type": "string" 314 | }, 315 | { 316 | "key": "TARGET_PHONE_NUMBER", 317 | "value": "", 318 | "type": "string" 319 | } 320 | ] 321 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # Python virtual environment 7 | */venv/ 8 | 9 | # User-specific files 10 | *.rsuser 11 | *.suo 12 | *.user 13 | *.userosscache 14 | *.sln.docstates 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Mono auto generated files 20 | mono_crash.* 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | [Aa][Rr][Mm]/ 30 | [Aa][Rr][Mm]64/ 31 | bld/ 32 | [Bb]in/ 33 | [Oo]bj/ 34 | [Ll]og/ 35 | [Ll]ogs/ 36 | 37 | # Visual Studio 2015/2017 cache/options directory 38 | .vs/ 39 | # Uncomment if you have tasks that create the project's static files in wwwroot 40 | #wwwroot/ 41 | 42 | # Visual Studio 2017 auto generated files 43 | Generated\ Files/ 44 | 45 | # MSTest test Results 46 | [Tt]est[Rr]esult*/ 47 | [Bb]uild[Ll]og.* 48 | 49 | # NUnit 50 | *.VisualState.xml 51 | TestResult.xml 52 | nunit-*.xml 53 | 54 | # Build Results of an ATL Project 55 | [Dd]ebugPS/ 56 | [Rr]eleasePS/ 57 | dlldata.c 58 | 59 | # Benchmark Results 60 | BenchmarkDotNet.Artifacts/ 61 | 62 | # .NET Core 63 | project.lock.json 64 | project.fragment.lock.json 65 | artifacts/ 66 | 67 | # StyleCop 68 | StyleCopReport.xml 69 | 70 | # Files built by Visual Studio 71 | *_i.c 72 | *_p.c 73 | *_h.h 74 | *.ilk 75 | *.meta 76 | *.obj 77 | *.iobj 78 | *.pch 79 | *.pdb 80 | *.ipdb 81 | *.pgc 82 | *.pgd 83 | *.rsp 84 | *.sbr 85 | *.tlb 86 | *.tli 87 | *.tlh 88 | *.tmp 89 | *.tmp_proj 90 | *_wpftmp.csproj 91 | *.log 92 | *.vspscc 93 | *.vssscc 94 | .builds 95 | *.pidb 96 | *.svclog 97 | *.scc 98 | 99 | # Chutzpah Test files 100 | _Chutzpah* 101 | 102 | # Visual C++ cache files 103 | ipch/ 104 | *.aps 105 | *.ncb 106 | *.opendb 107 | *.opensdf 108 | *.sdf 109 | *.cachefile 110 | *.VC.db 111 | *.VC.VC.opendb 112 | 113 | # Visual Studio profiler 114 | *.psess 115 | *.vsp 116 | *.vspx 117 | *.sap 118 | 119 | # Visual Studio Trace Files 120 | *.e2e 121 | 122 | # TFS 2012 Local Workspace 123 | $tf/ 124 | 125 | # Guidance Automation Toolkit 126 | *.gpState 127 | 128 | # ReSharper is a .NET coding add-in 129 | _ReSharper*/ 130 | *.[Rr]e[Ss]harper 131 | *.DotSettings.user 132 | 133 | # TeamCity is a build add-in 134 | _TeamCity* 135 | 136 | # DotCover is a Code Coverage Tool 137 | *.dotCover 138 | 139 | # AxoCover is a Code Coverage Tool 140 | .axoCover/* 141 | !.axoCover/settings.json 142 | 143 | # Visual Studio code coverage results 144 | *.coverage 145 | *.coveragexml 146 | 147 | # NCrunch 148 | _NCrunch_* 149 | .*crunch*.local.xml 150 | nCrunchTemp_* 151 | 152 | # MightyMoose 153 | *.mm.* 154 | AutoTest.Net/ 155 | 156 | # Web workbench (sass) 157 | .sass-cache/ 158 | 159 | # Installshield output folder 160 | [Ee]xpress/ 161 | 162 | # DocProject is a documentation generator add-in 163 | DocProject/buildhelp/ 164 | DocProject/Help/*.HxT 165 | DocProject/Help/*.HxC 166 | DocProject/Help/*.hhc 167 | DocProject/Help/*.hhk 168 | DocProject/Help/*.hhp 169 | DocProject/Help/Html2 170 | DocProject/Help/html 171 | 172 | # Click-Once directory 173 | publish/ 174 | 175 | # Publish Web Output 176 | *.[Pp]ublish.xml 177 | *.azurePubxml 178 | # Note: Comment the next line if you want to checkin your web deploy settings, 179 | # but database connection strings (with potential passwords) will be unencrypted 180 | *.pubxml 181 | *.publishproj 182 | 183 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 184 | # checkin your Azure Web App publish settings, but sensitive information contained 185 | # in these scripts will be unencrypted 186 | PublishScripts/ 187 | 188 | # NuGet Packages 189 | *.nupkg 190 | # NuGet Symbol Packages 191 | *.snupkg 192 | # The packages folder can be ignored because of Package Restore 193 | **/[Pp]ackages/* 194 | # except build/, which is used as an MSBuild target. 195 | !**/[Pp]ackages/build/ 196 | # Uncomment if necessary however generally it will be regenerated when needed 197 | #!**/[Pp]ackages/repositories.config 198 | # NuGet v3's project.json files produces more ignorable files 199 | *.nuget.props 200 | *.nuget.targets 201 | 202 | # Microsoft Azure Build Output 203 | csx/ 204 | *.build.csdef 205 | 206 | # Microsoft Azure Emulator 207 | ecf/ 208 | rcf/ 209 | 210 | # Windows Store app package directories and files 211 | AppPackages/ 212 | BundleArtifacts/ 213 | Package.StoreAssociation.xml 214 | _pkginfo.txt 215 | *.appx 216 | *.appxbundle 217 | *.appxupload 218 | 219 | # Visual Studio cache files 220 | # files ending in .cache can be ignored 221 | *.[Cc]ache 222 | # but keep track of directories ending in .cache 223 | !?*.[Cc]ache/ 224 | 225 | # Others 226 | ClientBin/ 227 | ~$* 228 | *~ 229 | *.dbmdl 230 | *.dbproj.schemaview 231 | *.jfm 232 | *.pfx 233 | *.publishsettings 234 | orleans.codegen.cs 235 | 236 | # Including strong name files can present a security risk 237 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 238 | #*.snk 239 | 240 | # Since there are multiple workflows, uncomment next line to ignore bower_components 241 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 242 | #bower_components/ 243 | 244 | # RIA/Silverlight projects 245 | Generated_Code/ 246 | 247 | # Backup & report files from converting an old project file 248 | # to a newer Visual Studio version. Backup files are not needed, 249 | # because we have git ;-) 250 | _UpgradeReport_Files/ 251 | Backup*/ 252 | UpgradeLog*.XML 253 | UpgradeLog*.htm 254 | ServiceFabricBackup/ 255 | *.rptproj.bak 256 | 257 | # SQL Server files 258 | *.mdf 259 | *.ldf 260 | *.ndf 261 | 262 | # Business Intelligence projects 263 | *.rdl.data 264 | *.bim.layout 265 | *.bim_*.settings 266 | *.rptproj.rsuser 267 | *- [Bb]ackup.rdl 268 | *- [Bb]ackup ([0-9]).rdl 269 | *- [Bb]ackup ([0-9][0-9]).rdl 270 | 271 | # Microsoft Fakes 272 | FakesAssemblies/ 273 | 274 | # GhostDoc plugin setting file 275 | *.GhostDoc.xml 276 | 277 | # Node.js Tools for Visual Studio 278 | .ntvs_analysis.dat 279 | node_modules/ 280 | 281 | # Visual Studio 6 build log 282 | *.plg 283 | 284 | # Visual Studio 6 workspace options file 285 | *.opt 286 | 287 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 288 | *.vbw 289 | 290 | # Visual Studio LightSwitch build output 291 | **/*.HTMLClient/GeneratedArtifacts 292 | **/*.DesktopClient/GeneratedArtifacts 293 | **/*.DesktopClient/ModelManifest.xml 294 | **/*.Server/GeneratedArtifacts 295 | **/*.Server/ModelManifest.xml 296 | _Pvt_Extensions 297 | 298 | # Paket dependency manager 299 | .paket/paket.exe 300 | paket-files/ 301 | 302 | # FAKE - F# Make 303 | .fake/ 304 | 305 | # CodeRush personal settings 306 | .cr/personal 307 | 308 | # Python Tools for Visual Studio (PTVS) 309 | __pycache__/ 310 | *.pyc 311 | 312 | # Cake - Uncomment if you are using it 313 | # tools/** 314 | # !tools/packages.config 315 | 316 | # Tabs Studio 317 | *.tss 318 | 319 | # Telerik's JustMock configuration file 320 | *.jmconfig 321 | 322 | # BizTalk build output 323 | *.btp.cs 324 | *.btm.cs 325 | *.odx.cs 326 | *.xsd.cs 327 | 328 | # OpenCover UI analysis results 329 | OpenCover/ 330 | 331 | # Azure Stream Analytics local run output 332 | ASALocalRun/ 333 | 334 | # MSBuild Binary and Structured Log 335 | *.binlog 336 | 337 | # NVidia Nsight GPU debugger configuration file 338 | *.nvuser 339 | 340 | # MFractors (Xamarin productivity tool) working folder 341 | .mfractor/ 342 | 343 | # Local History for Visual Studio 344 | .localhistory/ 345 | 346 | # BeatPulse healthcheck temp database 347 | healthchecksdb 348 | 349 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 350 | MigrationBackup/ 351 | 352 | # Ionide (cross platform F# VS Code tools) working folder 353 | .ionide/ 354 | -------------------------------------------------------------------------------- /callautomation-azure-openai-voice/azureOpenAIService.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from openai import AsyncAzureOpenAI 4 | 5 | import asyncio 6 | import json 7 | import random 8 | 9 | AZURE_OPENAI_API_ENDPOINT = '' 10 | AZURE_OPENAI_API_VERSION = "2024-10-01-preview" 11 | AZURE_OPENAI_API_KEY = '' 12 | AZURE_OPENAI_DEPLOYMENT_NAME = '' 13 | SAMPLE_RATE = 24000 14 | 15 | def session_config(): 16 | """Returns a random value from the predefined list.""" 17 | values = ['alloy', 'ash', 'ballad', 'coral', 'echo', 'sage', 'shimmer', 'verse'] 18 | ### for details on available param: https://platform.openai.com/docs/api-reference/realtime-sessions/create 19 | SESSION_CONFIG={ 20 | "input_audio_transcription": { 21 | "model": "whisper-1", 22 | }, 23 | "turn_detection": { 24 | "threshold": 0.4, 25 | "silence_duration_ms": 600, 26 | "type": "server_vad" 27 | }, 28 | "instructions": "Your name is Sam, you work for Contoso Services. You're a helpful, calm and cheerful agent who responds with a clam British accent, but also can speak in any language or accent. Always start the conversation with a cheery hello, stating your name and who do you work for!", 29 | "voice": random.choice(values), 30 | "modalities": ["text", "audio"] ## required to solicit the initial welcome message 31 | } 32 | return SESSION_CONFIG 33 | 34 | class OpenAIRTHandler(): 35 | incoming_websocket = None 36 | client = None 37 | connection = None 38 | connection_manager = None 39 | welcomed = False 40 | 41 | def __init__(self) -> None: 42 | print("Hello World") 43 | self.client = AsyncAzureOpenAI( 44 | azure_endpoint=AZURE_OPENAI_API_ENDPOINT, 45 | azure_deployment=AZURE_OPENAI_DEPLOYMENT_NAME, 46 | api_key=AZURE_OPENAI_API_KEY, 47 | api_version=AZURE_OPENAI_API_VERSION, 48 | ) 49 | self.connection_manager = self.client.beta.realtime.connect( 50 | model="gpt-4o-realtime-preview" # Replace with your deployed realtime model id on Azure OpenAI. 51 | ) 52 | 53 | def __exit__(self, exc_type, exc_value, traceback): 54 | self.connection.close() 55 | self.incoming_websocket.close() 56 | 57 | #start_conversation > start_client 58 | async def start_client(self): 59 | self.connection = await self.connection_manager.enter() 60 | await self.connection.session.update(session=session_config()) 61 | await self.connection.response.create() 62 | ### running an async task to listen and recieve oai messages 63 | asyncio.create_task(self.receive_oai_messages()) 64 | 65 | #send_audio_to_external_ai > audio_to_oai 66 | async def audio_to_oai(self, audioData: str): 67 | await self.connection.input_audio_buffer.append(audio=audioData) 68 | 69 | #receive_messages > receive_oai_messages 70 | async def receive_oai_messages(self): 71 | #while not self.connection._connection.close_code: 72 | async for event in self.connection: 73 | #print(event) 74 | if event is None: 75 | continue 76 | match event.type: 77 | case "session.created": 78 | print("Session Created Message") 79 | print(f" Session Id: {event.session.id}") 80 | pass 81 | case "error": 82 | print(f" Error: {event.error}") 83 | pass 84 | case "input_audio_buffer.cleared": 85 | print("Input Audio Buffer Cleared Message") 86 | pass 87 | case "input_audio_buffer.speech_started": 88 | print(f"Voice activity detection started at {event.audio_start_ms} [ms]") 89 | await self.stop_audio() 90 | pass 91 | case "input_audio_buffer.speech_stopped": 92 | pass 93 | case "conversation.item.input_audio_transcription.completed": 94 | print(f" User:-- {event.transcript}") 95 | case "conversation.item.input_audio_transcription.failed": 96 | print(f" Error: {event.error}") 97 | case "response.done": 98 | print("Response Done Message") 99 | print(f" Response Id: {event.response.id}") 100 | if event.response.status_details: 101 | print(f" Status Details: {event.response.status_details.model_dump_json()}") 102 | case "response.audio_transcript.done": 103 | print(f" AI:-- {event.transcript}") 104 | case "response.audio.delta": 105 | await self.oai_to_acs(event.delta) 106 | pass 107 | case _: 108 | pass 109 | 110 | #init_websocket -> init_incoming_websocket (incoming) 111 | async def init_incoming_websocket(self, socket): 112 | # print("--inbound socket set") 113 | self.incoming_websocket = socket 114 | 115 | #receive_audio_for_outbound > oai_to_acs 116 | async def oai_to_acs(self, data): 117 | try: 118 | data = { 119 | "Kind": "AudioData", 120 | "AudioData": { 121 | "Data": data 122 | }, 123 | "StopAudio": None 124 | } 125 | 126 | # Serialize the server streaming data 127 | serialized_data = json.dumps(data) 128 | await self.send_message(serialized_data) 129 | 130 | except Exception as e: 131 | print(e) 132 | 133 | # stop oai talking when detecting the user talking 134 | async def stop_audio(self): 135 | stop_audio_data = { 136 | "Kind": "StopAudio", 137 | "AudioData": None, 138 | "StopAudio": {} 139 | } 140 | 141 | json_data = json.dumps(stop_audio_data) 142 | await self.send_message(json_data) 143 | 144 | # send_message > send_message 145 | async def send_message(self, message: str): 146 | try: 147 | await self.incoming_websocket.send(message) 148 | except Exception as e: 149 | print(f"Failed to send message: {e}") 150 | 151 | async def send_welcome(self): 152 | if not self.welcomed: 153 | await self.connection.conversation.item.create( 154 | item={ 155 | "type": "message", 156 | "role": "user", 157 | "content": [{"type": "input_text", "text": "Hi! What's your name and who do you work for?"}], 158 | } 159 | ) 160 | await self.connection.response.create() 161 | self.welcomed = True 162 | 163 | #mediaStreamingHandler.process_websocket_message_async -> acs_to_oai 164 | async def acs_to_oai(self, stream_data): 165 | try: 166 | data = json.loads(stream_data) 167 | kind = data['kind'] 168 | if kind == "AudioData": 169 | audio_data_section = data.get("audioData", {}) 170 | if not audio_data_section.get("silent", True): 171 | audio_data = audio_data_section.get("data") 172 | await self.audio_to_oai(audio_data) 173 | except Exception as e: 174 | print(f'Error processing WebSocket message: {e}') 175 | -------------------------------------------------------------------------------- /callautomation-outboundcalling/main.py: -------------------------------------------------------------------------------- 1 | from azure.eventgrid import EventGridEvent, SystemEventNames 2 | from flask import Flask, Response, request, json, send_file, render_template, redirect 3 | from logging import INFO 4 | from azure.communication.callautomation import ( 5 | CallAutomationClient, 6 | CallConnectionClient, 7 | PhoneNumberIdentifier, 8 | RecognizeInputType, 9 | MicrosoftTeamsUserIdentifier, 10 | CallInvite, 11 | RecognitionChoice, 12 | DtmfTone, 13 | TextSource) 14 | from azure.core.messaging import CloudEvent 15 | 16 | # Your ACS resource connection string 17 | ACS_CONNECTION_STRING = "" 18 | 19 | # Your ACS resource phone number will act as source number to start outbound call 20 | ACS_PHONE_NUMBER = "" 21 | 22 | # Target phone number you want to receive the call. 23 | TARGET_PHONE_NUMBER = "" 24 | 25 | # Callback events URI to handle callback events. 26 | CALLBACK_URI_HOST = "" 27 | CALLBACK_EVENTS_URI = CALLBACK_URI_HOST + "/api/callbacks" 28 | COGNITIVE_SERVICES_ENDPOINT = "" 29 | 30 | #(OPTIONAL) Your target Microsoft Teams user Id ex. "ab01bc12-d457-4995-a27b-c405ecfe4870" 31 | TARGET_TEAMS_USER_ID = "" 32 | 33 | TEMPLATE_FILES_PATH = "template" 34 | 35 | # Prompts for text to speech 36 | SPEECH_TO_TEXT_VOICE = "en-US-NancyNeural" 37 | MAIN_MENU = "Hello this is Contoso Bank, we’re calling in regard to your appointment tomorrow at 9am to open a new account. Please say confirm if this time is still suitable for you or say cancel if you would like to cancel this appointment." 38 | CONFIRMED_TEXT = "Thank you for confirming your appointment tomorrow at 9am, we look forward to meeting with you." 39 | CANCEL_TEXT = "Your appointment tomorrow at 9am has been cancelled. Please call the bank directly if you would like to rebook for another date and time." 40 | CUSTOMER_QUERY_TIMEOUT = "I’m sorry I didn’t receive a response, please try again." 41 | NO_RESPONSE = "I didn't receive an input, we will go ahead and confirm your appointment. Goodbye" 42 | INVALID_AUDIO = "I’m sorry, I didn’t understand your response, please try again." 43 | CONFIRM_CHOICE_LABEL = "Confirm" 44 | CANCEL_CHOICE_LABEL = "Cancel" 45 | RETRY_CONTEXT = "retry" 46 | 47 | call_automation_client = CallAutomationClient.from_connection_string(ACS_CONNECTION_STRING) 48 | 49 | app = Flask(__name__, 50 | template_folder=TEMPLATE_FILES_PATH) 51 | 52 | def get_choices(): 53 | choices = [ 54 | RecognitionChoice(label = CONFIRM_CHOICE_LABEL, phrases= ["Confirm", "First", "One"], tone = DtmfTone.ONE), 55 | RecognitionChoice(label = CANCEL_CHOICE_LABEL, phrases= ["Cancel", "Second", "Two"], tone = DtmfTone.TWO) 56 | ] 57 | return choices 58 | 59 | def get_media_recognize_choice_options(call_connection_client: CallConnectionClient, text_to_play: str, target_participant:str, choices: any, context: str): 60 | play_source = TextSource (text= text_to_play, voice_name= SPEECH_TO_TEXT_VOICE) 61 | call_connection_client.start_recognizing_media( 62 | input_type=RecognizeInputType.CHOICES, 63 | target_participant=target_participant, 64 | choices=choices, 65 | play_prompt=play_source, 66 | interrupt_prompt=False, 67 | initial_silence_timeout=10, 68 | operation_context=context 69 | ) 70 | 71 | def handle_play(call_connection_client: CallConnectionClient, text_to_play: str): 72 | play_source = TextSource(text=text_to_play, voice_name=SPEECH_TO_TEXT_VOICE) 73 | call_connection_client.play_media_to_all(play_source) 74 | 75 | # GET endpoint to place phone call 76 | @app.route('/outboundCall') 77 | def outbound_call_handler(): 78 | target_participant = PhoneNumberIdentifier(TARGET_PHONE_NUMBER) 79 | source_caller = PhoneNumberIdentifier(ACS_PHONE_NUMBER) 80 | call_connection_properties = call_automation_client.create_call(target_participant, 81 | CALLBACK_EVENTS_URI, 82 | cognitive_services_endpoint=COGNITIVE_SERVICES_ENDPOINT, 83 | source_caller_id_number=source_caller) 84 | app.logger.info("Created call with connection id: %s", call_connection_properties.call_connection_id) 85 | return redirect("/") 86 | 87 | 88 | # POST endpoint to handle callback events 89 | @app.route('/api/callbacks', methods=['POST']) 90 | def callback_events_handler(): 91 | for event_dict in request.json: 92 | # Parsing callback events 93 | event = CloudEvent.from_dict(event_dict) 94 | call_connection_id = event.data['callConnectionId'] 95 | app.logger.info("%s event received for call connection id: %s", event.type, call_connection_id) 96 | call_connection_client = call_automation_client.get_call_connection(call_connection_id) 97 | target_participant = PhoneNumberIdentifier(TARGET_PHONE_NUMBER) 98 | if event.type == "Microsoft.Communication.CallConnected": 99 | # (Optional) Add a Microsoft Teams user to the call. Uncomment the below snippet to enable Teams Interop scenario. 100 | # call_connection_client.add_participant(target_participant = CallInvite( 101 | # target = MicrosoftTeamsUserIdentifier(user_id=TARGET_TEAMS_USER_ID), 102 | # source_display_name = "Jack (Contoso Tech Support)")) 103 | 104 | app.logger.info("Starting recognize") 105 | get_media_recognize_choice_options( 106 | call_connection_client=call_connection_client, 107 | text_to_play=MAIN_MENU, 108 | target_participant=target_participant, 109 | choices=get_choices(),context="") 110 | 111 | # Perform different actions based on DTMF tone received from RecognizeCompleted event 112 | elif event.type == "Microsoft.Communication.RecognizeCompleted": 113 | app.logger.info("Recognize completed: data=%s", event.data) 114 | if event.data['recognitionType'] == "choices": 115 | label_detected = event.data['choiceResult']['label']; 116 | phraseDetected = event.data['choiceResult']['recognizedPhrase']; 117 | app.logger.info("Recognition completed, labelDetected=%s, phraseDetected=%s, context=%s", label_detected, phraseDetected, event.data.get('operationContext')) 118 | if label_detected == CONFIRM_CHOICE_LABEL: 119 | text_to_play = CONFIRMED_TEXT 120 | else: 121 | text_to_play = CANCEL_TEXT 122 | handle_play(call_connection_client=call_connection_client, text_to_play=text_to_play) 123 | 124 | elif event.type == "Microsoft.Communication.RecognizeFailed": 125 | failedContext = event.data['operationContext'] 126 | if(failedContext and failedContext == RETRY_CONTEXT): 127 | handle_play(call_connection_client=call_connection_client, text_to_play=NO_RESPONSE) 128 | else: 129 | resultInformation = event.data['resultInformation'] 130 | app.logger.info("Encountered error during recognize, message=%s, code=%s, subCode=%s", 131 | resultInformation['message'], 132 | resultInformation['code'], 133 | resultInformation['subCode']) 134 | if(resultInformation['subCode'] in[8510, 8510]): 135 | textToPlay =CUSTOMER_QUERY_TIMEOUT 136 | else : 137 | textToPlay =INVALID_AUDIO 138 | 139 | get_media_recognize_choice_options( 140 | call_connection_client=call_connection_client, 141 | text_to_play=textToPlay, 142 | target_participant=target_participant, 143 | choices=get_choices(),context=RETRY_CONTEXT) 144 | 145 | elif event.type in ["Microsoft.Communication.PlayCompleted", "Microsoft.Communication.PlayFailed"]: 146 | app.logger.info("Terminating call") 147 | call_connection_client.hang_up(is_for_everyone=True) 148 | 149 | return Response(status=200) 150 | 151 | # GET endpoint to render the menus 152 | @app.route('/') 153 | def index_handler(): 154 | return render_template("index.html") 155 | 156 | 157 | if __name__ == '__main__': 158 | app.logger.setLevel(INFO) 159 | app.run(port=8080) 160 | -------------------------------------------------------------------------------- /callautomation-connect-room/main.py: -------------------------------------------------------------------------------- 1 | import time 2 | from quart import Quart, render_template, jsonify, request, redirect 3 | from azure.core.exceptions import HttpResponseError 4 | from datetime import datetime, timezone, timedelta 5 | from azure.communication.callautomation import ( 6 | PhoneNumberIdentifier, 7 | TextSource 8 | ) 9 | from azure.communication.identity import ( 10 | CommunicationUserIdentifier, 11 | CommunicationIdentityClient 12 | ) 13 | from azure.communication.callautomation.aio import ( 14 | CallAutomationClient 15 | ) 16 | from azure.communication.rooms import ( 17 | RoomsClient, 18 | RoomParticipant, 19 | ParticipantRole 20 | ) 21 | 22 | from logging import INFO 23 | import asyncio 24 | from config import Config 25 | 26 | # Initialize Quart app 27 | app = Quart(__name__) 28 | 29 | # Use config values from config.py 30 | PORT = Config.PORT 31 | CONNECTION_STRING = Config.CONNECTION_STRING 32 | ACS_RESOURCE_PHONE_NUMBER = Config.ACS_RESOURCE_PHONE_NUMBER 33 | TARGET_PHONE_NUMBER = Config.TARGET_PHONE_NUMBER 34 | CALLBACK_URI = Config.CALLBACK_URI 35 | COGNITIVE_SERVICES_ENDPOINT = Config.COGNITIVE_SERVICES_ENDPOINT 36 | 37 | # Initialize variables 38 | acs_client = None 39 | call_connection = None 40 | call_connection_id = None 41 | server_call_id = None 42 | room_id = None 43 | 44 | # Global variables to store room and user details 45 | room_details = None 46 | 47 | # Initialize ACS Client 48 | async def create_acs_client(): 49 | global acs_client 50 | acs_client = CallAutomationClient.from_connection_string(CONNECTION_STRING) 51 | print("Initialized ACS Client.") 52 | 53 | # Create Room 54 | async def create_room(): 55 | identity_client = CommunicationIdentityClient.from_connection_string(CONNECTION_STRING) 56 | app.logger.info("Test") 57 | # Correct unpacking of the returned tuple 58 | user1, token1 = identity_client.create_user_and_token(["voip"]) 59 | user2, token2 = identity_client.create_user_and_token(["voip"]) 60 | 61 | communication_user_id1 = user1.properties['id'] 62 | communication_user_id2 = user2.properties['id'] 63 | # Now you can access the 'user' and 'token' separately 64 | print(f"Presenter: {communication_user_id1}, Token: {token1.token}") 65 | print(f"Attendee: {communication_user_id2}, Token: {token2.token}") 66 | 67 | rooms_client = RoomsClient.from_connection_string(CONNECTION_STRING) 68 | 69 | valid_from = datetime.now(timezone.utc) 70 | valid_until = valid_from + timedelta(weeks=7) 71 | # Create participants 72 | participants = [ 73 | RoomParticipant( 74 | communication_identifier=CommunicationUserIdentifier(communication_user_id1), 75 | role=ParticipantRole.PRESENTER # Presenter role 76 | ), 77 | RoomParticipant( 78 | communication_identifier=CommunicationUserIdentifier(communication_user_id2), 79 | role=ParticipantRole.CONSUMER # Attendee role 80 | ) 81 | ] 82 | 83 | try: 84 | create_room_response = rooms_client.create_room( 85 | valid_from=valid_from, 86 | valid_until=valid_until, 87 | participants=participants, 88 | pstn_dial_out_enabled=True 89 | ) 90 | except HttpResponseError as ex: 91 | print(ex) 92 | 93 | global room_id 94 | room_id = create_room_response.id 95 | print(f"Room created with ID: {room_id}") 96 | 97 | room_details = { 98 | "room_id": create_room_response.id, 99 | "presenter_id": communication_user_id1, 100 | "presenter_token": token1.token, 101 | "attendee_id": communication_user_id2, 102 | "attendee_token": token2.token 103 | } 104 | return room_details 105 | # Connect call to the room 106 | async def connect_call(): 107 | global call_connection_id 108 | if room_id: 109 | # Use CALLBACK_URI from the config 110 | callback_uri = CALLBACK_URI + "/api/callbacks" 111 | app.logger.info(f"Callback URL: {callback_uri}") 112 | app.logger.info(f"Room ID: {room_id}") 113 | 114 | response = await acs_client.connect_call( 115 | room_id=room_id, 116 | callback_url=callback_uri, 117 | cognitive_services_endpoint=COGNITIVE_SERVICES_ENDPOINT, 118 | operation_context="connectCallContext") 119 | print("Call connection initiated.") 120 | app.logger.info(f"Connect request correlation id: {response.correlation_id}") 121 | else: 122 | print("Room ID is empty or room not available.") 123 | 124 | # Add PSTN participant to the call 125 | async def add_pstn_participant(): 126 | if call_connection_id: 127 | target = PhoneNumberIdentifier(TARGET_PHONE_NUMBER) 128 | source_caller_id_number = PhoneNumberIdentifier(ACS_RESOURCE_PHONE_NUMBER) 129 | app.logger.info("source_caller_id_number: %s", source_caller_id_number) 130 | 131 | add_participant_result= await call_connection.add_participant(target_participant=target, 132 | source_caller_id_number=source_caller_id_number, 133 | operation_context=None, 134 | invitation_timeout=15) 135 | app.logger.info("Add Translator to the call: %s", add_participant_result.invitation_id) 136 | 137 | print(f"Adding PSTN participant with invitation ID: {add_participant_result.invitation_id}") 138 | else: 139 | print("Call connection ID is empty or call not active.") 140 | 141 | # Hangup the call 142 | async def hang_up_call(): 143 | if call_connection: 144 | await call_connection.hang_up(True) 145 | print("Call hung up.") 146 | 147 | async def handle_play(): 148 | play_source = TextSource(text="Hello, welcome to connect room contoso app.", voice_name="en-US-NancyNeural") 149 | if call_connection: 150 | await call_connection.play_media_to_all(play_source) 151 | 152 | # Routes 153 | @app.route('/') 154 | async def home(): 155 | global room_details 156 | # Check if room details already exist 157 | if not room_details: 158 | # Create the ACS client and room only if room is not created yet 159 | await create_acs_client() 160 | room_details = await create_room() 161 | 162 | # Render the page with the room details 163 | return await render_template('index.html', details=room_details) 164 | 165 | @app.route('/connectCall', methods=['GET']) 166 | async def connect_call_route(): 167 | await connect_call() 168 | return redirect('/') 169 | 170 | @app.route('/addParticipant', methods=['GET']) 171 | async def add_participant_route(): 172 | await add_pstn_participant() 173 | return redirect('/') 174 | 175 | @app.route('/hangup', methods=['GET']) 176 | async def hangup_route(): 177 | await hang_up_call() 178 | return redirect('/') 179 | 180 | @app.route('/playMedia', methods=['GET']) 181 | async def play_media_route(): 182 | await handle_play() 183 | return redirect('/') 184 | 185 | @app.route('/api/callbacks', methods=['POST']) 186 | async def handle_callbacks(): 187 | try: 188 | global call_connection_id, server_call_id, call_connection 189 | 190 | # Extract the first event from the request body 191 | events = await request.json 192 | event = events[0] # Assumes at least one event is present 193 | event_data = event['data'] 194 | # Handle specific event types 195 | if event['type'] == "Microsoft.Communication.CallConnected": 196 | app.logger.info("Received CallConnected event") 197 | app.logger.info(f"Correlation ID: {event_data['correlationId']}") 198 | # Extract necessary details 199 | call_connection_id = event_data['callConnectionId'] 200 | server_call_id = event_data['serverCallId'] 201 | call_connection = acs_client.get_call_connection(call_connection_id) 202 | 203 | elif event['type'] == "Microsoft.Communication.AddParticipantSucceeded": 204 | app.logger.info("Received AddParticipantSucceeded event") 205 | 206 | elif event['type'] == "Microsoft.Communication.AddParticipantFailed": 207 | result_info = event_data['resultInformation'] 208 | app.logger.info("Received AddParticipantFailed event") 209 | app.logger.info(f"Code: {result_info['code']}, Subcode: {result_info['subCode']}") 210 | app.logger.info(f"Message: {result_info['message']}") 211 | elif event['type'] == "Microsoft.Communication.PlayCompleted": 212 | app.logger.info("Received PlayCompleted event") 213 | elif event['type'] == "Microsoft.Communication.PlayFailed": 214 | result_info = event_data['resultInformation'] 215 | app.logger.info("Received PlayFailed event") 216 | app.logger.info(f"Code: {result_info['code']}, Subcode: {result_info['subCode']}") 217 | app.logger.info(f"Message: {result_info['message']}") 218 | elif event['type'] == "Microsoft.Communication.CallDisconnected": 219 | app.logger.info("Received CallDisconnected event") 220 | app.logger.info(f"Correlation ID: {event_data['correlationId']}") 221 | 222 | # Respond with a success status 223 | return jsonify({"status": "OK"}), 200 224 | 225 | except Exception as e: 226 | app.logger.error(f"Error processing callback: {e}") 227 | return jsonify({"error": "Internal server error"}), 500 228 | 229 | if __name__ == '__main__': 230 | app.logger.setLevel(INFO) 231 | app.run(port=PORT) 232 | 233 | -------------------------------------------------------------------------------- /call-recording/Controller/RecordingsController.py: -------------------------------------------------------------------------------- 1 | from azure.eventgrid import EventGridEvent 2 | from ConfigurationManager import ConfigurationManager 3 | from Logger import Logger 4 | import json 5 | import ast 6 | from aiohttp import web 7 | from azure.core.messaging import CloudEvent 8 | from azure.communication.callautomation import ( 9 | CallAutomationClient, 10 | CallInvite, 11 | PhoneNumberIdentifier, 12 | ServerCallLocator) 13 | 14 | configuration_manager = ConfigurationManager.get_instance() 15 | connection_string = configuration_manager.get_app_settings("ACSResourceConnectionString") 16 | _client = CallAutomationClient.from_connection_string(connection_string) 17 | _call_connection_id = None 18 | _server_call_id = None 19 | _recording_id = None 20 | _content_location = None 21 | _delete_location = None 22 | 23 | class RecordingsController(): 24 | 25 | def __init__(self): 26 | app = web.Application() 27 | app.add_routes([web.get('/startRecording', RecordingsController.start_recording)]) 28 | app.add_routes([web.get('/pauseRecording', RecordingsController.pause_recording)]) 29 | app.add_routes([web.get('/resumeRecording', RecordingsController.resume_recording)]) 30 | app.add_routes([web.delete('/stopRecording', RecordingsController.stop_recording)]) 31 | app.add_routes([web.get('/getRecordingState', RecordingsController.get_recording_state)]) 32 | app.add_routes([web.get('/downloadRecording', RecordingsController.download_recording)]) 33 | app.add_routes([web.delete('/deleteRecording', RecordingsController.delete_recording)]) 34 | app.add_routes([web.get('/outboundCall', RecordingsController.outbound_call)]) 35 | app.add_routes([web.post('/api/callbacks', RecordingsController.start_callback)]) 36 | app.add_routes([web.post('/recordingFileStatus', RecordingsController.recording_file_status)]) 37 | web.run_app(app, port=58963) 38 | 39 | ## region outbound call - an active call required for recording to start. 40 | async def outbound_call(request): 41 | callback_url = configuration_manager.get_app_settings('CallbackUri') + "/api/callbacks" 42 | targetPhoneNumber = (str)(request.rel_url.query['targetPhoneNumber']).replace(" ", "+") 43 | target = PhoneNumberIdentifier(targetPhoneNumber) 44 | caller_id = PhoneNumberIdentifier(configuration_manager.get_app_settings('ACSAcquiredPhoneNumber')) 45 | call_invite = CallInvite(target=target, source_caller_id_number=caller_id) 46 | response = _client.create_call(target_participant=call_invite, callback_url=callback_url) 47 | global _call_connection_id 48 | _call_connection_id = response.call_connection_id 49 | return web.Response(text=response.call_connection_id) 50 | 51 | async def start_recording(request): 52 | try: 53 | server_call_id = request.rel_url.query['serverCallId'] 54 | if not server_call_id: 55 | server_call_id = _server_call_id 56 | 57 | response = _client.start_recording(call_locator=ServerCallLocator(server_call_id)) 58 | global _recording_id 59 | _recording_id = response.recording_id 60 | return web.Response(text=response.recording_id) 61 | except Exception as ex: 62 | Logger.log_message( Logger.ERROR, "Failed to start server recording --> " + str(ex)) 63 | return web.Response(text=str(ex), status=400) 64 | 65 | async def pause_recording(request): 66 | try: 67 | recording_id = request.rel_url.query['recordingId'] 68 | if not recording_id: 69 | recording_id = _recording_id 70 | 71 | res = _client.pause_recording(recording_id=recording_id) 72 | Logger.log_message(Logger.INFORMATION, "PauseRecording response --> " + str(res)) 73 | return web.Response(text="OK") 74 | except Exception as ex: 75 | Logger.log_message(Logger.ERROR, "Failed to pause server recording --> " + str(ex)) 76 | return web.Response(text=str(ex), status=500) 77 | 78 | async def resume_recording(request): 79 | try: 80 | recording_id = request.rel_url.query['recordingId'] 81 | if not recording_id: 82 | recording_id = _recording_id 83 | 84 | res = _client.resume_recording(recording_id=recording_id) 85 | Logger.log_message(Logger.INFORMATION, "ResumeRecording response --> " + str(res)) 86 | return web.Response(text="Ok") 87 | except Exception as ex: 88 | Logger.log_message(Logger.ERROR, "Failed to resume server recording --> " + str(ex)) 89 | return web.Response(text=str(ex), status=500) 90 | 91 | async def stop_recording(request): 92 | try: 93 | recording_id = request.rel_url.query['recordingId'] 94 | if not recording_id: 95 | recording_id = _recording_id 96 | 97 | res = _client.stop_recording(recording_id=recording_id) 98 | Logger.log_message(Logger.INFORMATION,"StopRecording response --> " + str(res)) 99 | return web.Response(text="Ok") 100 | except Exception as ex: 101 | Logger.log_message(Logger.ERROR, "Failed to stop server recording --> " + str(ex)) 102 | return web.Response(text=str(ex), status=500) 103 | 104 | async def get_recording_state(request): 105 | try: 106 | recording_id = request.rel_url.query['recordingId'] 107 | if not recording_id: 108 | recording_id = _recording_id 109 | 110 | res = _client.get_recording_properties(recording_id=recording_id) 111 | Logger.log_message(Logger.INFORMATION, "GetRecordingState response --> " + str(res)) 112 | return web.Response(text=res.recording_state, status=200) 113 | except Exception as ex: 114 | Logger.log_message(Logger.ERROR, "Failed to get recording status --> " + str(ex)) 115 | return web.Response(text=str(ex), status=500) 116 | 117 | async def download_recording(request): 118 | try: 119 | recording_data = _client.download_recording(_content_location) 120 | with open("Recording_File.wav", "wb") as binary_file: 121 | binary_file.write(recording_data.read()) 122 | return web.Response(text="Ok") 123 | except Exception as ex: 124 | Logger.log_message(Logger.ERROR, "Failed to download recording --> " + str(ex)) 125 | return web.Response(text=str(ex), status=500) 126 | 127 | async def delete_recording(request): 128 | try: 129 | _client.delete_recording(_delete_location) 130 | return web.Response(text="Ok") 131 | except Exception as ex: 132 | Logger.log_message(Logger.ERROR, "Failed to delete server recording --> " + str(ex)) 133 | return web.Response(text=str(ex), status=500) 134 | 135 | 136 | ## region call backs apis 137 | async def start_callback(request): 138 | try: 139 | content = await request.content.read() 140 | post_data = str(content.decode('UTF-8')) 141 | if post_data: 142 | json_data = ast.literal_eval(json.dumps(post_data)) 143 | event = CloudEvent.from_dict(ast.literal_eval(json_data)[0]) 144 | if event.type == "Microsoft.Communication.CallConnected": 145 | global _server_call_id 146 | _server_call_id = event.data['serverCallId'] 147 | Logger.log_message(Logger.INFORMATION, "ServerCallId => "+event.data['serverCallId']) 148 | 149 | except Exception as ex: 150 | Logger.log_message(Logger.ERROR, 'Failed to parse callback --> ' + str(ex)) 151 | 152 | 153 | # Web hook to receive the recording file update status event, [Do not call directly from Swagger] 154 | async def recording_file_status(request): 155 | try: 156 | content = await request.content.read() 157 | post_data = str(content.decode('UTF-8')) 158 | if post_data: 159 | json_data = ast.literal_eval(json.dumps(post_data)) 160 | event = EventGridEvent.from_dict(ast.literal_eval(json_data)[0]) 161 | Logger.log_message(Logger.INFORMATION,"Event type is --> " + str(event.event_type)) 162 | Logger.log_message(Logger.INFORMATION,"Request data --> " + str(event.data)) 163 | 164 | event_data = event.data 165 | if event.event_type == 'Microsoft.EventGrid.SubscriptionValidationEvent': 166 | subscription_validation_event = event_data 167 | code = subscription_validation_event['validationCode'] 168 | if code: 169 | data = {"validationResponse": code} 170 | Logger.log_message(Logger.INFORMATION,"Successfully Subscribed EventGrid.ValidationEvent --> " + str(data)) 171 | return web.Response(body=str(data), status=200) 172 | 173 | if event.event_type == 'Microsoft.Communication.RecordingFileStatusUpdated': 174 | acs_recording_file_status_updated_event_data = event_data 175 | acs_recording_chunk_info_properties = acs_recording_file_status_updated_event_data['recordingStorageInfo']['recordingChunks'][0] 176 | 177 | Logger.log_message(Logger.INFORMATION, "acsRecordingChunkInfoProperties response data --> " + str(acs_recording_chunk_info_properties)) 178 | 179 | global _content_location 180 | global _delete_location 181 | _content_location = acs_recording_chunk_info_properties['contentLocation'] 182 | _delete_location = acs_recording_chunk_info_properties['deleteLocation'] 183 | 184 | except Exception as ex: 185 | Logger.log_message(Logger.INFORMATION, "Failed to get recording file") 186 | return web.Response(text='Failed to get recording file', status=400) --------------------------------------------------------------------------------