Slackで特定のキーワードに反応して自動的にReactionをつけるBotを作った

例えばこんな感じでReactionがつきます。チャット内がReactionで賑やかになります。
![demo.gif](https://qiita-image-store.s3.amazonaws.com/0/44992/87b03213-15f9-2350-cb1c-6dff9a6720f5.gif)

GitHubで公開中です。
[https://github.com/kuluna/OverReaction](https://github.com/kuluna/OverReaction)
使い方や導入方法はGitHubのほうにまとめました。
みんなに使ってもらえるよう英語ドキュメントも泣きながら整備します :cry: 

ひとつの文にワードが複数あるとリアクションがバババッと連続でつきます。楽しい！

# 使用ツール・フレームワーク
- Visual Studio 2015 __コレがないと始まらない(個人差はあります)__
- ASP.NET Core
- EntityFrameworkCore
- AngularJS
- AngularMaterial

小さいアプリだしどうせならと思い.NET Coreに手を出してみました。

ASP.NET CoreではWebAPI機能をメインに使っています。
AngularJSでAPIからReaction設定を読み出し、画面に表示しています。
見た目はカッコよくしたかったのでマテリアルデザインを採用しました。AngularJSならAngularMaterialというライブラリがあるので割と楽に適用できます。

ちなみにフロントはこんな感じで動きます。
![addmemo.gif](https://qiita-image-store.s3.amazonaws.com/0/44992/73861c01-9511-4bf1-12fc-4c35b2ba3876.gif)


__ここでは.NET Coreをメインに実装方法などを解説します。__

# ASP.NET Core
## ASP.NET Coreのソースを紐解く順番
従来のASP.NETの構成と異なるのでどこからソースを読み始めればいいかわからない人も多いと思います。
私は以下の順番で見ています。

- project.json または Startup.cs
- Controllersフォルダ内のxxxController.cs
- Modelsフォルダ内
- Viewsフォルダ内の.htmlcs または wwwrootフォルダ内の.html

## project.json
プロジェクトに関する情報が書かれています。この中のdependenciesを見るとどういったライブラリが使われているか見ることができます。
Visual StudioのASP.NET Core WebAPIのテンプレートで作成するといくつかライブラリが登録されていますが、それに加えてこの3つをさらに登録しました。

```json:project.json
"Microsoft.AspNetCore.StaticFiles": "1.0.0",
"Microsoft.EntityFrameworkCore.Sqlite": "1.0.1",
"Swashbuckle": "6.0.0-beta902"
```
1つめはhtmlや画像を扱えるようにするためのライブラリです。WebAPIだと不要なのでデフォルトでは入ってないんですね。
2つはみんな大好きEntityFrameworkの.NET Core版です。今回はSQLiteを使いました。
3つめは[Swagger](http://swagger.io/)というAPIドキュメントを作成するためのライブラリです。

## Startup.cs
ASP.NET Coreからは後から追加したライブラリを使用する宣言を`Startup.cs`に書く必要があります。
`Startup.cs`には`ConfigureServices`メソッドと`Configure`メソッドがあります。ライブラリを追加していくとAddXXXやUseXXXメソッドが追加されていくのでここで利用を宣言します。

例えばSwaggerを追加するにはAddSwaggerGenメソッドを呼び出します。一部のライブラリはデリゲートで設定を書くこともできます。

```csharp:Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddOptions();
    services.AddMvc();
    
    services.AddSwaggerGen(setting =>
    {
        var location = Assembly.GetEntryAssembly().Location;
        var xmlPath = location.Replace("dll", "xml");
        setting.IncludeXmlComments(xmlPath);
    });

    services.Configure<AppSetting>(Configuration.GetSection(nameof(AppSetting)));
}
```

ASP.NET Coreではサービスを追加しただけでは(おそらく)使われません。UseXXXを呼び出すことで実際に使われるようになります。
気を付けなければならないのが__呼び出す順番がある__ものが存在します。例えば`UseDefaultFiles()`はwwwrootにあるdefault.htmlやindex.htmlをURLのルートで開けるようにするためのメソッドですが、`UseStaticFiles()`より先に呼び出さないとうまく動作しません。

```csharp:Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseDefaultFiles(); // 必ずUseStaticFiles()より上！
    app.UseStaticFiles();

    app.UseSwagger();
    app.UseSwaggerUi();

    app.UseMvc(); // 常に一番下！
}
```

## Controllers
ASP.NET Coreではサーバーサイドの処理はControllersフォルダにあります。
クラス名にRouteアノテーションがついていますが、こうすることによって`api/コントローラ名`でアクセスできます。ReactionsControllerの場合は`api/reactions`になります。

```csharp:ReactionsController.cs
[Route("api/[controller]")]
public class ReactionsController : Controller
{
    public ReactionsController()
    {
        using (var db = new ReactionContext())
        {
            db.Initialize();
        }
    }

    [HttpGet]
    public List<Reaction> GetAll()
    {
        using (var db = new ReactionContext())
        {
            return db.Reactions.ToList();
        }
    }
    .
    .
    .
```

あとはASP.NET WebAPI2とほぼ同じです。ここを見ればアプリがだいたい何をやっているかがわかります。

## EntityFrameworkCore
噂ではEntityFramework6より速いとか。書き方はほぼ同じですね。
自動マイグレーションがまだ機能不足なところがあります。Core版ではテーブルの自動生成はありますが、自動マイグレはまだありません。なのでControllerから毎回実行されるように`Initialize()`を独自で追加しています。

```csharp:Reaction.cs
public class Reaction
{
    /// <summary>
    /// ID(登録順連番)
    /// </summary>
    [Key]
    public int Id { get; set; }
    /// <summary>
    /// 検索ワード
    /// </summary>
    [Required]
    public string Word { get; set; }
    /// <summary>
    /// 絵文字
    /// </summary>
    [Required]
    public string Emoji { get; set; }
}

/// <summary>
/// データベースコンテキスト
/// </summary>
public class ReactionContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connectionString = new SqliteConnectionStringBuilder { DataSource = "overreaction.sqlite" }.ToString();
        optionsBuilder.UseSqlite(connectionString);
    }

    /// <summary>
    /// Slack Reaction データベース
    /// </summary>
    public DbSet<Reaction> Reactions { get; set; }

    /// <summary>
    /// データベースにテーブルがなければ作成し、カラムに変更があればマイグレーションを実行します。変更がない場合は何もしません。
    /// </summary>
    public void Initialize()
    {
        // Database initialize
        Database.Migrate();
    }
}
```

# Azure WebAppにデプロイ
appsettings.jsonを設定すればこのソースをそのままAzure WebAppに投げると動きます。Azureの中の妖精さん(Kudu)がASP.NET Core用に自動でデプロイしてくれるようです。

## そしてAzureADとの連携も
AzureADとの絡みについては [ASP.NET Advent Calendar 2016](http://qiita.com/advent-calendar/2016/asp-net)に書こうと思います。

# 所感
- ASP.NETの雰囲気を残しながらもこれでクロスプラットフォームで動くとかすごい
- UseXXXの並びは割と罠だと思う
- Azureはいいぞ