---
# System prepended metadata

title: dotnet Core Line-Bot 快速筆記

---

[TOC]
### 1) 安裝套件 (nuget)
```
 Line.Messaging 
```
### 2) 新增LineBot控制器

#### 2.1) 注入服務
在Program.cs內新增
```csharp=
builder.Services.AddHttpContextAccessor();
```
#### 2.2) 注入(DI) IServiceProvider 
在LineBotController.cs中新增
```csharp=
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly HttpContext _httpContext;
public LineBotController(IServiceProvider serviceProvider)
{
    _httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
    _httpContext = _httpContextAccessor.HttpContext;
    //...略
}
```
#### 2.3) 建立LineBot設定檔
```csharp=
public class LineBotConfig
{
    public string channelSecret { get; set; }
    public string accessToken { get; set; }
}
```
在LineBotController.cs中新增變數
```csharp=
private LineBotConfig _lineBotConfig;
```
在LineBotController的建構式中追加變數設定

```csharp=
_lineBotConfig = new LineBotConfig();
_lineBotConfig.channelSecret = "YOUR channelSecret";
_lineBotConfig.accessToken = "YOUR accessToken";
     
```

### 3) 撰寫相關處理程式

```csharp=
[HttpPost("run")]
public async Task<IActionResult> Post()
{
    var events = await _httpContext.Request.GetWebhookEventsAsync(_lineBotConfig.channelSecret);
    var lineMessagingClient = new LineMessagingClient(_lineBotConfig.accessToken);
    var lineBotApp = new LineBotApp(lineMessagingClient);
    await lineBotApp.RunAsync(events);
    
    return Ok();
}
```
#### 3.1) 改用 WebhookRequestMessageHelper (擴展方法)
```csharp=
/*
MIT License

Copyright (c) 2017 pierre3

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

public static class WebhookRequestMessageHelper
{
    public static async Task<IEnumerable<WebhookEvent>> GetWebhookEventsAsync(this HttpRequest request, string channelSecret, string botUserId = null)
    {
        if (request == null) { throw new ArgumentNullException(nameof(request)); }
        if (channelSecret == null) { throw new ArgumentNullException(nameof(channelSecret)); }

        var content = "";
        using (var reader = new StreamReader(request.Body))
        {
            content = await reader.ReadToEndAsync();
        }

        var xLineSignature = request.Headers["X-Line-Signature"].ToString();
        if (string.IsNullOrEmpty(xLineSignature) || !VerifySignature(channelSecret, xLineSignature, content))
        {
            throw new InvalidSignatureException("Signature validation faild.");
        }

        dynamic json = JsonConvert.DeserializeObject(content);

        if (!string.IsNullOrEmpty(botUserId))
        {
            if (botUserId != (string)json.destination)
            {
                throw new UserIdMismatchException("Bot user ID does not match.");
            }
        }
        return WebhookEventParser.ParseEvents(json.events);
    }

    internal static bool VerifySignature(string channelSecret, string xLineSignature, string requestBody)
    {
        try
        {
            var key = Encoding.UTF8.GetBytes(channelSecret);
            var body = Encoding.UTF8.GetBytes(requestBody);

            using (HMACSHA256 hmac = new HMACSHA256(key))
            {
                var hash = hmac.ComputeHash(body, 0, body.Length);
                var xLineBytes = Convert.FromBase64String(xLineSignature);
                return SlowEquals(xLineBytes, hash);
            }
        }
        catch
        {
            return false;
        }
    }

    private static bool SlowEquals(byte[] a, byte[] b)
    {
        uint diff = (uint)a.Length ^ (uint)b.Length;
        for (int i = 0; i < a.Length && i < b.Length; i++)
            diff |= (uint)(a[i] ^ b[i]);
        return diff == 0;
    }
}
```
>參考來源 [link](https://ithelp.ithome.com.tw/articles/10217452)

#### 3.2) LineBotApp.cs
```csharp=
public class LineBotApp : WebhookApplication
{
    private readonly LineMessagingClient _messagingClient;
    public LineBotApp(LineMessagingClient lineMessagingClient)
    {
        _messagingClient = lineMessagingClient;
    }

    protected override async Task OnMessageAsync(MessageEvent ev)
    {
        var result = null as List<ISendMessage>;

        switch (ev.Message)
        {
            //文字訊息
            case TextEventMessage textMessage:
                {
                    //頻道Id
                    var channelId = ev.Source.Id;
                    //使用者Id
                    var userId = ev.Source.UserId;
                    
                    //回傳 hellow
                    result = new List<ISendMessage>
                    {
                        new TextMessage("hellow")
                    };
                }
                break;
        }

        if (result != null)
            await _messagingClient.ReplyMessageAsync(ev.ReplyToken, result);
    }
}
```
### 4) 測試
#### 4.1)利用ngrok轉換服務
![](https://hackmd.io/_uploads/rkG6m9-d3.png)

#### 4.2)設定Line WebHook
![](https://hackmd.io/_uploads/H1rVm9bu2.png)
