Commit 5612de6d by Oleksandr Barabash

api for sending messages added

parent f07a07e2
...@@ -4,6 +4,7 @@ import sys ...@@ -4,6 +4,7 @@ import sys
import traceback import traceback
from datetime import datetime from datetime import datetime
from http import HTTPStatus from http import HTTPStatus
from typing import Dict, Union, List
import marshmallow_dataclass import marshmallow_dataclass
from aiohttp import web from aiohttp import web
...@@ -24,6 +25,7 @@ from config import AppConfig, COSMOS_CLIENT, TeamsAppConfig, TOKEN_HELPER, \ ...@@ -24,6 +25,7 @@ from config import AppConfig, COSMOS_CLIENT, TeamsAppConfig, TOKEN_HELPER, \
CosmosDBConfig CosmosDBConfig
from entities.json.admin_user import AdminUser from entities.json.admin_user import AdminUser
from entities.json.notification import Notification from entities.json.notification import Notification
from entities.json.pa_message import PAMessage
from utils.cosmos_client import ItemNotFound from utils.cosmos_client import ItemNotFound
from utils.functions import quote_b64encode_str_safe, quote_b64decode_str_safe from utils.functions import quote_b64encode_str_safe, quote_b64decode_str_safe
from utils.json_func import json_loads, json_dumps from utils.json_func import json_loads, json_dumps
...@@ -76,6 +78,16 @@ async def on_error(context: TurnContext, error: Exception): ...@@ -76,6 +78,16 @@ async def on_error(context: TurnContext, error: Exception):
ADAPTER.on_turn_error = on_error ADAPTER.on_turn_error = on_error
def make_response(code: int, message: str,
data: Union[Dict[any, any], List[Dict[any, any]]] = None)\
-> Response:
""" Make an API json response """
body = dict(status=dict(code=code, message=message))
if data is not None:
body.update(dict(data=data))
return Response(status=HTTPStatus.OK, body=json_dumps(body))
@TOKEN_HELPER.is_auth @TOKEN_HELPER.is_auth
async def v1_get_initiations(request: Request) -> Response: async def v1_get_initiations(request: Request) -> Response:
""" Get Initiations by Notification ID """ """ Get Initiations by Notification ID """
...@@ -234,6 +246,24 @@ async def v1_post_auth(request: Request) -> Response: ...@@ -234,6 +246,24 @@ async def v1_post_auth(request: Request) -> Response:
return Response(status=HTTPStatus.FORBIDDEN) return Response(status=HTTPStatus.FORBIDDEN)
async def v1_pa_message(request: Request) -> Response:
""" Send card to the bot """
# noinspection PyBroadException
try:
body = json_loads(await request.text())
pa_message = marshmallow_dataclass.class_schema(PAMessage)().load(body)
notification_id = await BOT.send_message(pa_message.conversation_id,
pa_message.tenant_id,
pa_message.text,
pa_message.card)
Log.d(TAG, f"v1_pa_message::notification: '{notification_id}'")
except Exception:
Log.e(TAG, "v1_pa_message::error sending message",
exc_info=sys.exc_info())
return make_response(500, "Server Error")
return make_response(400, "Bad Request")
async def init_db_containers(): async def init_db_containers():
""" To speed up the process we have to create containers first """ """ To speed up the process we have to create containers first """
await COSMOS_CLIENT.create_db(CosmosDBConfig.Conversations.DATABASE) await COSMOS_CLIENT.create_db(CosmosDBConfig.Conversations.DATABASE)
...@@ -275,6 +305,8 @@ async def app_factory(bot): ...@@ -275,6 +305,8 @@ async def app_factory(bot):
app.router.add_get("/{}".format(TeamsAppConfig.zip_name), v1_get_app_zip) app.router.add_get("/{}".format(TeamsAppConfig.zip_name), v1_get_app_zip)
app.router.add_post("/api/v1/auth", v1_post_auth) app.router.add_post("/api/v1/auth", v1_post_auth)
# PA endpoints
app.router.add_post("/api/pa/v1/message", v1_pa_message)
bot.add_web_app(app) bot.add_web_app(app)
bot.add_cosmos_client(COSMOS_CLIENT) bot.add_cosmos_client(COSMOS_CLIENT)
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
import asyncio import asyncio
import uuid import uuid
from asyncio import Future from asyncio import Future
from typing import Optional from typing import Optional, Dict
from urllib.parse import urlparse, parse_qsl, urlencode from urllib.parse import urlparse, parse_qsl, urlencode
from aiohttp.web_app import Application from aiohttp.web_app import Application
from botbuilder.core import (TurnContext, CardFactory, BotFrameworkAdapter, from botbuilder.core import (TurnContext, CardFactory, BotFrameworkAdapter,
BotFrameworkAdapterSettings) BotFrameworkAdapterSettings)
from botbuilder.schema import Activity, ActivityTypes from botbuilder.schema import Activity, ActivityTypes, ResourceResponse
from botbuilder.schema.teams import (TaskModuleContinueResponse, from botbuilder.schema.teams import (TaskModuleContinueResponse,
TaskModuleTaskInfo, TaskModuleResponse, TaskModuleTaskInfo, TaskModuleResponse,
TaskModuleRequest) TaskModuleRequest)
...@@ -79,6 +79,49 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -79,6 +79,49 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
mx = turn_context.activity.value.get("mx", {}) mx = turn_context.activity.value.get("mx", {})
return mx.get("notificationId", None) return mx.get("notificationId", None)
def send_message(self,
conversation_id: str,
tenant_id: str, text: str = None,
card: Dict[any, any] = None) -> Future[ResourceResponse]:
""" Send message as a bot """
io_loop = asyncio.get_event_loop()
future = Future()
async def routine():
""" async routine """
try:
reference = await self.cosmos_client.get_conversation(
conversation_id, tenant_id
)
except ItemNotFound:
future.set_exception(ConversationNotFound("not found"))
return
except Exception as e:
future.set_exception(e)
return
async def callback(turn_context: TurnContext) -> None:
""" Turn Context callback. Kinda awful syntax, I know """
try:
attachments = None
if card is not None:
attachments = [CardFactory.adaptive_card(card)]
response = await turn_context.send_activity(Activity(
type=ActivityTypes.message,
text=text,
attachments=attachments)
)
if response:
future.set_result(response)
except Exception as exception:
future.set_exception(exception)
await self.adapter.continue_conversation(reference, callback,
self.settings.app_id)
io_loop.create_task(routine())
return future
def send_notification(self, notification: NotificationCosmos)\ def send_notification(self, notification: NotificationCosmos)\
-> Future[str]: -> Future[str]:
""" Notify conversation that there's a message waiting in portal """ """ Notify conversation that there's a message waiting in portal """
......
...@@ -327,14 +327,14 @@ class CosmosClient: ...@@ -327,14 +327,14 @@ class CosmosClient:
except ItemNotFound: except ItemNotFound:
return None return None
async def get_conversation(self, conversation_id: str)\ async def get_conversation(self, conversation_id: str,
-> ConversationReference: tenant_id: str = None) -> ConversationReference:
""" Get Conversation Reference """ """ Get Conversation Reference """
from config import AppConfig from config import AppConfig
container = await self.get_conversations_container() container = await self.get_conversations_container()
item = await self.get_item(container, conversation_id, item = await self.get_item(container, conversation_id,
AppConfig.TENANT_ID) tenant_id or AppConfig.TENANT_ID)
return ConversationReference.get_schema(unknown=EXCLUDE)\ return ConversationReference.get_schema(unknown=EXCLUDE)\
.load(item).to_ms_reference() .load(item).to_ms_reference()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment