[Day 7] 讓 C# 也可以很 Social — .NET 6 C# 與 Line Services API 開發 — 回覆與廣播訊息

APPX
appxtech

--

Hihi 各位好,今天開始進入訊息的介紹了~預計連續8至9篇的內容,詳細的為各位講解 Line Bot Messaging api 傳訊機制~

<<回顧上一篇<<

今天的主題內容會是建立 廣播訊息功能使用 ReplyToken 回覆使用者文字訊息

本篇大綱:

一、
訊息推播簡介
二、
基本Message Class宣告
三、建立傳訊機制
1.
建立Broadcast機制
2.
建立Reply機制
四、
結語/腦筋急轉彎/範例程式碼

一、訊息推播簡介

⩶傳訊息給使用者的方式簡單分為兩種⩶

  1. Reply (透過 webhook event 傳入的 replyToken 回覆訊息)
  2. Push (主動推播)

⩶而Push (主動推播)又有 4 種不同的方法⩶

  1. Push message (推播訊息給一位使用者,one-to-one)

2. Multicast messages (根據一個 UserID 清單推播訊息給多位使用者,one-to-many)

3. Narrowcast messages (推播訊息給特定標籤或預設好的受眾,one-to-many)

4. Broadcast messages (推播訊息給 Line bot 的所有好友,one-to-many)

其中主動傳訊的功能並不是能無限制數量傳送的,免費方案的 Line Bot 每個月只有 500 則訊息的用量,達到 500 則訊息後則不能再傳送,而另外的升級方案需要通過驗證才能購買,這邊就不說明了,有興趣再去了解吧。

不過若是使用 Reply 的方式傳送訊息就是完全免費的! 這是因為 Line 希望 Line Bot 是一個能夠有與使用者有很多互動的存在,所以透過使用者與 Line Bot 互動產生 webhook event ,使用其中夾帶的 replyToken 就可以直接回覆這個事件,傳送訊息給使用者,也不消耗免費訊息則數!

⩶能夠傳送的訊息種類為以下 9 種⩶

  1. Text message
  2. Sticker message
  3. Image message
  4. Video message
  5. Audio message
  6. Location message
  7. Imagemap message
  8. Template message
  9. Flex message

訊息的詳細介紹下一篇再開始,這篇我們先建立傳送訊息的基本機制,這樣下一篇就可以邊實作邊測試了~

二、基本 Message Class 宣告

這邊我直接貼上要基礎架構的 class 宣告,後面會因應情況再逐漸地加入細節~

  • 先新增 Dtos/Messages 資料夾

BaseMessageDto

  • 在 Dtos/Messages 資料夾中新增 BaseMessageDto.cs
  • (這個 BaseMessageDto也是基礎架構的一部份,後面會因應情況再逐漸地加入更多細節)
namespace LineBotMessage.Dtos
{
public class BaseMessageDto
{
public string Type { get; set; }
}
}

MessageTypeEnum

  • 在 Enum 資料夾中新增 MessageTypeEnum.cs
  • 先準備好會未來會用到的 enum
namespace LineBotMessage.Enum
{
public static class MessageTypeEnum
{
public const string Text = "text";
public const string Sticker = "sticker";
public const string Image = "image";
public const string Video = "video";
public const string Audio = "audio";
public const string Location = "location";
public const string Imagemap = "imagemap";
public const string Template = "template";
public const string Flex = "flex";
}
}

TextMessageDto

  • 在 Dtos/Messages 資料夾中新增 TextMessageDto.cs
  • (先列出 基本架構的程式碼,後續的細節再逐步加入更多細節)
using LineBotMessage.Enum;
namespace LineBotMessage.Dtos
{
public class TextMessageDto : BaseMessageDto
{
public TextMessageDto()
{
Type = MessageTypeEnum.Text;
}
public string Text { get; set; }
}
}

三、建立傳訊機制

  • 這一篇我們就簡單的以能夠廣播訊息 & 回覆使用者為目標來建立傳訊機制

新增 JsonProvider

  • 因為 Line 接收格式的變數名稱開頭為小寫,但 C# 習慣的命名規則 Class 變數名稱為大寫開頭,所以在傳送 Json 給 Line 的過程中無法直接使用 JsonSerializer 做資料的處理(序列化/反序列化),所以我們需要加工一下,自己包一個 Provider 來處理 Json 轉換的問題。(會在下面的實作用到)
  • 新增 Providers 資料夾,並新增 JsonProvider.cs 檔案
using System.Text.Json;
using System.Text.Json.Serialization;

namespace LineBotMessage.Providers
{
public class JsonProvider
{
private JsonSerializerOptions serializeOption = new JsonSerializerOptions()
{
** PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
** };

private static JsonSerializerOptions deserializeOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};

public string Serialize<T>(T obj)
{
return JsonSerializer.Serialize(obj, serializeOption);
}

public T Deserialize<T>(string str)
{
return JsonSerializer.Deserialize<T>(str, deserializeOptions);
}
}
}

LineBotService 宣告變數

  • 將以下變數加進 LineBotService 中
// 宣告變數
private readonly string replyMessageUri = "https://api.line.me/v2/bot/message/reply";
private readonly string broadcastMessageUri = "https://api.line.me/v2/bot/message/broadcast";
private static HttpClient client = new HttpClient(); // 負責處理HttpRequest
private readonly JsonProvider _jsonProvider = new JsonProvider();

①建立 Broadcast 機制 文件連結

廣播訊息功能為在 LineBotContorller 上新增一支 API 去負責接收廣播請求並將該請求送至 LineBotService 轉換成 Line 要求的 Http request 格式送至 Line,最後成功送出廣播訊息。

  • 此畫面可以得知,送出廣播訊息的 request 時需要的資訊為
  1. Http Method : Post
  2. Request Uri : https://api.line.me/v2/bot/message/broadcat
  3. Headers : Content-Type, Authorization
  4. Request Body 格式(下方有詳細說明)
  • 以下則對 Headers & Request body 的格式與內容作出說明

程式實作

  • 在 Dtos/Messages 資料夾下新增 Request 資料夾,並新增 BroadcastMessageRequestDto.cs 然後依照文件中的資訊建立 Class 屬性。
namespace LineBotMessage.Dtos
{
public class BroadcastMessageRequestDto<T>
{
public List<T> Messages { get; set; }
public bool? NotificationDisabled { get; set; }
}
}
  • 各位會發現 Messages 的型別是 List<T> 使用到了泛型,這是因為訊息的屬性非常多種的原因,在傳送時先決定好目前要傳送的訊息類型能夠比較簡單直接的實作功能。
  • 在 LineBotService 中加入以下兩個 function
/// <summary>
/// 接收到廣播請求時,在將請求傳至 Line 前多一層處理,依據收到的 messageType 將 messages 轉換成正確的型別,這樣 Json 轉換時才能正確轉換。
/// </summary>
/// <param name="messageType"></param>
/// <param name="requestBody"></param>
public void BroadcastMessageHandler(string messageType, object requestBody)
{
string strBody = requestBody.ToString();
switch (messageType)
{
case MessageTypeEnum.Text:
var messageRequest = _jsonProvider.Deserialize<BroadcastMessageRequestDto<TextMessageDto>>(strBody);
BroadcastMessage(messageRequest);
break;
}

}

/// <summary>
/// 將廣播訊息請求送到 Line
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="request"></param>
public async void BroadcastMessage<T>(BroadcastMessageRequestDto<T> request)
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", channelAccessToken); //帶入 channel access token
var json = _jsonProvider.Serialize(request);
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(broadcastMessageUri),
Content = new StringContent(json, Encoding.UTF8, "application/json")
};

var response = await client.SendAsync(requestMessage);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
  • 最後在 LineBotController 新增接收API
[HttpPost("SendMessage/Broadcast")]
public IActionResult Broadcast([Required] string messageType, object body)
{
_lineBotService.BroadcastMessageHandler(messageType, body);
return Ok();
}

測試

  • 在 Swagger 上進行廣播測試
  • 廣播訊息傳送結果

②建立 Reply 機制 文件連結

  • 此畫面可以得知,送出回覆訊息的 request 時需要的資訊。
  1. Http Method : Post
  2. Request Uri : https://api.line.me/v2/bot/message/reply
  3. Headers : Content-Type, Authorization,
  4. Request Body 格式(下方有詳細說明)
  • 以下則對 Headers & Request body 的格式與內容作出說明

程式實作

  • 在 Dtos/Messages/Request 資料夾新增 ReplyMessageRequestDto.cs 依照文件中的資訊建立 Class 屬性。
namespace LineBotMessage.Dtos
{
public class ReplyMessageRequestDto<T>
{
public string ReplyToken { get; set; }
public List<T> Messages { get; set; }
public bool? NotificationDisabled { get;set; }
}
}
  • LineBotService 加入以下兩個 function
/// <summary>
/// 接收到回覆請求時,在將請求傳至 Line 前多一層處理(目前為預留)
/// </summary>
/// <param name="messageType"></param>
/// <param name="requestBody"></param>
public void ReplyMessageHandler<T>(string messageType, ReplyMessageRequestDto<T> requestBody)
{
ReplyMessage(requestBody);
}

/// <summary>
/// 將回覆訊息請求送到 Line
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="request"></param>
public async void ReplyMessage<T>(ReplyMessageRequestDto<T> request)
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", channelAccessToken); //帶入 channel access token
var json = _jsonProvider.Serialize(request);
var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri(replyMessageUri),
Content = new StringContent(json, Encoding.UTF8, "application/json")
};

var response = await client.SendAsync(requestMessage);
Console.WriteLine(await response.Content.ReadAsStringAsync());
}

測試

  • 要使用回覆功能,要在接收到 webhook event 時帶入收到的 replyToken 進行回覆。
  • 在 LineBotService — ReceiveWebhook function 中修改收到 Message Event 時的動作。
case WebhookEventTypeEnum.Message:
var replyMessage = new ReplyMessageRequestDto<TextMessageDto>()
{
ReplyToken = eventObject.ReplyToken,
Messages = new List<TextMessageDto>
{
new TextMessageDto(){Text = $"您好,您傳送了\"{eventObject.Message.Text}\"!"}
}
};
ReplyMessageHandler("text",replyMessage);
break;
  • 實際效果

四、結語

撒花 ~ 今天的程式內容依舊蠻多的,希望能讓各位簡單地看懂流程建立的方式,下一篇開始會詳細的介紹 Line 提供的訊息格式並帶大家實作,中間有可能會穿插一些相關知識或是簡單的API串接,請各位期待吧!

腦筋急轉彎

  • Messages 使用泛型的原因? 不使用泛型要怎麼正確做 Json 序列化。
  • HttpClient 使用 static 的原因?
  • 如果使用 HttpClientFactory 的話怎麼用?
  • Enum 列舉裏面的數值,除了數字以外,可以使用字串嗎?

範例程式碼

如果想要參考今天範例程式碼的部份,下面是 Git Repo 連結,方便大家參考。

Day7_Message Introducing & Broadcasting

>>下一篇>>

--

--