iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 4
0

第12 屆iT邦幫忙鐵人賽系列文章 (Day4)

本篇開始我們要來逐步實踐我們的婚禮 Chatbot 了!

我們預期在加入好友的時候 (OnFollow) 事件時回傳歡迎詞:Hi UserName, 感謝您加入 Kyle 的婚禮小助理!

取得使用者的資訊

記得上一篇講 Webhook Event 時的結構嗎? 裡面有個 userId

查看文件後,可以透過 access token 和 userId 來取得用戶資訊

建立一個 LineProfileUtility.cs 取得使用者資訊

我們建立一個 Class 來實作取得使用者相關程式,讓未來不同的情境可以調用,以下是文件取得使用者範例的Sample

curl -v -X GET https://api.line.me/v2/bot/profile/{userId} \
-H 'Authorization: Bearer {channel access token}'

有個小技巧可以把 curl 轉成 C# 的 HttpClinet ,將以上的資訊貼到 https://curl.olsh.me/ 即可轉換完成,再做一些微調就好了

轉換後如下:

using (var httpClient = new HttpClient())
{
    using (var request = new HttpRequestMessage(new HttpMethod("GET"), "[https://api.line.me/v2/bot/profile/{userId](https://api.line.me/v2/bot/profile/{userId)}"))
    {
        request.Headers.TryAddWithoutValidation("Authorization", "Bearer {channel access token}");

var response = await httpClient.SendAsync(request);
    }
}

access token 在幾天前的文章,我們已經定義在 appsetting.json 了,在建構的時候我們注入進來

新增一個 GetUserProfile function,實作如下

public async Task<UserProfile> GetUserProfile(string userId)
{
 using (var httpClient = new HttpClient())
 {
  using (var request = new HttpRequestMessage(new HttpMethod("GET"), $"{lineMessageApiBaseUrl}/{userId}"))
  {
   request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");
   var response = await httpClient.SendAsync(request);
   if(response.StatusCode != HttpStatusCode.OK)
   {
    // 這邊未來應該要 log 起來
    throw new Exception("get_profile_error");
   }
   var result = await response.Content.ReadAsStringAsync();
   return JsonConvert.DeserializeObject<UserProfile>(result);
  }
 }
}

UserProfile.cs 定義如下

public class UserProfile
{
 public string userId { get; set; }
 public string displayName { get; set; }
 public string pictureUrl { get; set; }
 public string statusMessage { get; set; }
}

JSON.NET 轉換

在 .NET Core 3.1 其實有內建 System.Text.Json 來做JSON的序列化跟反序列化處理,但個人還是習慣用 JSON.NET

JsonConvert.DeserializeObject<UserProfile>(result)

您可在專案以下打開 Nuget 套件管理

安裝 JSON.NET 回來

在 OnFollow 事件取得使用者資訊

建立一個 LineReplyMessageUtility.cs 來回覆訊息

我們建立一個 Class 來實作回覆使用者相關程式,以下是官方文件回覆使用者範例的Sample

curl -v -X POST https://api.line.me/v2/bot/message/reply \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {channel access token}' \
-d '{
    "replyToken":"nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
    "messages":[
        {
            "type":"text",
            "text":"Hello, user"
        },
        {
            "type":"text",
            "text":"May I help you?"
        }
    ]
}'

https://curl.olsh.me/ 轉成 C#

using (var httpClient = new HttpClient())
{
    using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://api.line.me/v2/bot/message/reply"))
    {
        request.Headers.TryAddWithoutValidation("Authorization", "Bearer {channel access token}"); 

        request.Content = new StringContent("{\n    \"replyToken\":\"nHuyWiB7yP5Zw52FIkcQobQuGDXCTA\",\n    \"messages\":[\n        {\n            \"type\":\"text\",\n            \"text\":\"Hello, user\"\n        },\n        {\n            \"type\":\"text\",\n            \"text\":\"May I help you?\"\n        }\n    ]\n}");
        request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); 

        var response = await httpClient.SendAsync(request);
    }
}

做點調整:將 StringContent 變成class,並用 JSON.NET序列化,完整實作如下:

private readonly string accessToken;
private static string lineMessageApiBaseUrl = "[https://api.line.me/v2/bot/message/reply](https://api.line.me/v2/bot/message/reply)";

public LineReplyMessageUtility(IOptions<LineSetting> lineSetting)
{
 accessToken = lineSetting.Value.ChannelAccessToken;
}

public async Task ReplyMessageAsync(string replyToken, params string[] messages)
{
 using (var httpClient = new HttpClient())
 {
  using (var request = new HttpRequestMessage(new HttpMethod("POST"), $"{lineMessageApiBaseUrl}"))
  {
   request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");

LineMessageReq req = new LineMessageReq();
   req.ReplyToken = replyToken;

foreach (var message in messages)
   {
    req.Messages.Add(new TextMessage()
    {
     Text = message
    });
   }

var postJson = JsonConvert.SerializeObject(req, new JsonSerializerSettings
   {
    ContractResolver = new DefaultContractResolver
    {
     NamingStrategy = new CamelCaseNamingStrategy() //轉小寫
    },
    Formatting = Formatting.Indented
   });

request.Content = new StringContent(postJson);
   request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
   var response = await httpClient.SendAsync(request);
  }
 }
}

LineMessageReq
這邊定義一個 IMessage 的介面,之後不同的訊息類型,將會以此來繼承

public class LineMessageReq

{

public string ReplyToken { get; set; }

public List<IMessage> Messages { get; set; } = new List<IMessage>();

}

TextMessage Class

public class TextMessage : IMessage

{

public string Text { get; set; }

public LineMessageType Type => LineMessageType.text;

}

於 OnFollowAsync 加入 Reply

protected virtual async Task OnFollowAsync(Event ev)
{
 // 取得使用者的資訊
 var user = await lineProfileUtility.GetUserProfile(ev.source.userId);
 // 回傳歡迎詞
 await lineMessageUtility.ReplyMessageAsync(ev.replyToken, $@"Hi {user.displayName}, 感謝您加入婚禮小助理!")
}

如何測試加入好友事件呢? 其實只要封鎖再解除封鎖就好了

實作效果

實作效果

懶人包,本次學到了什麼?

本篇文章同步發佈於我的 Medium 如果這篇文章對你有幫助,就大力追蹤和拍手鼓掌下去吧 !


上一篇
用 .NET Core 接收Line的訊息事件
下一篇
回傳不文謅謅的 Line 貼圖
系列文
宅男的浪漫 - 用 .NET Core 打造 Line 婚禮聊天機器人經驗分享30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
s10730047
iT邦新手 5 級 ‧ 2021-12-24 18:56:35

請問大大為什麼我在LineMessageReq的class裡面 IMessage會報錯

The type or namespace name 'IMessage' could not be found (are you missing a using directive or an assembly reference?)

我要留言

立即登入留言