Qiitaに投稿した記事のリンクを楽にWordPressに送りたい #1

#シリーズ
[Qiitaに投稿した記事のリンクを楽にWordPressに送りたい #2](https://qiita.com/GodPhwng/items/8de81c46a3a01f53d4b5)
[Qiitaに投稿した記事のリンクを楽にWordPressに送りたい #3](https://qiita.com/GodPhwng/items/c41facd5fef42fec7f47)
[Qiitaに投稿した記事のリンクを楽にWordPressに送りたい #4](https://qiita.com/GodPhwng/items/875f5fbacec35f038d4a)

#動機
WordPressのHPにQiitaに投稿した記事のリンクを公開しているのですが、その方法がQiitaリンクをWordPressに手動で張り付けるというものであり、これが結構手間で、自動化できないかと思い、自作することにしました。RPAで実現すればいいのかな・・・？

欲を言えば、Qiitaに投稿した瞬間に自動で連携してくれればベストですが、記事を投稿した時のイベントをフック(検知)する必要があります。
→Qiitaには[Webhook](https://qiita.com/api/webhook/docs)というものがありました。
これは、記事投稿時等に指定されたURLへ作成した記事の情報をPOST送信する機能です。


その他には定期的にQiitaの記事を監視して同期するサービスでもいいですね・・・

**やりたいこと**

 - 記事を投稿したタイミングでWordPressに反映してほしい

**実現するには**

- WordPressに記事をインポートするサービス(Webサービス)の構築・・・①
- ①を配置するサーバーの用意　→　常時動いていてほしいので、ローカルではなく、クラウドorレンタルサーバー等にする
- 記事を投稿した時に[Webhook](https://qiita.com/api/webhook/docs)を用いて、WordPressに記事をインポートするサービスを呼び出す

#段階的に進めて実現を目指す
~~いきなりベストの構築をやろうとするとなんかめんどくさいので、~~段階的に作っていって、動作や使用間を確認しながら、完成にもっていくようにしようと思います。

以下手順に沿って進めていきたい。

**プロセス**

1. Qiitaから記事を読み取って、WordPressに記事をインポートするアプリケーションを作る(c# コンソールアプリ)
2. 「1.」をWebサービス化する(ローカルにWebサービスとして立てる)
3. 「2.」のサービスをブラウザから１クリックで呼び出す。→ブックマークレット
4. 「2.」のサービスを クラウド or レンタルサーバーに乗せる
5. 「2.」のサービスを[Webhook](https://qiita.com/api/webhook/docs)から受けた情報を用いてWordPressに送信できるように改修する

まずは**1. Qiitaから記事を読み取って、WordPressに記事をインポートするアプリケーションを作る(c# コンソールアプリ)**からやっていきたいと思います。

#環境
**IDE**：VisualStudio2019
**アプリケーション**：コンソールアプリ
**フレームワーク**：.NET Core 3.1

#転送の仕様

- WordPressの記事一覧内にQiita側の記事が存在しない場合、WordPressに新規追加する
- WordPressの記事一覧内にQiita側の記事が存在する場合、WordPressの記事を更新する
- 存在可否の判定はWordPress記事内のQiitaリンクURLと、Qiitaの記事のURLを比較して行う。

#設計
Qiita記事一覧取得
↓
WordPress記事一覧取得
↓
二つの一覧を比較して、記事追加／更新

#Qiita APIを用いて、記事を取得
[Qiita API v2 の概要(非公式)](https://qiita.com/tag1216/items/b0b90e30c7e581aa2b00)を参考にさせていただいて、指定ユーザの投稿記事一覧取得APIの[/api/v2/users/:user_id/items](http://qiita.com/api/v2/docs#get-apiv2usersuser_iditems)を使用したいと思います。

GETのリクエストにはHttpClientを使用しようと思います。

**基底クラス**
HttpClientを使いまわすため、基底クラスを用意します。

```C#
using Newtonsoft.Json.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace TestProject.QiitaToWP
{
    /// <summary>
    /// サービス基底クラス
    /// </summary>
    public abstract class ServiceBase
    {
        /// <summary>
        /// HttpClient
        /// </summary>
        protected static readonly HttpClient HpClient;

        /// <summary>
        /// staticコンストラクタ
        /// </summary>
        static ServiceBase()
        {
            HpClient = new HttpClient();
        }

        /// <summary>
        /// 記事一覧取得
        /// </summary>
        /// <param name="param">パラメータ</param>
        /// <returns>記事一覧</returns>
        public abstract Task<JArray> GetArticleList(params string[] param);
    }
}
```

**Qiitaの記事一覧取得**

```C#
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;

namespace TestProject.QiitaToWP
{
    /// <summary>
    /// QiitaAPI呼び出しクラス
    /// </summary>
    public class QiitaService : ServiceBase
    {
        private const string URL_FIRST = "https://qiita.com";
        private const string GET_ARTICLELIST = "/api/v2/users/{0}/items";

        public override async Task<JArray> GetArticleList(params string[] param)
        {
            var body = await HpClient.GetStringAsync(URL_FIRST + string.Format(GET_ARTICLELIST, param[0]));
            return JArray.Parse(body);
        }
    }
}
```

#WordPress APIを用いて、記事一覧取得
**WordPressの記事取得**
公式のリファレンスを参考にしました。
[WordPress API 投稿](https://ja.wp-api.org/reference/posts/)の「Post一覧」の部分です。
※取得処理はGetArticleListだけ参照してください。

```C#
using Newtonsoft.Json.Linq;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace TestProject.QiitaToWP
{
    /// <summary>
    /// WordPress API呼び出しクラス
    /// </summary>
    public class WPService : ServiceBase
    {
        private const string URL_FIRST = "http://kurosu.s1009.xrea.com";
        private const string GET_ARTICLELIST = "/wp-json/wp/v2/posts";
        private const string UPDATE_ARTICLE = "/wp-json/wp/v2/posts/{0}";

        public override async Task<JArray> GetArticleList(params string[] param)
        {
            var body = await HpClient.GetStringAsync(URL_FIRST + GET_ARTICLELIST);
            return JArray.Parse(body);
        }

        public async Task<bool> UpdateWPArticle(string id, string json)
        {
            // TODO「Application Passwords」プラグインというプラグインを有効化にしていないせいかまだ動作しない
            var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("XXXX:BBBBBBB"));
            var content = new StringContent(json, Encoding.UTF8, "application/json");
            content.Headers.TryAddWithoutValidation("Authorization", $"Basic {credentials}");
            var result = await HpClient.PostAsync(URL_FIRST + string.Format(UPDATE_ARTICLE, id), content);

            if (result.StatusCode == HttpStatusCode.OK)
            {
                return true;
            }
            else
            {
                return false;
            }            
        }
    }
}
```

#取得した２つの一覧を比較して、追加／更新
**上記2つのサービスクラスを操作するクラス**

```C#
using System.Linq;
using System.Threading.Tasks;

namespace TestProject.QiitaToWP
{
    public class Qiita2WP
    {
        public async Task Qiita2WPArticle()
        {
            // Qiita記事取得
            var qiitaService = new QiitaService();
            var qiitaList = await qiitaService.GetArticleList("GodPhwng");

            // WP記事取得
            var wpService = new WPService();
            var wpList = await wpService.GetArticleList();

            foreach (var qiita in qiitaList)
            {
                var url = qiita["url"].ToString();
                var title = qiita["title"].ToString();

                // Qiitaの記事URLが含まれる物を取得
                var matchArticle = wpList.FirstOrDefault(w => w["content"]["rendered"].ToString().Contains(url));

                // 記事更新
                if (matchArticle != null)
                {
                    // タイトル
                    matchArticle["title"] = title;

                    // リンク部分
                    matchArticle["content"]["rendered"] = $"\n<p>{title}<a href=\"{url}\">{url}</a></p>\n";

                    // 更新
                    var result = await wpService.UpdateWPArticle(matchArticle["id"].ToString(), matchArticle.ToString());
                }
                // 記事新規追加
                else
                {
                    // TODO
                }
            }
        }
    }
}
```

**メインクラス**

```C#
using System;
using System.Threading.Tasks;

namespace TestProject.QiitaToWP
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            try
            {
                var q2wp = new Qiita2WP();

                // Qiitaの記事をWordPressに反映
                await q2wp.Qiita2WPArticle();                
            }
            catch (Exception err)
            {
                Console.WriteLine(err.Message);
            }
            finally
            {
                Console.Read();
            }
        }
    }
}
```

#取得は正常動作、更新はダメ
Qiita、WordPressの両方共取得処理は正常動作しました。
公開されているデータなので認証等も必要ありません。

追加、更新処理については認証が必要であり、**クッキー認証／OAuth認証／アプリケーションパスワード認証**を利用する必要があるようです。
今回はアプリケーションパスワード認証を用います。
まずWordPress側で設定をする必要があるようです。[WordPress 認証](https://ja.wp-api.org/guide/authentication/)
現状私のほうでその設定をする権限がないため(ある方のWordPressの環境を使わせてもらっており、その方が管理者のため・・・)
今回はここまでとします。

[次回](https://qiita.com/GodPhwng/items/8de81c46a3a01f53d4b5)は、設定を有効後、記事追加と更新をやっていきます。