Commit 031916c5 by Oleksandr Barabash

localization added, small fixes

parent f0d253c8
...@@ -47,7 +47,7 @@ async def on_error(context: TurnContext, error: Exception): ...@@ -47,7 +47,7 @@ async def on_error(context: TurnContext, error: Exception):
# This check writes out errors to console log .vs. app insights. # This check writes out errors to console log .vs. app insights.
# NOTE: In production environment, # NOTE: In production environment,
# you should consider logging this to Azure application insights. # you should consider logging this to Azure application insights.
print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr) Log.e(TAG, f"\n [on_turn_error] unhandled error: {error}")
traceback.print_exc() traceback.print_exc()
# Send a message to the user # Send a message to the user
...@@ -134,8 +134,8 @@ async def v1_notification(request: Request) -> Response: ...@@ -134,8 +134,8 @@ async def v1_notification(request: Request) -> Response:
except DataParsingError: except DataParsingError:
return Response(status=HTTPStatus.BAD_REQUEST, return Response(status=HTTPStatus.BAD_REQUEST,
reason="Bad data structure") reason="Bad data structure")
except Exception as e: except Exception:
print("error:", e) Log.e(TAG, exc_info=sys.exc_info())
return Response(status=HTTPStatus.INTERNAL_SERVER_ERROR) return Response(status=HTTPStatus.INTERNAL_SERVER_ERROR)
......
en:
hi_message: |
Hi %{name}!
Thank you for installing %{bot_name}.
Use '%{cmd_help}' command for more info.
greetings_message: |
Greetings, new users!
Thank you for installing %{bot_name}.
Use '%{cmd_help}' command for more info.
tenant_forbidden: |
Sorry, you dont have rights to communicate with this bot from your tenant.
unknown_request: |
This requires is not supported.
cmd_help: help
cmd_portal: portal
response_help: |
This is a help response.
response_unknown_cmd: |
Unknown command received.
Send "Help" to get the list of awailable commands.
...@@ -8,24 +8,25 @@ from urllib.parse import urlparse, parse_qsl, urlencode ...@@ -8,24 +8,25 @@ 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, ConversationReference, \ from botbuilder.schema import Activity, ActivityTypes
ChannelAccount
from botbuilder.schema.teams import (TaskModuleContinueResponse, from botbuilder.schema.teams import (TaskModuleContinueResponse,
TaskModuleTaskInfo, TaskModuleResponse, TaskModuleTaskInfo, TaskModuleResponse,
TaskModuleRequest, TeamsChannelData) TaskModuleRequest)
from botbuilder.core.teams import TeamsActivityHandler from botbuilder.core.teams import TeamsActivityHandler
from botframework.connector import Channels from botframework.connector import Channels
from marshmallow import EXCLUDE from marshmallow import EXCLUDE
from bots.exceptions import ConversationNotFound from bots.exceptions import ConversationNotFound
from config import TaskModuleConfig, AppConfig from config import TaskModuleConfig, AppConfig
from entities.json.conversation_reference_schema import (
ConversationReferenceSchema
)
from entities.json.medx import MedX, MXTypes from entities.json.medx import MedX, MXTypes
from entities.json.notification import NotificationCosmos from entities.json.notification import NotificationCosmos
from utils.card_helper import CardHelper from utils.card_helper import CardHelper
from utils.cosmos_client import CosmosClient, ItemNotFound from utils.cosmos_client import CosmosClient, ItemNotFound
from utils.functions import get_i18n
from utils.log import Log
TAG = __name__
class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
...@@ -78,7 +79,8 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -78,7 +79,8 @@ 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_notification(self, notification: NotificationCosmos) -> Future[str]: def send_notification(self, notification: NotificationCosmos)\
-> Future[str]:
""" Notify conversation that there's a message waiting in portal """ """ Notify conversation that there's a message waiting in portal """
io_loop = asyncio.get_event_loop() io_loop = asyncio.get_event_loop()
future = Future() future = Future()
...@@ -102,8 +104,6 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -102,8 +104,6 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
async def callback(turn_context: TurnContext) -> None: async def callback(turn_context: TurnContext) -> None:
""" Turn Context callback. Kinda awful syntax, I know """ """ Turn Context callback. Kinda awful syntax, I know """
# TODO(s1z): Add exception handler
# (like conversation not found etc...)
try: try:
card = CardHelper.create_notification_card( card = CardHelper.create_notification_card(
await self.cosmos_client.create_notification( await self.cosmos_client.create_notification(
...@@ -115,8 +115,8 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -115,8 +115,8 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
attachments=attachments) attachments=attachments)
await turn_context.send_activity(message) await turn_context.send_activity(message)
future.set_result(notification.id) future.set_result(notification.id)
except Exception as e: except Exception as exception:
future.set_exception(e) future.set_exception(exception)
await self.adapter.continue_conversation(reference, callback, await self.adapter.continue_conversation(reference, callback,
self.settings.app_id) self.settings.app_id)
...@@ -134,6 +134,7 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -134,6 +134,7 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
async def on_conversation_update_activity(self, turn_context: TurnContext): async def on_conversation_update_activity(self, turn_context: TurnContext):
""" On update conversation """ """ On update conversation """
i18n = get_i18n(turn_context)
await self.cosmos_client.create_conversation_reference(turn_context) await self.cosmos_client.create_conversation_reference(turn_context)
if turn_context.activity.channel_id == Channels.ms_teams: if turn_context.activity.channel_id == Channels.ms_teams:
members = [] members = []
...@@ -142,23 +143,33 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -142,23 +143,33 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
members.append(member) members.append(member)
if len(members) == 1 and turn_context.activity.members_added: if len(members) == 1 and turn_context.activity.members_added:
member = members[0] member = members[0]
# TODO(s1z): Add languages and translations name = member.name or ''
await turn_context.send_activity( bot_name = AppConfig.BOT_NAME
f"Hi {member.name or ''}!" cmd_help = i18n.t("cmd_help")
f"Thank you for installing {AppConfig.BOT_NAME}.\n" greetings = i18n.t("hi_message", name=name, bot_name=bot_name,
f"Use 'help' command for more info." cmd_help=cmd_help)
) await turn_context.send_activity(greetings)
return
if len(members) > 1 and turn_context.activity.members_added:
bot_name = AppConfig.BOT_NAME
cmd_help = i18n.t("cmd_help")
greetings = i18n.t("greetings_message", bot_name=bot_name,
cmd_help=cmd_help)
await turn_context.send_activity(greetings)
return
async def handle_submit_action(self, turn_context: TurnContext) -> None: async def handle_submit_action(self, turn_context: TurnContext) -> None:
""" Handle card submit action """ """ Handle card submit action """
i18n = get_i18n(turn_context)
mx = self.get_mx(turn_context) mx = self.get_mx(turn_context)
if mx.type == MXTypes.ACKNOWLEDGE: if mx.type == MXTypes.ACKNOWLEDGE:
try: try:
account = turn_context.activity.from_property account = turn_context.activity.from_property
acks = await self.cosmos_client.get_acknowledge_items( ack_objects = await self.cosmos_client.get_acknowledge_items(
mx.notification_id mx.notification_id
) )
if len(acks) > 0: if len(ack_objects) > 0:
return return
await self.cosmos_client.create_acknowledge(mx.notification_id, await self.cosmos_client.create_acknowledge(mx.notification_id,
...@@ -176,18 +187,18 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -176,18 +187,18 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
attachments=attachments) attachments=attachments)
await turn_context.update_activity(message) await turn_context.update_activity(message)
except ItemNotFound: except ItemNotFound:
# todo(s1z): Handle unknown notification ID # DO NOTHING, Notification not found!
pass pass
return return
await turn_context.send_activity("Unknown request!") await turn_context.send_activity(i18n.t("unknown_request"))
async def on_message_activity(self, turn_context: TurnContext) -> None: async def on_message_activity(self, turn_context: TurnContext) -> None:
""" on message activity """ """ on message activity """
i18n = get_i18n(turn_context)
if turn_context.activity.conversation.tenant_id != AppConfig.TENANT_ID: if turn_context.activity.conversation.tenant_id != AppConfig.TENANT_ID:
# TODO(s1z): translation await turn_context.send_activity(i18n.t("tenant_forbidden"))
await turn_context.send_activity("Sorry, you dont have rights "
"to communicate with this bot "
"from your tenant")
return return
# try to save conversation reference, # try to save conversation reference,
...@@ -199,15 +210,19 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -199,15 +210,19 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
message = turn_context.activity.text.strip().lower() message = turn_context.activity.text.strip().lower()
# TODO(s1z): translations if message == i18n.t("cmd_help"):
if message == "help": await turn_context.send_activity(i18n.t("response_help"))
# TODO(s1z): add proper text and translation return
return await turn_context.send_activity("This is a help command")
if message == i18n.t("cmd_portal"):
card = CardHelper.load_assets_card("default_card") card = CardHelper.load_assets_card("default_card")
attachments = [CardFactory.adaptive_card(card)] attachments = [CardFactory.adaptive_card(card)]
message = Activity(type=ActivityTypes.message, attachments=attachments) message = Activity(type=ActivityTypes.message,
attachments=attachments)
await turn_context.send_activity(message) await turn_context.send_activity(message)
return
await turn_context.send_activity(i18n.t("response_unknown_cmd"))
async def on_mx_task_unsupported(self, turn_context: TurnContext) \ async def on_mx_task_unsupported(self, turn_context: TurnContext) \
-> TaskModuleResponse: -> TaskModuleResponse:
...@@ -233,7 +248,7 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -233,7 +248,7 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
task=TaskModuleContinueResponse(value=task_info) task=TaskModuleContinueResponse(value=task_info)
) )
except ItemNotFound: except ItemNotFound:
print(f"item '{notification_id}' not found") Log.e(TAG, f"item '{notification_id}' not found")
return await self.on_mx_task_default(turn_context) return await self.on_mx_task_default(turn_context)
async def on_mx_task_default(self, turn_context: TurnContext) \ async def on_mx_task_default(self, turn_context: TurnContext) \
...@@ -256,13 +271,17 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -256,13 +271,17 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
) -> TaskModuleResponse: ) -> TaskModuleResponse:
""" On task module fetch. """ On task module fetch.
Requested when user clicks on "msteams": {"type": "task/fetch"} """ Requested when user clicks on "msteams": {"type": "task/fetch"} """
mx_object_key = "mx"
# "mx": { # "mx": {
# "type": "task/notification", # "type": "task/notification",
# "notificationId": notification_id # "notificationId": notification_id
# } # }
mx = MedX.get_schema(unknown=EXCLUDE).load( mx = MedX.get_schema(unknown=EXCLUDE).load(
task_module_request.data.get("mx", dict()) task_module_request.data.get(mx_object_key, dict())
) )
if mx.type == MXTypes.Task.NOTIFICATION and mx.notification_id: if mx.type == MXTypes.Task.NOTIFICATION and mx.notification_id:
# 1. save action to DB # 1. save action to DB
# 2. return URL # 2. return URL
......
...@@ -10,6 +10,7 @@ from utils.token_helper import TokenHelper ...@@ -10,6 +10,7 @@ from utils.token_helper import TokenHelper
PROJECT_ROOT_PATH = os.path.dirname(os.path.abspath("__file__")) PROJECT_ROOT_PATH = os.path.dirname(os.path.abspath("__file__"))
ASSETS_PATH = os.path.join(PROJECT_ROOT_PATH, "assets") ASSETS_PATH = os.path.join(PROJECT_ROOT_PATH, "assets")
CARDS_PATH = os.path.join(ASSETS_PATH, "cards") CARDS_PATH = os.path.join(ASSETS_PATH, "cards")
STRINGS_PATH = os.path.join(ASSETS_PATH, "strings")
class Auth: class Auth:
......
...@@ -10,3 +10,4 @@ stringcase==1.2.0 ...@@ -10,3 +10,4 @@ stringcase==1.2.0
azure-keyvault-secrets==4.2.0 azure-keyvault-secrets==4.2.0
azure-keyvault-keys==4.3.1 azure-keyvault-keys==4.3.1
pycryptodome==3.15.0 pycryptodome==3.15.0
python-i18n[YAML]==0.3.9
import os import os
from typing import Dict, Any, Optional, Mapping from typing import Dict, Any, Optional, Mapping, Union
from config import CARDS_PATH from config import CARDS_PATH
from entities.json.notification import NotificationCosmos from entities.json.notification import NotificationCosmos
...@@ -13,7 +13,8 @@ class CardHelper: ...@@ -13,7 +13,8 @@ class CardHelper:
""" Card Helper """ """ Card Helper """
@staticmethod @staticmethod
def load_assets_card(name: str) -> Mapping[str, Any]: def load_assets_card(name: str) -> Union[dict[str, Any],
Mapping[str, Any]]:
""" 123 """ """ 123 """
filename = name + ".json" if name.find(".json") < 0 else name filename = name + ".json" if name.find(".json") < 0 else name
filename_path = os.path.join(CARDS_PATH, filename) filename_path = os.path.join(CARDS_PATH, filename)
......
...@@ -2,6 +2,40 @@ ...@@ -2,6 +2,40 @@
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
from typing import List, Optional, Dict, Tuple from typing import List, Optional, Dict, Tuple
from botbuilder.core import TurnContext
from utils.log import Log
TAG = __name__
DEFAULT_LOCALE = "en"
def get_locale(turn_context: TurnContext, default=DEFAULT_LOCALE) -> str:
""" Try to get locale and fallback to "en" """
reference = TurnContext.get_conversation_reference(turn_context.activity)
if reference.locale is None:
Log.w(TAG, "get_locale::Error: locale is None")
Log.w(TAG, "get_locale: returning default locale")
return default
def get_i18n(turn_context: TurnContext,
default: str = DEFAULT_LOCALE,
fallback: str = DEFAULT_LOCALE):
""" Get i18n configured """
from config import STRINGS_PATH
import i18n
locale = get_locale(turn_context, default)
i18n.load_path.append(STRINGS_PATH)
i18n.set("enable_memoization", True)
i18n.set('filename_format', '{locale}.{format}')
i18n.set('locale', locale)
i18n.set('fallback', fallback)
return i18n
def get_first_or_none(items: List) -> Optional[Dict[str, any]]: def get_first_or_none(items: List) -> Optional[Dict[str, any]]:
""" Get first object from list or return None len < 1 """ """ Get first object from list or return None len < 1 """
......
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