Commit 031916c5 by Oleksandr Barabash

localization added, small fixes

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