COLUMN
【テックメモシリーズ】Amazon Chime SDK Messagingで、絵文字機能を追加した
クラウド活用に関するさまざま情報をお届けするメルマガを毎週配信しておりますので、ぜひこの機会にご登録ください。
4人家族なのに、自転車が6台ある中村です。 |
社内展開中の、Reactで作られたWebベースのコミュニケーションツール「FARAT」では、Amazon Chimeを使用したビデオ・チャットの機能を提供しています。
このFARATに、以前社内から「絵文字機能を追加して欲しい」という要望があり、機能追加を行いました。
開発の流れをまとめましたので、簡単に紹介させて頂きます。
Amazon Chime SDK Messagingとは
Amazon Chime SDKの機能の一つで、テキストチャットの機能を提供するものです。
Amazon Chimeは、AWSが提供する、ビデオ会議やチャット、音声通話など、ビジネスコミュニケーションに必要な機能を提供するサービスを、SDKとして開発者に提供しています。
その一つとして、テキストチャットの機能が提供されている、というものです。
元々はビデオ会議や音声通話などの機能だったのですが、利用者からテキストチャットも行いたいという要望があり、Amazon Chime SDK Messagingが追加された、という経緯があるようです。
Amazon Chime SDK Messagingの機能
改めての紹介ですが、文字によるチャットに特化したサービスで、WebSocketを利用した、リアルタイムなメッセージの送受信が特徴です。
加えて、チャットを使いやすくする様々な機能が追加されています。
- チャンネル機能(複数のチャットルームを作成できる)
- 権限管理機能(チャットの閲覧や投稿の権限を設定できる)
- 保持期間の指定が可能な、チャット履歴
- チャネルフロー(発言によりトリガーされるLambdaにより、様々なサービスと統合できる)
- 最大100万人が参加できる、Elasticチャネル
スクラッチで開発すると非常に手間の掛かるこのような機能を、Amazon Chime SDK Messagingを使うことで、簡単にアプリに追加できるのは、大きなメリットですね。
Amazon Chime SDK Messagingの料金
主にメッセージの送受信、メッセージ履歴の保存するストレージにコストが発生します。
詳細は「メッセージング」の項目をご覧下さい。
絵文字機能の追加
このように便利なAmazon Chime SDKですが、「スレッド機能」や「絵文字機能」など、他のチャットアプリにある機能が存在せず、必要に応じて追加開発を行う必要があります。
社内でも、簡便なコミュニケーションとして「絵文字機能」を是非追加して欲しいという要望があり、絵文字機能を追加することになりました。
先に出来たものをお見せします。PCとスマートフォンを並べて、チャットの動作テストを行っています。
(絵文字の動作は、は0:15秒辺りから)
絵文字実装の要件の確認
ざっくりとした「良くある絵文字の機能が欲しい」という要望をブレイクダウンし、要件をまとめました。
- A:発言に対し、自分・他人がリアルタイムに絵文字を付与することができる
- B:一つの発言に対し、一人が複数の絵文字を付与することができる
- C:自分が付与した絵文字は、取り消すことができる
実装方法の検討
大きく「Amazon Chimeのシステム内・外、どちらで実装するか」という方向性を決める必要があり、最終的に、両方を組み合わせることになりました。
メッセージタイプの整理
まず、メッセージタイプの整理を行いました。
Amazon Chime SDK Messagingでテキストチャットを使う時、このいずれかのメッセージタイプが使用されます。これを絵文字に流用できると、工数が簡略化できます。
- System
Amazon Chime SDKがシステムとして送るメッセージで、(メンバーのチャンネル参加、退出等)等を通知します。ユーザはコントロールできません。
- Standard 4KBのテキストメッセージと、最大1KBのメタデータ
テキストチャットを行う上で、核となるメッセージタイプです。4KBの部分で任意のメッセージをを送信します。1KBのメタデータの利用方法は自由ですが、例えば事前にファイルをアップロードし、ファイルへのリンクをメタデータに追加することで、添付ファイル付きの発言を行うことができます。
FARATでは、宛先を指定したメッセージ(メンション)にメタデータを使用しています。
- Control 30バイトのメッセージ
30バイトという非常に短いメッセージの制限の代わりに、STANDARDの230分の1という低コストでメッセージを送信します。通常、このメッセージはチャットとして表示するのではなく、受信した人が何かを行うためのトリガーとして用意されています。例えば、位置情報をバックグラウンドで共有したり、同時に専用のモーダルを開かせたりする、等です。とはいえ、使い方は自由です。
Standard、ControlはSendChannelMessageのAPIを使って送信しますが、Persistenceオプションにより、永続・非永続(チャット履歴に残す・残さない)を決めることができます。
AWS SDK for JavaScript v3 - SendChannelMessageCommand
絵文字を送るための情報の整理
絵文字を送るために、下記の情報が必要です。
- 誰が(ログイン情報:ユーザを特定するChime SDK MessagingのID。16バイト程度)
- どのメッセージに対して(メッセージID:16バイト)
- どの種類の絵文字を(絵文字のバイト数分必要)と、追加するか、削除するか
Chime SDk Messagingでチャットメッセージを送る場合、「誰が送ったか」というsendeIDパラメータは送ってくれるので、ビジネスロジックで実装するべきなのは「どのメッセージか」「どの絵文字か」を、テキストメッセージで送れば良い、ということになります。
できるだけコストを抑えるために、Control Messageを使用したかったのですが、Control messageの30bytesに収めることが難しく、STANDARD Messageの1KBのメタデータを使用することにしました。
この絵文字用メッセージを発言すると、オンラインでチャットに参加している全員に配信されます。各参加者は、受信した絵文字用のメッセージに対して、対象のチャットに絵文字を表示する処理を加えればOKです。絵文字を消したいときは、DeleteChannelMessageで絵文字のStandard MessageのIDを削除することで、絵文字の取り消しができるように思われました。
AWS SDK for JavaScript v3 - DeleteChannelMessageCommand
つまり、永続のStandard Messageで絵文字の実現は可能であり、システム内で完結することができると思われましたが、過去のメッセージの取得を取得する際の問題があり、設計の見直しが必要となりました。
不可視の永続メッセージを多用する注意点
チャットの過去メッセージの取得は、ListChannelMessagesというAPIを使用します。
ListChannelMessages APIには、過去N件(最大50件)までのメッセージを取得するかを指定するMaxResults、次のN件を取得するためのNextTokenのパラメータが存在します。
チャット画面を開いただけで過去1年の全てのメッセージを取得すると、無駄なデータを取得する可能性が高くなります。これらのパラメータを使い、画面のスクロールに応じて遅延読み込み(Lazy Load)を行うことで、必要なデータだけを効率良く取得できるよう、設計されています。
AWS SDK for JavaScript v3 - ListChannelMessagesCommand
不可視の永続メッセージが多いと、どういった問題が発生するでしょうか。
一つの発言があり、そこに5つの絵文字が追加された場合、1つの見える永続のStandard Messageが最も古い履歴として存在し、その後に、5の見えない永続のStandard Messageがチャットの履歴に存在することになります。
ここで、とあるユーザがチャットのチャンネルにログインした場合、ListChannelMessagesにより、チャット履歴から新しい順に、最大5件のチャット発言を取得する場合、そのデータは全て見えない永続のStandard Messageになってしまいます。
つまり、ユーザは過去履歴の取得を試みたはずが、表面上は何も履歴が表示されない、という問題が発生します。
全てが通常のメッセージだった場合、過去履歴を5件取得すると、画面にも5件表示される
最新の5件が不可視の絵文字用メッセージだった場合、最新の5件を取得していても、画面には何も表示されない
Standard Messageが任意の件数蓄積されるまで、ListChannelMessagesを繰り返すと良いでしょうか?
必要なデータは取得できますが、蓄積されるまで、多くのListChannelMessagesのAPIリクエストを繰り返し、ユーザを待たせることになります。
実装方針の決定
このような問題があり、絵文字データを別で管理し、非永続メッセージで絵文字のリアルタイム更新を行う、という方式に方針を変更することになりました。
- DynamoDBのテーブルで絵文字を管理する。
- 絵文字の追加・削除を行う時、最初にDynamoDBに絵文字の追加・削除情報を登録し、常に最新の絵文字情報をデータベースに格納しておく。登録成功時、今追加した絵文字についてStandard Messageを非永続で参加者に配信する。参加者はリアルタイムな絵文字情報を受け取り、各自のブラウザ上で絵文字を反映させる
- 絵文字の追加・削除が行われた時にログインしていないユーザは、リアルタイムな情報を受け取ることができないが、ログインしてチャンネル入室後、DynamoDBから絵文字データを受け取ることで、最新の絵文字情報のリストを受け取ることができる
つまり、チャンネル入室時点の絵文字情報はDynamoDBから取得して、以降の絵文字追加・削除の情報は、Chimeからリアルタイムに受信し、入室時の情報に継ぎ足しています。
DynamoDBのデータ構造
システムの規模感から粒度を考え、発言IDごとのレコードで、対象の発言の全ての絵文字情報を管理するように設計しています。
- PK:チャンネルを特定するID(ChannelArn)
- SK:チャンネル内の発言を特定するID(MessageID)
同時に絵文字の更新・削除が走った場合、データの整合性が問題になりますが、Version属性による楽観的ロックとリトライ処理により回避しています。高頻度のアクセスや絵文字の更新が発生すると予測される場合、より細かい粒度でのデータ保存の設計を行う必要があります。
バージョン番号を使用した楽観的ロック - Amazon DynamoDB
Amazon Chime SDK Messagingは履歴の保存期間を設定できるオプションがあり、この保存期間に合わせたDynamoDB TTLを設定することにより、有効期限の過ぎたレコードは削除され、必要なデータのみが取得されます。
Time to Live (TTL) - Amazon DynamoDB
入室時、最新の絵文字MAPを取得する
チャンネルIDを元にDynamoDBにアクセスし、発言IDと絵文字のリストを受け取り、Reactのコンポーネントで保持します。画面上でチャットメッセージに絵文字をマッピングしています。
誰かの発言に絵文字を追加・削除した時に行われる、DynamoDBへのリクエスト
「どこの発言に」「何の絵文字を」「追加・削除」をパラメータとして投げ、Lambda経由でレコードを更新します。
message/index.tsx
DynamoDBに追加・削除が成功した時、チャット参加者に送るリアルタイムメッセージ
Lambdaからsend_channel_messageのAPIをリクエストし、Standard Messageを履歴に残らない非永続で実行します。
このメッセージは、対象のチャンネルにログインしている全ユーザが受信します。
lambda/index.py
受信後は、各自、Componentに保持している絵文字MAPを更新、宣言的に絵文字の更新が行われる、という流れになっています。
最後に
Amazon Chime SDK Messagingの紹介と、絵文字機能の実装の流れを紹介しました。
「この機能は気に入っているけど、もう少しカスタムしたい」「こんな機能が欲しい」と思った時、製品やサービスでは中々要望に答えられない事もありますが、Chime SDKはかゆい所に手が届く、良い選択肢だと感じています。
機能開発でご紹介できるものがあれば、また書かせて頂きます。
RECOMMEND
その他のコラム
相談無料!プロが中立的にアドバイスいたします
クラウド・AWS・Azureでお困りの方はお気軽にご相談ください。