The Bot
The magic happens in the wxc_cc_bot.CallControlBot class.
A wxc_cc_bot.CallControlBot instance is created in wxc_cc_bot.create_app(). When running locally
wxc_cc_bot.create_app() is called from within the main code of wxc_cc_bot.py. When run on Heroku
wxc_cc_bot.create_app() is referenced in the Procfile:
web: gunicorn 'wxc_cc_bot:create_app()' --workers=1 --log-file -
The bot class:
- class wxc_cc_bot.CallControlBot(*, teams_bot_name: str, teams_bot_token: str, teams_bot_email: str, teams_bot_url: str, client_id: str, client_secret: str, client_scopes: str, client_redirect_url: str, debug=False)[source]
Bases:
TeamsBotThe call control demo bot
- Parameters:
teams_bot_name – Friendly name for the Bot (webhook name). Bot parameters are obtained from https://developer.webex.com/my-apps/wxcc-bot when creating the bot
teams_bot_token – Teams Auth Token for Bot Account
teams_bot_email – Teams Bot Email Address
teams_bot_url – WebHook URL for this Bot
The teams_bot_* parameters are used to initialize the
webexteamsbot.TeamsBotbase class.- Parameters:
client_id – client ID of the integration the Bot uses to obtain tokens to act on behalf of a user. Integration parameters are obtained from https://developer.webex.com/my-apps/wxcc-bot when creating the integration.
client_secret – client secret of the integration the Bot uses to obtain tokens to act on behalf of a user
client_scopes – scopes of the integration the Bot uses to obtain tokens to act on behalf of a user
client_redirect_url – redirect URL of the integration the Bot uses to obtain tokens to act on behalf of a user
debug – debug mode
During initialization some bot commands are registered using the
webexteamsbot.add_command()method.Command
Handler
Description
/auth
Authenticate current user if needed or print authentication state.
/monitor
Enable or disable monitoring von
telephony_callevents for the current user. Events are echoed to the chat./dial
Dial a destination for the current user. Uses
wxc_sdk.telephony.calls.CallsApi.dial()/answer
Answer an incoming call for the current user. Uses
wxc_sdk.telephony.calls.CallsApi.list_calls()andwxc_sdk.telephony.calls.CallsApi.answer()/hangup
Hang up an active call. Uses
wxc_sdk.telephony.calls.CallsApi.hangup()/history
Show call history for current user. Uses
wxc_sdk.telephony.calls.CallsApi.call_history()/redis
Interact with Redis data store. This command is only available if the Bot is using the Redis integration via a
user_context.RedisTokenManagertoken manager.The bot needs to persist state (access and refresh tokens) for each user so that users don’t need to reauthenticate every time the bot has been restarted. This per user state is stored in
user_context.UserContextobjects which are managed by auser_context.TokenManager. During initialization the bot creates either auser_context.YAMLTokenManager(persist state in local yaml file) or auser_context.RedisTokenManager(persist user state in Redis) for token management and handling of OAuth authentication flows. Auser_context.RedisTokenManageris only used if the bot is running in an environment with a running Redis server. This is determined by checking theREDIS_HOST,REDIS_TLS_URL, andREDIS_URLenvironment variables.The
/redirectendpoint under the bot base url (for example http://localhost:6001/redirect) is registered with theflask.Flaskbase class ofCallControlBot. This endpoint is used as redirect URL for the OAuth authorization flows to obtain access tokens for the bot users. The registration is done by callinguser_context.TokenManager.register_redirect().Users can interact with the Bot in 1:1 spaces using above commands, In the registered command handlers if an API call is needed to serve the user request then the 1st check is whether a user context exists for the user the message was received from:
user_context = self._token_manager.get_user_context_and_refresh(user_id=message.personId) if user_context is None or not user_context.tokens.access_token: return f'User {message.personEmail} not authenticated. Use /auth command first'
These user contexts are created as the result of a successful user authorization flow which is initiated in the handler of the /auth command:
auth_callback()like this:# to initiate authentication we need a new flow id, then have to create an authorization URL, and finally # share the authorization URL with the user so that the user can initiate the authorization flow using # that link # register auth flow and get flow id flow_id = self._token_manager.start_flow(user_id=user_id) # get auth URL and share URL with user auth_url = self._integration.auth_url(state=flow_id) self.teams.messages.create(toPersonEmail=user_email, markdown=f'Click this [link]({auth_url}) to authenticate ({flow_id})')
A flow is started, an authorization URL is built and this URL is then presented to the user in a 1:1 message. At the end of the flow an authorization code is passed back via an HTTP GEt on teh redirect URL of the integration. This GET is served by either
user_context.RedisTokenManager.process_redirect()oruser_context.YAMLTokenManager.process_redirect()depending on whether the backend to persist state is a local YAML file or redis. In botch cases first a check is executed whether for thestatevalued passed in the IRL a flow exists. If that’s the case then a new set of tokens is executed. The final check then gets the user details for the authenticated user and then checks whether the tokens belong to the user who initiated the OAuth flow. If all goes well then the tokens (access and refresh token) are stored in the user context for the user.- call_event_url(user_id: str) str[source]
User specific call event URL:
/callevent/{user_id}This URL is used as target URL when creating a webhook for call events of a given user in
monitor_callback()- Parameters:
user_id (str) – user ID to create a
telephony_callwebhook events URL for.- Returns:
generated URL
- Return type:
str
- call_event(user_id: str)[source]
This is the view function that is called by Flask when a POST on the call event URL needs to be handled. This endpoint is used as target URL when creating a webhook for call events of a given user.
The demo bot simply tries to deserialize the
telephony_callevent and then send a message to the user containing the JSON representation of thetelephone_callevent.- Parameters:
user_id (str) – user id, passed as parameter in the request URL
- auth_callback(message: Message) str[source]
handler for /auth command.
- The handler supports:
/auth: check if the user has been authenticated. Get authorization URL if needed, else display existing access token validity/auth clear: clear existing user context for current user/auth force: force new authentication of current user even if the bot already has tokens for the current user./auth maintenance: forceuser_context.RedisTokenManager.flow_maintenance(). This cleans up all dangling OAuth authorization flows: flows which have been initiated but never completed.This command only makes sense if the bot is using a
user_context.RedisTokenManager.
- Parameters:
message (
webexteamssdk.Message) – The message from the user in the 1:1 space with the bot- Returns:
response to user
- Return type:
str
- monitor_callback(message: Message)[source]
handler for /monitor command
- The handler supports:
/montor on: turn
telephony_callsevent monitoring on/montor off: turn
telephony_callsevent monitoring off
- Parameters:
message (
webexteamssdk.Message) – The message from the user in the 1:1 space with the bot- Returns:
response to user
- Return type:
str
To turn monitoring on a webhook for
telephony_callsevents is created for the current user. The URL for the webhook (destination for the POST from Webex) is user specific and created usingcall_event_url().
- dial_callback(message: Message)[source]
handler for /dial command
- Parameters:
message (
webexteamssdk.Message) – The message from the user in the 1:1 space with the bot- Returns:
response to user
- Return type:
str
- answer_callback(message: Message)[source]
handler for /answer command
- Parameters:
message (
webexteamssdk.Message) – The message from the user in the 1:1 space with the bot- Returns:
response to user
- Return type:
str
- hangup_callback(message: Message)[source]
handler for /hangup command
- Parameters:
message (
webexteamssdk.Message) – The message from the user in the 1:1 space with the bot- Returns:
response to user
- Return type:
str
- redis_callback(message: Message)[source]
handler for /redis command
- Parameters:
message (
webexteamssdk.Message) – The message from the user in the 1:1 space with the bot- Returns:
response to user
- Return type:
str
- call_history_callback(message: Message) str[source]
handler for /history command
- Parameters:
message (
webexteamssdk.Message) – The message from the user in the 1:1 space with the bot- Returns:
response to user
- Return type:
str