C#からSlackAPIを利用して、指定期間内に投稿されたメッセージを取得する

## はじめに 
本記事はC#からSlackAPIを利用して、指定期間内に投稿されたメッセージを取得する方法をまとめることを目的としています。
なお、以前に以下のようなSlackAPIに関する記事を投稿していますので、こちらの記事を先に読んでいただくことをお勧めします。

- SlackAPIを使うための準備
https://qiita.com/tat_tt/items/e10bd5d9aee897f0b8b6

- メッセージを画像付きで取得する方法
https://qiita.com/tat_tt/items/14b688ea48afbcd52505

## 利用するAPIとAPI引数
メッセージを取得するためには[こちら](https://api.slack.com/methods/conversations.history)のAPIを使います。
投稿時間を指定するためには以下のoldest, latest引数を設定してAPIを呼び出す必要があります。

![image-20200709090717712.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/452292/06a2ed01-9de7-9896-802c-027e1f161a35.png)


oldestは取得するメッセージの投稿時間の__最小値__、latestは取得するメッセージの投稿時間の__最大値__を表します。つまり、oldestとlatestを両方設定すると、oldest~latest間のメッセージを取得することができます。

## oldest, latestの指定方法
[こちら](https://api.slack.com/reference/surfaces/formatting#date-formatting)の公式文書に書かれているようにSlackでは時間をUNIX時間形式で扱っています。UNIX時間はUNIX系のコンピューターシステムで標準的に使われている時刻表現のことです。詳細は[こちら](https://www.weblio.jp/content/UNIX%E6%99%82%E9%96%93)を参照ください。
そのため、例えば2020年7月1日に投稿されたメッセージを取得する場合は、oldest, latestにそれぞれ以下のような値を入れて、APIを呼び出します。

- oldest
2020年7月1日 0:00を表現するUNIX時間

- latest
2020年7月2日 0:00を表現するUNIX時間

## C#でUNIX時間を作り出す方法
C#上でUNIX時間を作り出すには、DateTimeOffset構造体から変換するのが簡単だと思います。
手順としては、以下になります。

1. DateTime構造体でUNIX時間に変換したい日時情報を作成
2. 1でつくったDateTime型の値を使って、DateTimeOffset構造体を作成(リファレンスは[こちら](https://docs.microsoft.com/ja-jp/dotnet/api/system.datetimeoffset.-ctor?view=netcore#System_DateTimeOffset__ctor_System_DateTime_))
3. 2のDateTimeOffset型の値を[こちら](https://docs.microsoft.com/ja-jp/dotnet/api/system.datetimeoffset.tounixtimeseconds?view=netcore-3.1)のメソッドでUNIX時間に変換

## 時間を指定してメッセージを取得するサービス
これまでの内容を活かして、投稿時間を指定してメッセージを取得するサービスを作成します。[こちら](https://qiita.com/tat_tt/items/14b688ea48afbcd52505)の記事で作成したメッセージを取得するGetMessagesメソッドを拡張して、実現します。

```csharp
/// <summary>
/// Slackメッセージの取得に必要な機能を提供するサービス
/// </summary>
public class SlackMessageGetService
{
    #region 定数

    /// <summary>
    /// アクセストークン用のクエリパラメータ名
    /// </summary>
    private const string c_AccessTokenQueryParameterName = "token";

    /// <summary>
    /// チャンネルID用のクエリパラメータ名
    /// </summary>
    private const string c_ChannelIdQueryParameterName = "channel";

    /// <summary>
    /// 取得メッセージ数用のクエリパラメータ名
    /// </summary>
    private const string c_MessageCountQueryParameterName = "limit";

    /// <summary>
    /// 投稿時間の最小値用のクエリパラメータ名
    /// </summary>
    private const string c_OldestQueryParameterName = "oldest";

    /// <summary>
    /// 投稿時間の最大値用のクエリパラメータ名
    /// </summary>
    private const string c_LatestQueryParameterName = "latest";

    ・・・

    #endregion

    #region フィールド

    /// <summary>
    /// SlackにPOST,GETするために使用するHttpClient
    ///
    /// ユーザーの利用方法を想定したとき、接続先のホストは[チーム名].slack.comしかありえないため、
    /// HttpClientは単一インスタンスのみとする
    /// </summary>
    // ReSharper disable once InconsistentNaming
    private static readonly HttpClient m_HttpClient = new HttpClient();

    #endregion

    #region 公開サービス

    /// <summary>
    /// メッセージを取得する
    /// </summary>
    /// <param name="accessToken">アクセストークン</param>
    /// <param name="channelId">チャンネルID</param>
    /// <param name="messageCount">取得するメッセージ数</param>
    /// <param name="oldest">投稿時間の最小値</param>
    /// <param name="latest">投稿時間の最大値</param>
    /// <returns>取得メッセージ一覧</returns>
    public async Task<IEnumerable<Message>> GetMessages(string accessToken, string channelId, int messageCount, DateTime oldest, DateTime latest)
    {
        var messages = new List<Message>();

        // latestとoldestをUNIX時間に変換
        var oldestDateTimeOffset = new DateTimeOffset(oldest);
        var oldestUnixTime = oldestDateTimeOffset.ToUnixTimeSeconds();
        var latestDateTimeOffset = new DateTimeOffset(latest);
        var latestUnixTime = latestDateTimeOffset.ToUnixTimeSeconds();

        // クエリパラメータを作成するためにディクショナリを作成
        // ディクショナリのKeyがクエリパラメータ名、ディクショナリのValueがクエリパラメータの値
        var parameters = new Dictionary<string, string>()
        {
            { c_AccessTokenQueryParameterName, accessToken},
            { c_ChannelIdQueryParameterName, channelId},
            { c_MessageCountQueryParameterName, messageCount.ToString()},
            { c_OldestQueryParameterName, oldestUnixTime.ToString()},
            { c_LatestQueryParameterName, latestUnixTime.ToString()}
        };

        try
        {
            // クエリパラメータを作成し、文字列で読み出す
            var parametersString = await new FormUrlEncodedContent(parameters).ReadAsStringAsync();
            // 読みだしたクエリパラメータを使ってリクエストURLを作成する。
            var requestBaseUrl = "https://slack.com/api/conversations.history";
            var requestUrl = $"{requestBaseUrl}?{parametersString}";
            var response = await m_HttpClient.GetAsync(requestUrl).ConfigureAwait(false);
            // レスポンスのコンテンツをstringで読み出す
            var responseBodyString = await response.Content.ReadAsStringAsync();
            // 読みだしたJsonを、オブジェクトにデシリアライズする
            var responceObject = JsonConvert.DeserializeObject<GetSlackMessagesResponce>(responseBodyString);

            // 戻り値用のメッセージ一覧を作成
            foreach (var messageResponce in responceObject.messages)
            {
                // 本文を設定
                var message = new Message()
                {
                    Text = messageResponce.text
                };

                // 添付ファイルを設定
                foreach (var file in messageResponce.files)
                {
                    var addFile = new File()
                    {
                        FileURL = file.url_private,
                        Id = file.id,
                    };
                    message.Files.Add(addFile);
                }

                messages.Add(message);
            }

            return messages;
        }
        catch
        {
            return messages;
        }
    }

    ・・・

    #endregion
}
```

メソッドに新たに追加したロジックである、引数oldestとlatestをUNIX時間に変換する箇所を細かく説明します。
引数oldestとlatestはDateTime型なので、その値を使ってDateTimeOffset構造体の値を作ります。コンストラクタにDataTime型の値を渡すだけで作成できます。その後、ToUnixTimeSecondsメソッドでUNIX時間に変換します。
その後は、元々API引数として渡していたchannelIdなどと同様にディクショナリに情報を詰めます。ディクショナリのKey値がAPI引数名(oldestやlatest)、Value値が実際の値です。ディクショナリをどのようにAPI引数とするかの細かい説明は[以前の記事](https://qiita.com/tat_tt/items/14b688ea48afbcd52505)を参照ください。

これらのロジック追加によって、oldest,latestを指定してAPIを呼び出すことが可能になりました。

## まとめ
本記事では、C#からSlackAPIを利用して指定期間内に投稿されたメッセージを取得する方法をまとめました。
最も伝えたかったのは、以下2点です。

- SlackAPIにおいて時間はUNIX時間で表現される
- C#でUNIX時間を作るにはDateTimeOffset構造体を使うのが簡単