LINE Loginを利用したWebアプリを ASP.NET Core + OpenID Connectで実装する(その2:メールアドレスを取得する)

この記事は、以下のエントリの続きとなります。

pierre3.hatenablog.com

今回は、LINEでログイン時にユーザーのメールアドレスを取得する方法について述べます。

メールアドレスの取得権限を取得する

LINE Loginでメールアドレスを取得するには、LINE Loginアカウントの設定画面から申請して取得権限を付与してもらう必要があります。

f:id:pierre3:20180331212033p:plain:w600

LINE Developers コンソール でLINE Loginアカウントを選択して、Channel基本設定画面を開きます。
OpenID Connect - メールアドレス」の「申請」をクリックすると以下のような申請画面が表示されます。

f:id:pierre3:20180331212045p:plain:w600

この画面から申請を行うのですが、

本サービスで、メールアドレスの取得/利用目的について、ユーザに通知、明示、または同意取得している部分のスクリーンショットをアップロードしてください。

と記載されている通り、申請するには上記のようなスクリーンショットをアップロードする必要があります。

メールの取得/利用目的を明示したログイン画面を作る

前回までのアプリケーションでは、未ログイン時にトップページへアクセスするとLINEのログイン画面が直接表示されるようになっていましたが、以下のようなログイン画面を挟むように修正します。

f:id:pierre3:20180331223950p:plain:w600

この画面のスクリーンショットを取って、申請に使用することとします。

View とControllerを修正する

・Views/AccountフォルダにLogin.cshtmlを作成します。

<-- Login.cshtml -->

@{
    ViewData["Title"] = "Login";
}

<div class="jumbotron">
    <div class="panel panel-info center-block">
        <div class="panel-heading">ログインしてください</div>
        <div class="panel-body">
            LINEアカウントを利用してログインを行います。<br />
            <hr />
            本Webサービスでは、ログイン時の認証画面にて許可を頂いた場合のみ、あなたのLINEアカウントに登録されているメールアドレスを取得します。<br />
            取得したメールアドレスは、以下の目的以外では使用いたしません。また、法令に定められた場合を除き、第三者への提供はいたしません。
            <ul>
                <li>ユーザーIDとしてアカウントの管理に利用</li>
                <li>パスワード再発行時の本人確認に利用</li>
            </ul>
            <hr/>
        </div>
        <p class="text-center"><a asp-area="" asp-controller="Account" asp-action="LineLogin">
            <img src="~/images/btn_login_base.png"/>
        </a>
        </p>
    </div>
</div>

・Viewに対応するメソッドをAccountControllerに追加します。

public class AccountController : Controller
{
    //ログイン画面(/Account/Login)
    public IActionResult Login()
    {
        return View();
    }

    //ログイン画面の「LINE Log in」ボタンのアクション
    public IActionResult LineLogin()
    {
        return Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme);
    }

    public IActionResult Logout()
    {
        return SignOut(new AuthenticationProperties { RedirectUri = "/" }, CookieAuthenticationDefaults.AuthenticationScheme);
    }
}

これでパーツは揃いました。あとはControllerで[Authorize]属性を付けたページにアクセスした際に、ログインページ(/Acount/Login)にリダイレクトするように設定するだけです。

現在LIENのログイン画面にリダイレクトされているのは、StartupクラスのConfigurationService() に記述した以下の設定があるためです。

options.DefaultChallengeScheme = OpenIdConnectDefault.AuthenticationScheme;

この1行を削除するだけで、既定のログインページ「/Account/Login」へリダイレクトされるようになります。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.Configure<LineSettings>(Configuration.GetSection("LineSettings"));

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        
        //この設定により、未ログイン時はLINEのログイン画面へリダイレクトされる。
        //options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(o =>
    {
        //...
    });
}

ログイン画面のスクリーンショットを取ったら、先ほどの申請画面にアップロードします。

f:id:pierre3:20180401081819p:plain:w600

同意する項目のチェックボックスにチェックを入れて「申請」をクリックします。
申請が受理されると以下の様に「申請済み」と表示され、メールアドレスの取得ができるようになります。

( 私が試したところ、「申請」クリック後即座に「申請済み」となりメールアドレスの取得も可能となりました。(画像認識で自動判定とかしているのかしら?))

f:id:pierre3:20180401082656p:plain:w600

OpenID Connectでメールアドレスを取得するように設定する

メールアドレスの取得権限が付与されたら、あとは認証時のスコープに"email"を追加するだけです。

//Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.Configure<LineSettings>(Configuration.GetSection("LineSettings"));

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        //options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(o =>
    {
        o.ClientId = Configuration["LineSettings:LoginClientId"];
        o.ClientSecret = Configuration["LineSettings:LoginClientSecret"];
        o.ResponseType = OpenIdConnectResponseType.Code;
        o.UseTokenLifetime = true;
        o.SaveTokens = true;
        //スコープにemailを追加
        o.Scope.Add("email");
                
        o.Configuration = new OpenIdConnectConfiguration
        {
            Issuer = "https://access.line.me",
            AuthorizationEndpoint = "https://access.line.me/oauth2/v2.1/authorize?bot_prompt=aggressive",
            TokenEndpoint = "https://api.line.me/oauth2/v2.1/token"
        };
        o.TokenValidationParameters = new TokenValidationParameters
        {
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(o.ClientSecret)),
            NameClaimType = "name",
            ValidAudience = o.ClientId
        };
    });
}

取得したメールアドレスを確認する

トップページに取得したメールアドレスを表示するようにHome/IndexのViewを書き換えます。
メールアドレスの取得にはClaimTypes.Emailを使用します。

@{
    ViewData["Title"] = "Home Page";
}
@using System.Security.Claims;

<div class="jumbotron">
    <div class="panel panel-default center-block">
        <div class="panel panel-heading">
            <div class="row">
                <div class="col-md-10 col-sm-10 col-xs-8">
                    <p>
                        <img src="@User.FindFirst("picture")?.Value" width="32" height="32" alt="User Image" />
                        @User.Identity.Name (@User.FindFirst(ClaimTypes.Email)?.Value)
                    </p>
                </div>
                <div class="col-md-2 col-sm-2 col-xs-4">
                    <a class="btn btn-default" role="button" asp-area="" asp-controller="Account" asp-action="Logout">Logout</a>
                </div>
            </div>
        </div>
        <div class="panel panel-body">
            <h2>プッシュメッセージ送信テスト</h2>
            <form role="form" asp-area="" asp-controller="Home" asp-action="PushMessage" method="post">
                <input type="text" name="pushMessage" class="form-control" placeholder="プッシュするメッセージを入力してください">
                <button type="submit" class="btn btn-default">送信</button>
            </form>
        </div>
    </div>
</div>

これで準備完了です。確認してみましょう。

・ログイン時の認可画面で取得項目にメールアドレスが追加されています。

f:id:pierre3:20180401085958p:plain:w500

・メールアドレスの取得を許可してログイン。取得したメールアドレスが表示されました!

f:id:pierre3:20180401090041p:plain:w600

LINE Loginを利用したWebアプリを ASP.NET Core + OpenID Connectで実装する


■ 目次


チュートリアル

今回は、LINE Loginを利用したWebアプリケーションをASP.NET Core +OpenID Connectで実装する方法について解説したいと思います。

はじめに

ここでは、Visual Studio 2017を使って「「ASP.NET Core Webアプリケーション」テンプレートからLINE Loginに対応したWebアプリを実装する手順」について解説します。

なお、ASP.NET CoreでOpenID Connectを使用する手順は、こちらの記事を参考にさせていただきました。

blog.shibayan.jp

また、この記事の解説に使用したサンプルアプリをGitHubに上げています。 とりあえず動かしてみたいという方はこちらをCloneして試してみてください。

github.com

※ このサンプルアプリを試す際は、以下の手順でLINE LoginのChannel情報等を設定してください。

  • appSettings.Base.json ファイルをappSettings.jsonにリネームする
  • リネームしたファイル内の"LineSettings"以下を、ご自身のLINE Loginアカウント情報に書き換える
    • LINE Login アカウントのChannel ID
    • LINE Login アカウントのChannel Secret
    • 連携するBotアカウント のAccess Token

LINE の設定

LINE Loginアカウントを作成する

まずは、以下の公式ドキュメントに従ってLINE Loginアカウントを作成します。

LINEログインを利用するには

連携するBotアカウントの用意

ログイン時に友達追加するBotのアカウントも作成しておきます。プッシュメッセージを使用するのでDeveloper Trialプランで作成します。 (もちろん、すでに作成済みのBotアカウントで試しても良いです)

Messaging APIを利用するには

なお、今回はログイン時に友達追加できることと、Webアプリからプッシュメッセージが送信できることが確認できれば良いのでWebhookの設定は不要です。

LINE Login アカウントの設定

LINE Loginアカウントを作成したら「Channel基本設定」を確認します。

  • Channel ID とChannel Secretは後で必要となるので控えておきます
  • 「このチャネルにリンクされたボット」欄で、作成済みのBotの一覧から先ほど用意したBotを選択します
  • 「アプリタイプ」にはWEBを選択します

f:id:pierre3:20180331072144p:plain:w400

左側のメニューで「アプリ設定」を選択し、ログイン後のリダイレクト先を設定します。

f:id:pierre3:20180331065551p:plain:w600

  • リダイレクト先のパスは「/signin-oidc」とします。
  • ローカルデバッグでの確認であれば、Visual Studio でのデバッグ実行時のURLでOKです。(デバッグ時のURLはPropertiesフォルダ内にある「launchSettings.json」で確認できます)

Webアプリケーションの作成

ASP.NET Coreのプロジェクトを作成する

メニュー[ファイル]-[新規作成]-[プロジェクト...]から「ASP.NET Core Webアプリケーション」を選択

f:id:pierre3:20180329215712p:plain:w600

テンプレートのタイプは「Webアプリケーション(モデル ビュー コントローラ)」を選択。「認証なし」のままで作成します。

f:id:pierre3:20180329220425p:plain:w600

NuGetパッケージを追加する

「ソリューションのNuGetパッケージの管理」画面または、パッケージマネージャコンソールで Microsoft.AspNetCore.Authentication.OpenIdConnect をインストールします。

PM> Install-Package Microsoft.AspNetCore.Authentication.OpenIdConnect

AppSettings.json に LINE Login のChannel情報を設定する

予め、appsettings.json に LINE Login のアカウント登録時に取得した ChannelIdとChannel Secretの値を追加しておきます。

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "LineSettings": {
    "LoginClientId": "156xxxxxxxxxx",
    "LoginClientSecret": "934a4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  }
}

StartupクラスでOpenID Connectを使用するように設定する

あとは、Stertup.cs で以下の記述を追加するだけでOKです。 (詳細は冒頭でリンクしたしばやん先生の記事をご確認ください)

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        //OpenID Connectを使用するための記述 ここから==>
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddOpenIdConnect(o =>
        {
            //ClientIdとClientSecretをConfiguration(appsettings.json)から取得。階層のあるデータはコロン(:)で区切って指定する
            o.ClientId = Configuration["LineSettings:LoginClientId"];
            o.ClientSecret = Configuration["LineSettings:LoginClientSecret"];
            o.ResponseType = OpenIdConnectResponseType.Code;
            o.UseTokenLifetime = true;
            o.SaveTokens = true;
            
            o.Configuration = new OpenIdConnectConfiguration
            {
                Issuer = "https://access.line.me",
                AuthorizationEndpoint = "https://access.line.me/oauth2/v2.1/authorize",
                TokenEndpoint = "https://api.line.me/oauth2/v2.1/token"
            };

            o.TokenValidationParameters = new TokenValidationParameters
            {
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(o.ClientSecret)),
                NameClaimType = "name",
                ValidAudience = o.ClientId
            };
        });
        //<==ここまで
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();
        
        //Authentication Middleware を使う
        app.UseAuthentication();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

これで、Controller の[Authorize]属性を付けたメソッドにアクセスすると、LINEアカウントのログイン画面にリダイレクトされるようになります。

・Controllers/HomeController.c

//<HomeController.cs>

public class HomeController:Controller
{
    [Authorize]
    public IActionResult Index()
    {
        return View();
    }
}

f:id:pierre3:20180329234308p:plain:w300

ログイン時にBotアカウントを友達追加できるようにする

ユーザーがLINEアカウントの認可画面で関連するBotを友達追加するか否かを選択できるようにするには、認可URLに、クエリパラメータbot_promptを追加します。

先ほどのコードのAddOpenIdConnect() に渡すラムダ式内、AuthorizationEndpoint に設定するURLを以下の様に修正します。

o.Configuration = new OpenIdConnectConfiguration
{
    Issuer = "https://access.line.me",
    AuthorizationEndpoint = "https://access.line.me/oauth2/v2.1/authorize?bot_prompt=normal",
    TokenEndpoint = "https://api.line.me/oauth2/v2.1/token"
};

bot_prompt の値は、normal、aggressiveの2種類があり、aggressiveを設定した場合は、認可画面を閉じた後にボット追加用の確認画面が表示されるようになります。

f:id:pierre3:20180330223612p:plain:w300

  • aggressive の場合(認可画面からログイン後、確認用のダイアログが表示される)

f:id:pierre3:20180330224231p:plain:w300

なお、「ブロック解除」と表示されているのは、動作確認用に一度友達追加したBOTを「ブロック」しているためです。初めて友達追加する場合は「友達追加」と表示されます。

取得したユーザー情報を確認する

デバッカで確認してみると、取得したユーザーの情報はUser.Identity.Claimesに格納されているのが分かります。

f:id:pierre3:20180331095929p:plain

ClaimsプロパティはKey-Valueのコレクションとなっていて、値はUser.FindFirst()メソッドにKeyを指定して取得することができます。
定義済みのClaimは"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"の様にURLで指定する必要がありますが、 System.Security.Claims.ClaimTypes に定数として定義されているのでこれを使用します。

//View(Razor)でもControllerでも同じ様に取得できます
var name =User.FindFirst("picture").Value;  //ユーザー名はUser.Identity.Nameでも取得可
var picture = User.FindFirst("picture").Value;  //画像のURLを取得
var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier).Value;  //LINEアカウントに紐づくユーザーID

それでは、ログインしたユーザーの情報が確認できるように、トップページ(Home/Index)のViewを書き換えてみます。

<-- Index.cshtml -->

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <div class="panel panel-default center-block">
        <div class="panel panel-heading">
            <div class="row">
                <div class="col-md-10 col-sm-10 col-xs-8">
                    <p><img src="@User.FindFirst("picture").Value" width="32" height="32" alt="User Image"> @User.Identity.Name</p>
                </div>
                <div class="col-md-2 col-sm-2 col-xs-4">
                    <a class="btn btn-default" role="button" asp-area="" asp-controller="Account" asp-action="Logout">Logout</a>
                </div>
            </div>
        </div>
    </div>
</div>

ついでに「Logout」ボタンでログアウトできるように、AccountControllerを追加してLogout()メソッドを実装します。

//<AccountController.cs>

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Mvc;

namespace LineLoginSampleApp.Controllers
{
    public class AccountController : Controller
    {
        public IActionResult Logout()
        {
            return SignOut(new AuthenticationProperties { RedirectUri = "/" }, CookieAuthenticationDefaults.AuthenticationScheme);
        }
    }
}

ここで、一度デバッグ実行してみましょう。 LINEのログイン画面からログイン後、以下の様にユーザー名と画像が表示されればOKです。
「Logout」ボタンも確認してみましょう。ボタン押下後、再びLINEのログイン画面にリダイレクトされることが確認できると思います。

f:id:pierre3:20180331093703p:plain:w400

Botからプッシュメッセージを送ってみる

次は、LINEログイン時にBotを友達追加したユーザーに対してBotからプッシュメッセージを送ってみましょう。

Messaging APIのライブラリを追加

LINE Messaging APISDKをNuGetからインストールします。

PM > Install-Package Line.Messaging
appSettings.jsonBotのChannel アクセストークンを追加する

LINE Developersコンソールを開き、今回用意したBotを選択します。
Channel基本設定で「アクセストークン(ロングターム)」を確認し、appSettings.jsonに追加します。

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "LineSettings": {
    "LoginClientId": "156xxxxxxxxxx",
    "LoginClientSecret": "934a4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "MessagingAccessToken": "zd1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}
メッセージを送ってみる

画面から送信するメッセージの内容を設定して送れるようにViewとControllerを書き換えます。

・Home/Index.cshtml に、メッセージの入力ボックスと送信ボタンを追加します。

<-- Index.cshtml -->

@{
    ViewData["Title"] = "Home Page";
}
@using System.Security.Claims;

<div class="jumbotron">
    <div class="panel panel-default center-block">
        <div class="panel panel-heading">
            <div class="row">
                <div class="col-md-10 col-sm-10 col-xs-8">
                    <p>
                        <img src="@User.FindFirst("picture")?.Value" width="32" height="32" alt="User Image" />
                        @User.Identity.Name
                    </p>
                </div>
                <div class="col-md-2 col-sm-2 col-xs-4">
                    <a class="btn btn-default" role="button" asp-area="" asp-controller="Account" asp-action="Logout">Logout</a>
                </div>
            </div>
        </div>
        <div class="panel panel-body">
            <h2>プッシュメッセージ送信テスト</h2>
            <form role="form" asp-area="" asp-controller="Home" asp-action="PushMessage" method="post">
                <input type="text" name="pushMessage" class="form-control" placeholder="プッシュするメッセージを入力してください">
                <button type="submit" class="btn btn-default">送信</button>
            </form>
        </div>
    </div>
</div>

・Views/Home/ の配下にメッセージ送信後の遷移先となるView(PushMessage.cshtml)を作成しておきます。

<-- PushMessage.cshtml -->

@{
    ViewData["Title"] = "PushMessage";
}

<h2>送信しました</h2>
<a class="btn btn-default" role="button" asp-area="" asp-controller="Home" asp-action="Index">戻る</a>

・HomeControllerにPushMessageメソッドを追加してそこに送信処理を記述します。

//<HomeController.cs>

public class HomeController : Controller
{
    private LineSettings lineSettings;
    public HomeController(IOptions<LineSettings> options)
    {
        lineSettings = options.Value;
    }

    [Authorize]
    public IActionResult Index()
    {
        return View();
    }
    
    [HttpPost]
    [Authorize]
    public async Task<IActionResult> PushMessage(string pushMessage)
    {
        var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier).Value;
         using (var line = new Line.Messaging.LineMessagingClient(lineSettings.MessagingAccessToken))
        {
            await line.PushMessageAsync(userId, pushMessage);
        }
        return View();
    }
}

なお、appSettings.jsonに設定したアクセストークンを、HomeControllerのコンストラクタの引数LineSettingsオブジェクトから取得していますが、これを行うためにはStartupクラスでアプリケーション設定をDI(Dependency Injection)するよう設定しておく必要があります。

・ModelsディレクトリにLineSettingsクラスを追加します。

//<LineSettings.cs>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace LineLoginSampleApp.Models
{
    public class LineSettings
    {
        public string LoginClientId { get; set; }
        public string LoginClientSecret { get; set; }
        public string MessagingAccessToken { get; set; }
    }
}

・StartupクラスのConfigurationServices()メソッドに、アプリケーション設定をDIするコードを追加します。

//<Stertup.cs>

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    
    //ConfigurationからLineSettingsセクションを抽出してDependency Injectionする
    services.Configure<LineSettings>(Configuration.GetSection("LineSettings"));
    
   //...
}

これで準備完了です。実行してみましょう。

・メッセージを入力して「送信」

f:id:pierre3:20180331161942p:plain:w500

・LINEアプリを確認し、BOTから入力したメッセージが送られてきていれば成功です!

f:id:pierre3:20180331162032p:plain:w500

次は

次は、ユーザーのメールアドレスを取得する方法を見てみようと思いますが、長くなりすぎてしまったので次回にします。

pierre3.hatenablog.com

C#で開発するLINE Botアプリケーション (開発環境編)

(もうだいぶ前の話になってしまいましたが)C# 版LINE Messaging API が、コミュニティSDKとしてLINEの公式サイトに掲載されました!

C#でLINE BOTを開発する環境もいろいろと整ってきましたので、この辺で少し整理したいと思います。

■ 目次

開発環境

開発環境としては、大きく分けて以下の2つがあります。

MacLinuxで開発したい方は前者を選択することになります。
Windowsで開発する場合は、どちらでもお好きな方を。

クロスプラットフォームでの開発

日本マイクロソフト@kenakamu さんがyomanのテンプレートや導入記事を公開してくださっています。

テンプレート

yoman テンプレートは、C#以外にもgo、nodejs、pyhon、rubyなどにも対応しています。

github.com

チュートリアル

Azure Functions (v2.0) で実装する方法と、ASP.NET CoreのWeb Apps で実装する方法の2種類が用意されています。

特にWeb Apps 編の記事は、LINE Bot開発入門者向けのハンズオンセミナーのテキストとして使用しているものです。
入門者の方は、まずはこちらの手順に従って試してみるのが良いでしょう。

line.connpass.com

Visual Studio 2017での開発

テンプレート

Visual Studio Market Place にてプロジェクトテンプレートを公開しています。

marketplace.visualstudio.com

このテンプレートをインストールすると、以下の3種類のプロジェクトが作成できるようになります。

  • Azure Functions 用テンプレート(LINE Bot Function)
  • Web Apps 用テンプレート (LINE Bot Application)
  • Bot Frameworkに対応したWeb Appsのテンプレート(LINE with Bot Framework Application)

チュートリアル

各テンプレートの利用手順は、以下を参照してください

  • Aure Function

pierre3.hatenablog.com

開発を便利にするツールなど

LINESimulator

アプリケーションのデバッグに非常に便利なLINE サーバーのシミュレータです。
node.js 製ですので、C#に限らずあらゆる開発環境で利用可能です。

github.com

line-richmenus-manager

Botアプリケーションの使い勝手を格段に向上する「リッチメニュー」の作成をサポートするアプリケーションです。
リッチメニューの作成とLINE サーバーへのアップロードが可能です。

github.com

C#で開発するLINE Botアプリケーション(環境構築:Visual Studio 2017 + Azure Functions編)

この記事では、Visual Studio 2017とAzure Functionsを使用してLINE Bot アプリケーションを開発を行う手順を解説します。

1.Azure アカウント、LINE@ アカウントの準備

Microsoft Azure アカウント

Microsoft  Azureのアカウントを持っていない場合は、作成しておきます。

Azure Functions では無料のサービスプランを選択することはできないため、3000円/月のクレジットが1年間付いてくる「Visual Studio Dev Essentials」の利用をお勧めします。

LINE@アカウント

LINE Messaging API を利用するLINE@アカウントを作成します。

作成や設定の手順はこちらの記事の「LINE のセットアップ」を参照ください。 - qiita.com

2.プロジェクトテンプレートをインストールし、LINE Bot Function のプロジェクトを作成する

  1. Visual Studio Market Placeから「LINE Bot C# Template」をインストールします。
    以下のリンクで「Download」をクリックしてダウンロードしたVSIXファイルを実行するか、Visual Studioのメニュー[ツール]-[拡張機能と更新プログラム...]から「LINE Bot C# Template」を検索してインストールします。 marketplace.visualstudio.com

  2. インストール後、Visual Studio 2017 を起動し、メニューから[ファイル]-[新規作成]-[プロジェクト]を選択します。

  3. 「新しいプロジェクト」ダイアログで[Visual C#]-[Cloud]のカテゴリ内にある「LINE Bot Function」を選択します。
  4. プロジェクト名を入力し、「OK」をクリックします。(ここで入力したプロジェクト名が作成するAzure Functionの関数名の既定値となります。)

f:id:pierre3:20180318160255p:plain:w800

3.作成されたプロジェクトをビルドする

メニュー[ビルド]-[ソリューションのビルド]を選択し、ビルドエラーがないことを確認します。

4.ビルドしたFunctionをAzureに発行する

  • ソリューションエクスプローラで、作成したLINE Bot Functionのプロジェクトを右クリックし、ポップアップメニューで[発行...]を選択します

f:id:pierre3:20180318160435p:plain:w400

  • 次の画面で「Azure 関数アプリ」-「新規作成」を選択し、 「発行」をクリックします。

f:id:pierre3:20180318160708p:plain:w800

  • App Serviceの作成ダイアログで、作成するFuncsion App Serviceの設定を行います。

f:id:pierre3:20180318161128p:plain:w400

  • 「作成」をクリックするとAzure FunctionのApp Serviceが作成され、そこにビルドしたアプリケーションが発行されます。

5.LINE Developpersに、作成したFunctionのURLを登録する。

LINE Developers Console で、作成したBotアカウントを選択し、Channel基本設定画面を開きます。

f:id:pierre3:20180318162734p:plain:w800

  • メッセージ送受信設定の「Webhook送信」を「利用する」に設定します。
  • Botグループトーク参加」を「利用する」に設定します。
  • 「Webhook URL」に、作成したFunctionのURLを入力します。
    入力するAzure FunctonのURLは、既定では『https://{App ServiceのApp Name}.azurewebsites.net/api/{Function名}』となります。

f:id:pierre3:20180318164136p:plain:w600

  • LINE@機能の利用の「自動応答メッセージ」及び「友だち追加時あいさつ」を「利用しない」に設定します。

f:id:pierre3:20180318165111p:plain:w600

また、この画面に記載されているChannel Secret とアクセストークンの値は、この後の設定で必要となりますので確認しておきます。

6.LINE Messaging APIの 「Channel Secret」および「Channel Access Token」をアプリケーション設定に登録する

  1. Azureポータルで、作成したAzure Functionを選択して[Application Settings]画面を開きます。
  2. アプリケーション設定の一覧に、先ほどのChannel基本設定画面で確認した「Channel Secret」と「Channel Access Token(アクセストークン(ロングターム))」を追加します。

f:id:pierre3:20180318170305p:plain:w800

7.デバッグ情報通知用に自分のLINEアカウントを設定する

アプリケーション内のエラーの情報やデバッグ情報の通知先として、自分のLINEアカウントのUserIDを登録します。User IDは、Channel基本設定ページの一番下に記載されています。

f:id:pierre3:20180318171239p:plain:w600

これを、「DebugUser」という名前で、Azure Functionのアプリケーション設定に追加します。

f:id:pierre3:20180318171018p:plain:w800

8.動作確認

確認する端末でBOTアカウントを友達登録(※)し、メッセージを送ってみましょう。

送信したメッセージと同じ内容がBOTアカウントから返信されればOK。設定完了です。

(※)Channel基本設定(LINE Developpers)画面に表示されるQRコードを読み取ることで友達登録が可能です。

LINE BOTアプリケーションのエラーを自分のLINEアカウントに通知する

Visual StudioとAzure Functionsで作るLINE BOTアプリケーション入門 - pierre3のブログの続きです。

LINE BOT 内のエラーを自分のLINEアカウントに通知する

BOTアプリケーション内で発生した(処理できない)例外の内容等は、Logに記録するだけでも良いのですが、自分のLINEアカウント宛に通知できるようにするとデバッグが非常にはかどります。

f:id:pierre3:20171008215758p:plain:w300

BOTアカウントを管理するLINEアカウント(自分のLINEアカウント)のユーザーIDはLINE Developers consoleのChannel基本設定ページで確認できます。このユーザーIDにエラーの内容をPush通知するように設定します。

f:id:pierre3:20171008222411p:plain:w600

LINE BOT Functionでの使用例

通知先のユーザーIDはアプリケーション設定に登録しておきます。
Azure ポータルで以下の様に設定しておきます。今回は"DebugUser"という名前で追加しました。

f:id:pierre3:20171008145027p:plain:w500

あとは、LINE Messaging APIがエラーレスポンスを返した際の例外処理に、エラーの詳細をPush通知するコードを記述すればOKです。

public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
    //Webhookイベントを取得する
    IEnumerable<WebhookEvent> events;
    try
    {
        var channelSecret = System.Configuration.ConfigurationManager.AppSettings["ChannelSecret"];
        events = await req.GetWebhookEventsAsync(channelSecret);
    }
    catch (InvalidSignatureException e)
    {
        return req.CreateResponse(HttpStatusCode.Forbidden, new { Message = e.Message });
    }
    
    //Webhookイベントを処理する
    try
    {
        var connectionString = System.Configuration.ConfigurationManager.AppSettings["AzureWebJobsStorage"];
        var tableStorage = await LineBotTableStorage.CreateAsync(connectionString);
        var blobStorage = await BlobStorage.CreateAsync(connectionString, "linebotcontainer");
        var app = new LineBotApp(lineMessagingClient, tableStorage, blobStorage, log);
        await app.RunAsync(events);

    }
    //LINE Messaging APIがエラーレスポンスを返した時の例外
    catch (LineResponseException e) 
    {
        //ログ出力        
        log.Error(e.ToString()); 
    
    //アプリケーション設定に登録しておいたユーザーIDにPush通知
        var debugUserId = System.Configuration.ConfigurationManager.AppSettings["DebugUser"];
        if (debugUserId != null)
        {
            await lineMessagingClient.PushMessageAsync(debugUserId, e.ResponseMessage.ToString());
        }
    }
    catch (Exception e)
    {
        log.Error(e.ToString());
    }

    return req.CreateResponse(HttpStatusCode.OK);
}

Visual StudioとAzure Functionsで作るLINE BOTアプリケーション入門

以前の記事で紹介した LINE BOT開発用のAzure Functionsプロジェクトテンプレートですが、 サンプルコードを大幅に追加しました。
(詳しくはMarketplaceのRelease Notesを確認してみてください。)

marketplace.visualstudio.com

この機会に是非、使ってほしい!ということで、今回はLine.Messagingライブラリを使ったAzure FunctionsのLINE BOTアプリケーション開発について少し解説してみたいと思います。

目次

Visual Studio とAzuzre Functionsで作るLINE BOTアプリケーション入門

はじめに

ここで紹介するサンプルコードは、拙作Line.Messagingクラスライブラリを使用することを前提としています。

www.nuget.org

この記事の内容を実際に試したい方は、MarketPlaceに記載のクイックスタートガイド参考に開発環境を作っておきましょう。
(導入時の問題、質問等ありましたら、このブログのコメント欄でも、MarketplaceのQ&AでもTwitterでも何でも良いのでご連絡ください)

Webhook イベントを処理する

友だち追加やメッセージ送信などのイベントは、指定したAzure FunctionsのURLにHTTP POSTリクエストとして送信されます。 そのリクエストによって、HttpTriggerFunctionクラスのRunメソッドが実行されます。

以下は、Line.Messagingを使用したHttpTriggerFunctionの実装例です。

public static class HttpTriggerFunction
{
    //LINE Messaging API クライアントの初期化
    static LineMessagingClient lineMessagingClient;
    static HttpTriggerFunction()
    {
        lineMessagingClient = new LineMessagingClient(System.Configuration.ConfigurationManager.AppSettings["ChannelAccessToken"]);
        var sp = ServicePointManager.FindServicePoint(new Uri("https://api.line.me"));
        sp.ConnectionLeaseTimeout = 60 * 1000;
    }

    [FunctionName("LineMessagingApiSample")]
    public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
    {
        //WebhookのリクエストBodyからイベントオブジェクトを取得
        IEnumerable<WebhookEvent> events;
        try
        {
            var channelSecret = System.Configuration.ConfigurationManager.AppSettings["ChannelSecret"];
            events = await req.GetWebhookEventsAsync(channelSecret);
        }
        catch (InvalidSignatureException e)
        {
            return req.CreateResponse(HttpStatusCode.Forbidden, new { Message = e.Message });
        }

        //イベントを処理する
        try
        {
            var app = new EchoBotApp(lineMessagingClient,log);
            await app.RunAsync(events);
        }
        catch (Exception e)
        {
            log.Error(e.ToString());
        }
        return req.CreateResponse(HttpStatusCode.OK);
    }
}

LINE Messaging APIクライアントを初期化する

メッセージのリプライ、プッシュ通知などのLINEサーバーへのリクエストにはLine.Messaging.LineMessagingClientクラスを使用します。 このクラスは内部でSystem.Net.Http.HttpClientクラスを使用しています。
これをHttpTriggerFunctionクラスのStaticフィールドに1インスタンスだけ作成し、使いまわすようにします。

インスタンスを使いまわす理由は、以下を参照ください。

リクエストBodyからWebhook Event Objectを取得する

Webhookイベントの内容は、リクエストBodyにJSONで格納されています。
Line.Messagingライブラリでは、Run関数の引数HttpRequestMessage reqからEvent Objectを取得する拡張メソッドを用意しています。

static Task<IEnumerable<WebhookEvent>> GetWebhookEventsAsync(this HttpRequestMessage req,strin channelSecret);

このメソッドは以下の処理を行います。

  • リクエストの構文検証(Signature Validation)を行い、NGの場合は例外を投げる
  • リクエストBodyのJSONをパースしてWebhook Event Object(のコレクション)を返す
[FunctionName("LineMessagingApiSample")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
    IEnumerable<WebhookEvent> events;
    try
    {
        var channelSecret = System.Configuration.ConfigurationManager.AppSettings["ChannelSecret"];
        //Webhook Event Objectの取得
        events = await req.GetWebhookEventsAsync(channelSecret);
    }
    catch (InvalidSignatureException e)
    {
        //構文検証エラー
        return req.CreateResponse(HttpStatusCode.Forbidden, new { Message = e.Message });
    }

    //...
}

Webhook Event Object

Webhook Event Object はイベントのタイプ別に7種類用意されています。
Line.Messagingライブラリでは、WebhookEvent抽象クラスのサブクラスとして実装されています。

public abstract class WebhookEvent
{
    public WebhookEventType Type { get; }
    public WebhookEventSource Source { get; }
    public long Timestamp { get; }
}
  • MessageEvent
    ユーザーからメッセージが送られた際に発行されるイベント。
    メッセージには、送られてくるデータの種類によって異なる以下のタイプがあります。

    • text (文字列メッセージ)
    • image(画像データ)
    • audio(オーディオデータ)
    • video(ビデオデータ)
    • file(ファイル)
    • location(位置情報)
    • sticker(スタンプ
  • FollowEvent
    ユーザーがBOTアカウントをフォローした際に発行されるイベント

  • UnfollowEvent
    ユーザーにBTOアカウントをブロックされた際に発行されるイベント
  • JoinEvent
    BOTアカウントがグループやトークルームに参加した際に発行されるイベント
  • LeaveEvent
    BOTアカウントがグループやトークルームから退室させられた際に発行されるイベント
  • PostbackEvent
    テンプレートメッセージに設定したPostbackアクションが実行された際に発行されるイベント
  • BeaconEvent
    LINE Beaconデバイスの受信圏内に入った(出た)際に発行されるイベント

※各イベントの詳細はLINE公式のAPIリファレンスを参照ください。

WebhookEventSource

WebhookEventクラスは、イベントの送信元を表すSourceプロパティを持ちます。

Line.MessagingライブラリではWebhookEventSourceクラスとして実装しています。

public class WebhookEventSource
{
    public EventSourceType Type { get; }
    public string Id { get; }        
    public string UserId { get; }
}

送信元のタイプは以下の3種類です。

  • User (ユーザーと1対1のトークで発生するイベントに付与されます)
  • Group (トークグループで発生するイベントに付与されます)
  • Room (トークルーム(複数ユーザー間でのトーク)で発生するイベントに付与されます)

また、Idプロパティには、各送信元を示すIDが格納されます(User ID、Group ID、Room ID)。
UserIdには、各送信元から発信したユーザーのIDが入ります。

イベントを処理する

あとは、リクエストBodyから取得したWebhookEventオブジェクトの内容を見てそれに応じた処理を記述すればOKです。
これをHttpTriggerFunctionクラスに直接記述しても良いのですが、Line.Messagingライブラリではイベントの種類に応じて処理を振り分けて実行するためのクラスが用意されているので、これを使います。

WebhookApplicationクラスを使用する

WebhookApplicationクラスは、RunAsyncというメソッドでWebhookEventのコレクションを受け取り、イベントの種類に応じて各イベントの処理(On~ 仮想メソッド)を呼ぶだけのクラスです。

public abstract class WebhookApplication
{
    protected virtual Task OnMessageAsync(MessageEvent ev) => Task.CompletedTask;
    protected virtual Task OnJoinAsync(JoinEvent ev) => Task.CompletedTask;
    protected virtual Task OnLeaveAsync(LeaveEvent ev) => Task.CompletedTask;
    protected virtual Task OnFollowAsync(FollowEvent ev) => Task.CompletedTask;
    protected virtual Task OnUnfollowAsync(UnfollowEvent ev) => Task.CompletedTask;
    protected virtual Task OnBeaconAsync(BeaconEvent ev) => Task.CompletedTask;
    protected virtual Task OnPostbackAsync(PostbackEvent ev) => Task.CompletedTask;

    public async Task RunAsync(IEnumerable<WebhookEvent> events)
    {
        foreach (var ev in events)
        {
            switch (ev.Type)
            {
                case WebhookEventType.Message:
                    await OnMessageAsync((MessageEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Join:
                    await OnJoinAsync((JoinEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Leave:
                    await OnLeaveAsync((LeaveEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Follow:
                    await OnFollowAsync((FollowEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Unfollow:
                    await OnUnfollowAsync((UnfollowEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Postback:
                    await OnPostbackAsync((PostbackEvent)ev).ConfigureAwait(false);
                    break;
                case WebhookEventType.Beacon:
                    await OnBeaconAsync((BeaconEvent)ev).ConfigureAwait(false);
                    break;
            }
        }
    }
}

このクラスを継承したクラスを作成し、必要な仮想メソッドをオーバーライドして処理を記述します。
以下に、ユーザーのメッセージをオウム返しするだけの処理の実装例を示します。

//オウム返しBOTアプリケーション
public class EchoBotApp:WebhookApplication
{
    private LineMessagingClient MessagingClient {get;}
    private TraceWriter Log{get;}

    //コンストラクタでLineMessagingClientのインスタンスを渡す
    public EchoBotApp(LineMessagingClient messagingClient, TraceWriter log)
    {
        MessagingClient = messagingClient;
        Log = log;
    } 

    //メッセージEventを処理する
    protected override async Task OnMessageAsync(MessageEvent ev)
    {
        //Textメッセージにだけ返信する
        if(ev.Type != EventMessageType.Text){return;}

        return MessagingClient.ReplyMessageAsync(ev.ReplyToken, ((TextEventMessage)ev.Message).Text);
    }
}

あとは、HttpTriggerFunctionのRunメソッドで以下の様に記述するだけです。

//WebhookApplicationを継承したクラスでイベントを処理する
var app = new EchoBotApp(lineMessagingClient,log);
await app.RunAsync(events);

まとめ

基本的な使い方は以上です。
次回からは、もう少し具体的な実装例を解説してみたいと思います。

LINE BOT 開発に使えるLINE Messaging API の.Net Standard Libraryと、Visual Studio 2017用プロジェクトテンプレートを作りました

LINEでビンゴゲームができるBOTを作ったのですが、 このBOTで実装したMessaging API のコードをクラスライブラリに切り出してみました。

pierre3.hatenablog.com

Line.Messaging V0.20-alpha

NuGet Galleryで公開しています。

www.nuget.org

  • ターゲットバージョンは.Net Standard 1.3 としました。
  • APIは一通り実装されていますが、テストが不十分のためAlphaリリース版としています。

LINE BOT Function プロジェクトテンプレート

Azure Functionsのプロジェクトに、「Line.MessagingのNuGet参照」と「ユーザーのメッセージを受け取り、オウム返しをするコード」を追加したテンプレートです。

Visual Studio Marketplaceからダウンロードできます。
テンプレートの使い方は、以下のリンク先に記載されています。

marketplace.visualstudio.com

これからLINE BOT を作ってみたいという方は、ぜひこのテンプレートをダウンロートして試してみてください。

GitHubリポジトリ

ソースコード等はこちらから github.com