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

在 Line 的官方帳號可以設定進入時的選單,讓使用者快速點選,設定方式可以從官方帳號後台(有既定的範本可以參考),或者是用程式自己指定(可以自己控制圖片的座標&設定點選區塊),以下分別介紹

從官方帳號後台

這應該是最方便的方式了,Line 已經提供了一些範本和設計規範,只要照著範本製作一個圖片,並設定每個區塊要觸發的事件,即可完成一個圖文選單

進入 https://manager.line.biz/ 選擇圖文選單 => 建立 => 設計規範 => 下載範本,(或者直接點此下載),下載後會是一個壓縮檔,今天來用這個範例做修改

我們用 PhotoShop 改成我們要的圖片 (這就不介紹啦) https://imgur.com/ev4LQzp.jpg

上傳圖片,並設定 A.B.C 所要觸發的文字

這樣就完成了,是不是很方便阿

從程式指定 Rich Menu

程式就沒有用 Business 後台直接上傳圖片來得那麼方便,但程式可以做到極彈性,它可以自訂圖片的座標來設定 click 的範圍,可以針對使用者來動態的切換 Rich Menu 等等…。Rich Menu 所牽扯的 Api 有點多,故先整理今天會實作的 Api 清單如下

Get rich menu list:取得此 chatbot 已註冊的 Rich Menu 清單 Upload rich menu image:設定 Rich Menu 的圖片 Set default rich menu:設定所檢視的 Rich Menu

新增 LineRichMenuUtility.cs

之前 Message API 我們做了 一個 Utility ,Rich Menu 我們也新增一個

    public class LineRichMenuUtility

    {

    private readonly string accessToken;

    private static string lineMessageApiBaseUrl = "https://api.line.me/v2/bot/richmenu";

    private static string lineMessageApiBaseUrlForAllUser = "https://api.line.me/v2/bot/user/all/richmenu";

    public LineRichMenuUtility(IOptions<LineSetting> lineSetting)

    {

    accessToken = lineSetting.Value.ChannelAccessToken;

    }

    ....

    }

Get rich menu list:取得此 chatbot 已註冊的 Rich Menu 清單

RichMenuResponse 這個 class 內容來自於這個文件

    public async Task<RichMenuResponse> GetRichMenuListAsync()

    {

    using (var httpClient = new HttpClient())

    {

    using (var request = new HttpRequestMessage(new HttpMethod("GET"), $"{lineMessageApiBaseUrl}/list"))

    {

    request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");

    var response = await httpClient.SendAsync(request);

    var result = await response.Content.ReadAsStringAsync();

    var richMenuRes = JsonConvert.DeserializeObject<RichMenuResponse>(result);

    return richMenuRes;

    }

    }

    }

Upload rich menu image:設定 Rich Menu 的圖片

    public async Task UploadRichMenuImage(string imgUrl, string richMenuId)

    {

    using (var httpClient = new HttpClient())

    {

    using (var request = new HttpRequestMessage(new HttpMethod("POST"), $"lineMessageApiBaseUrl/{richMenuId}/content"))

    {

    request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");

    var imgBytes = GetImage(imgUrl);

    request.Content = new ByteArrayContent(imgBytes);

    request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

    var response = await httpClient.SendAsync(request);

    var results = await response.Content.ReadAsStringAsync();

    };

    }

    }

    private byte[] GetImage(string url)

    {

    var request = (HttpWebRequest)WebRequest.Create(url);

    var response = (HttpWebResponse)request.GetResponse();

    using (Stream dataStream = response.GetResponseStream())

    {

    if (dataStream == null)

    return null;

    using (var sr = new BinaryReader(dataStream))

    {

    byte[] bytes = sr.ReadBytes(100000000);

    return bytes;

    }

    }

    }

Set default rich menu:設定預設的 Rich Menu

CreateRichmenu 這個 class 內容來自於這個文件

自己用程式定義的麻煩點就在這,要設長寬和這種座標與Action,但未來可以考慮將一些常用範本用程式封裝起來,直接使用

    public async Task<Richmenu> SetDefaultRichMenuAsync()

    {

    using (var httpClient = new HttpClient())

    {

    using (var request = new HttpRequestMessage(new HttpMethod("POST"), lineMessageApiBaseUrl))

    {

    request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");

    var defaultRichMenu = new CreateRichmenu();

    defaultRichMenu.size = new CreateSize(1200, 810);

    defaultRichMenu.selected = true;

    defaultRichMenu.name = "Default RichMenu";

    defaultRichMenu.chatBarText = "Kyle's Wedding";

    defaultRichMenu.areas = new List<CreateArea>()

    {

    new CreateArea(boundx:0,boundy:0,width:800,height:810, _action: new MessageAction("婚紗輪播"))

    };

    var postJson = JsonConvert.SerializeObject(defaultRichMenu, new JsonSerializerSettings

    {

    NullValueHandling = NullValueHandling.Ignore,

    ContractResolver = new DefaultContractResolver

    {

    NamingStrategy = new CamelCaseNamingStrategy()

    }

    });

    request.Content = new StringContent(postJson);

    request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");

    var response = await httpClient.SendAsync(request);

    var results = await response.Content.ReadAsStringAsync();

    var richmenu = JsonConvert.DeserializeObject<Richmenu>(results);

    return richmenu;

    };

    }

    }

Set default rich menu:設定所"指定"的 Rich Menu

    public async Task SetDefaultRichMenuAsync(string richId)

    {

    using (var httpClient = new HttpClient())

    {

    using (var request = new HttpRequestMessage(new HttpMethod("POST"), $"{lineMessageApiBaseUrlForAllUser}/{richId}"))

    {

    request.Headers.TryAddWithoutValidation("Authorization", $"Bearer {accessToken}");

    var response = await httpClient.SendAsync(request);

    var results = await response.Content.ReadAsStringAsync();

    }

    }

    }

回到 Api Controller,我們在 app 啟動的時候給設定 Rich Menu

InitRichMenu 實作

    public async Task InitRickMenu()

    {

    var rickResponse = await lineRichMenuUtility.GetRichMenuListAsync();

    if(rickResponse.richmenus.Count == 0)

    {

    // 沒有取到任何 Rich Menu,設定預設

    var richMenu = await lineRichMenuUtility.SetDefaultRichMenuAsync();

    await lineRichMenuUtility.UploadRichMenuImage("https://i.imgur.com/ev4LQzp.jpg", richMenu.richMenuId);

    }

    else

    {

    // 取得預設並設定該 Rich Menu

    var defaultRich = rickResponse.richmenus.First(c => c.name == "Default RichMenu");

    await lineRichMenuUtility.SetDefaultRichMenuAsync(defaultRich.richMenuId);

    }

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