ようこそ。睡眠不足なプログラマのチラ裏です。

はてなハイクAPIを使ってみよう(LINQ to XML)

C#から「はてなハイクAPI (http://h.hatena.ne.jp/api)」を利用するサンプルを作りました。
他の言語のサンプルは割りとあるようですが、C#ではなかったようなので。


画像を添付した投稿については面倒くさいので直接はサポートしていませんが、
呼び出し側で頑張れば、画像を投稿することも可能なようになっています。
また、画像添付投稿以外のAPI操作については、ほぼサポートしています。
(取得データの扱いについては、LINQ to XMLでの利用を前提としています^^)

HatenaHaikuクラス

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml.Linq;

namespace ClassLibrary1
{
    /// <summary>
    /// はてなハイク
    /// </summary>
    public class HatenaHaiku
    {
        #region コンストラクタ

        public HatenaHaiku(string id, string password)
        {
            Id = id;
            Password = password;
        }

        public HatenaHaiku(string id, string pass, string clientName)
            : this(id, pass)
        {
            ClientName = clientName;
        }

        #endregion

        #region プロパティ

        private string Id { get; set; }
        private string Password { get; set; }
        private string ClientName { get; set; }

        /// <summary>
        /// 接続できるか否かを取得します。
        /// </summary>
        /// <returns></returns>
        public bool CanConnect
        {
            get
            {
                WebRequest req = CreateWebRequest("http://h.hatena.ne.jp/api/statuses/update.xml");
                req.Method = "GET";
                try
                {
                    using (var wr = (HttpWebResponse)req.GetResponse())
                        return wr.StatusCode == HttpStatusCode.OK;
                }
                catch
                {
                    return false;
                }
            }
        }

        #endregion

        #region メソッド

        /// <summary>
        /// 対象UriからデータをXDocument形式で取得します。
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="parameters">パラメータのキーと値</param>
        /// <returns>XDocument</returns>
        private XDocument Get(string uri, params KeyValuePair<string, object>[] parameters)
        {
            var req = CreateWebRequest(uri);
            req.Method = "GET";

            SetRequestParameters(req, parameters);

            var ar = req.BeginGetResponse(r => { }, req);
            ar.AsyncWaitHandle.WaitOne(new TimeSpan(0, 0, 20), false);
            var rps = req.EndGetResponse(ar);
            using (var sr = new StreamReader(rps.GetResponseStream(), Encoding.UTF8))
                return XDocument.Parse(sr.ReadToEnd());
        }

        /// <summary>
        /// 対象UriにデータをPOSTします。
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="parameters">パラメータのキーと値</param>
        /// <returns></returns>
        public bool Post(string uri, params KeyValuePair<string, object>[] parameters)
        {
            try
            {
                WebRequest req = CreateWebRequest(uri);
                req.Method = "POST";

                SetRequestParameters(req, parameters);

                var result = (HttpWebResponse)req.GetResponse();
                return result.StatusCode == HttpStatusCode.OK;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// WebRequestに対してPOSTする値を設定します。
        /// </summary>
        /// <param name="req"></param>
        /// <param name="parameters"></param>
        private void SetRequestParameters(WebRequest req, IEnumerable<KeyValuePair<string, object>> parameters)
        {
            byte[] postValue = null;
            if (parameters.Count() != 0)
            {
                var s = "";
                foreach (var item in  parameters.Select((x,i) => new {v = x, Index = i}))
                {
                    if (item.Index != 0) s += "&";
                    s += item.v.Key + "=" + Uri.EscapeUriString(item.v.Value.ToString());
                }

                postValue = Encoding.ASCII.GetBytes(s);
                req.ContentLength = postValue.Length;

                using (var strm = req.GetRequestStream())
                {
                    strm.Write(postValue, 0, postValue.Length);
                }
            }
        }

        #endregion

        #region 認証

        /// <summary>
        /// Basic認証用の文字列を生成します。
        /// </summary>
        /// <returns></returns>
        private string CreateAuthString()
        {
            return "Authorization: Basic "
                 + Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}",Id,Password)));
        }

        /// <summary>
        /// uriからWebリクエストを生成します。
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        private WebRequest CreateWebRequest(string uri)
        {
            var req = HttpWebRequest.Create(uri);
            req.Proxy = new WebProxy();
            ServicePointManager.FindServicePoint(uri, req.Proxy).Expect100Continue = false;
            req.ContentType = "application/x-www-form-urlencoded";
            //req.ContentType = "multipart/form-data";
            req.Headers.Add(CreateAuthString());
            return req;
        }

        #endregion

        #region ステータス・タイムライン

        #region GET
        /// <summary>
        /// http://h.hatena.ne.jp/ に対応する、はてなハイク全体の最新のエントリーを返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetPublicTimeline()
        {
            return Get("http://h.hatena.ne.jp/api/statuses/public_timeline.xml");
        }

        /// <summary>
        /// http://h.hatena.ne.jp/user/following に対応する、認証したユーザー、
        /// または指定したユーザーがフォローしているユーザー・キーワードのエントリーを返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetFriendsTimeline(params KeyValuePair<string, object>[] parameters)
        {
            return Get("http://h.hatena.ne.jp/api/statuses/friends_timeline.xml", parameters);
        }

        /// <summary>
        /// http://h.hatena.ne.jp/user/ に対応する、認証したユーザー、または指定したユーザーの最新のエントリーを返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetUserTimeline(params KeyValuePair<string, object>[] parameters)
        {
            return Get("http://h.hatena.ne.jp/api/statuses/user_timeline.xml", parameters);
        }

        /// <summary>
        /// http://h.hatena.ne.jp/keyword/keyword に対応する、指定したキーワードに投稿されたエントリを返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetKeywordTimeline(params KeyValuePair<string, object>[] parameters)
        {
            return Get("http://h.hatena.ne.jp/api/statuses/keyword_timeline.xml",parameters);
        }

        /// <summary>
        /// 画像を含む最新のエントリのリストを返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetAlbum()
        {
            return Get("http://h.hatena.ne.jp/api/statuses/album.xml");
        }

        /// <summary>
        /// 指定されたキーワードの画像を含む最新のエントリリストを返します。
        /// </summary>
        /// <param name="keyword"></param>
        /// <returns></returns>
        public XDocument GetAlbum(string keyword)
        {
            return Get(string.Format("http://h.hatena.ne.jp/api/statuses/album/{0}.xml", Uri.EscapeUriString(keyword)));
        }

        /// <summary>
        /// http://h.hatena.ne.jp/user/id に対応する、指定したエントリーの情報を返します。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public XDocument GetShow(string id)
        {
            return Get(string.Format("http://h.hatena.ne.jp/api/statuses/show/{0}.xml", Uri.EscapeUriString(id)));
        }

        #endregion

        #region POST

        /// <summary>
        /// 新しいエントリーを投稿します。
        /// </summary>
        /// <param name="status">
        /// 投稿する本文を指定します。 keyword=本文 の形式でも投稿できます。
        /// 先頭の @id は id さんの最後のエントリーへの返信になります。
        /// </param>
        /// <returns>結果</returns>
        public bool UpdateStatus(string status)
        {
            var parameters = new KeyValuePair<string, object>[] { new KeyValuePair<string, object>("source",ClientName)
                                                                 ,new KeyValuePair<string, object>("status",status)};
            return Post("http://h.hatena.ne.jp/api/statuses/update.xml", parameters);
        }

        /// <summary>
        /// 新しいエントリーを投稿します。
        /// </summary>
        /// <param name="parameters">パラメータのキーと値</param>
        /// <returns></returns>
        public bool UpdateStatus(params KeyValuePair<string, object>[] parameters)
        {
            return Post("http://h.hatena.ne.jp/api/statuses/update.xml", parameters);
        }

        /// <summary>
        /// 指定したエントリーを削除します。
        /// </summary>
        public  bool DestroyStatus(string id)
        {
                return Post(string.Format("http://h.hatena.ne.jp/api/statuses/destroy/{0}.xml", Uri.EscapeUriString(id)));
        }

        #endregion

        #endregion

        #region スター関係

        /// <summary>
        /// 指定したエントリーにスターを一つ追加します。
        /// </summary>
        /// <param name="id"></param>
        public bool AddStar(string id)
        {
            return Post(string.Format("http://h.hatena.ne.jp/api/favorites/create/{0}.xml", Uri.EscapeUriString(id)));
        }

        /// <summary>
        /// 指定したエントリーのスターを一つ減らします。
        /// </summary>
        /// <param name="id"></param>
        public bool DeleteStar(String id)
        {
            return Post(string.Format("http://h.hatena.ne.jp/api/favorites/destroy/{0}.xml", Uri.EscapeUriString(id)));
        }

        #endregion

        #region ユーザフォロー関係

        #region GET

        /// <summary>
        /// 認証したユーザーがフォローしているユーザーのリストを100件返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetFriends()
        {
            return Get("http://h.hatena.ne.jp/api/statuses/friends.xml");
        }

        /// <summary>
        /// 指定したユーザーがフォローしているユーザーのリストを100件返します。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public XDocument GetFriends(string id)
        {
            return Get(string.Format("http://h.hatena.ne.jp/api/statuses/friends/{0}.xml", Uri.EscapeUriString(id)));
        }

        /// <summary>
        /// 認証したユーザーをフォローしているユーザーのリストを返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetFollowers()
        {
            return Get("http://h.hatena.ne.jp/api/statuses/followers.xml");
        }

        /// <summary>
        /// 指定したユーザーをフォローしているユーザーのリストを返します。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public XDocument GetFollowers(string id)
        {
            return Get(string.Format("http://h.hatena.ne.jp/api/statuses/followers/{0}.xml", Uri.EscapeUriString(id)));
        }

        /// <summary>
        /// 指定したユーザの情報を取得します。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public XDocument Getaaa(string id)
        {
            return Get(string.Format("http://h.hatena.ne.jp/api/friendships/show/{0}.xml", Uri.EscapeUriString(id)));
        }

        #endregion

        #region POST
        
        /// <summary>
        /// 指定ユーザーをお気に入りに追加します。
        /// 
        /// (旧:ユーザーをフォローします。)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool AddFollowUser(string id)
        {
            return Post(string.Format("http://h.hatena.ne.jp/api/friendships/create/{0}.xml", Uri.EscapeUriString(id)));
        }

        /// <summary>
        /// 指定ユーザーをお気に入りから解除します。
        /// 
        /// (旧:ユーザーのフォローをやめます。)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool DeleteFollowUser(string id)
        {
            return Post(string.Format("http://h.hatena.ne.jp/api/friendships/destroy/{0}.xml", Uri.EscapeUriString(id)));
        }

        #endregion

        #endregion

        #region キーワード関係

        #region GET

        /// <summary>
        /// Hot Keywords のリストを返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetHotKeyword()
        {
            return Get("http://h.hatena.ne.jp/api/keywords/hot.xml");
        }

        /// <summary>
        /// Keywords のリストを返します。
        /// </summary>
        /// <returns></returns>
        public XDocument GetKeywordList()
        {
            return Get("http://h.hatena.ne.jp/api/keywords/list.xml");
        }

        /// <summary>
        /// Keywords のリストを返します。
        /// </summary>
        /// <param name="page"></param>
        /// <returns></returns>
        public XDocument GetKeywordList(int page)
        {
            return Get("http://h.hatena.ne.jp/api/keywords/list.xml",new KeyValuePair<string, object>("page",page));
        }

        /// <summary>
        /// キーワードの情報を表示します。
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public XDocument GetKeywordShow(string keyword)
        {
            return Get(string.Format("http://h.hatena.ne.jp/api/keywords/show/{0}.xml"
                     , Uri.EscapeUriString(keyword)));
        }

        #endregion

        #region POST

        /// <summary>
        /// 指定キーワードをお気に入りに追加します。
        /// 
        /// (旧:キーワードをフォローします。)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool AddFollowKeyword(string keyword)
        {
            return Post(string.Format("http://h.hatena.ne.jp/api/keywords/create/{0}.xml"
                      , Uri.EscapeUriString(keyword)));
        }

        /// <summary>
        /// 指定キーワードをお気に入りから解除します。
        /// 
        /// (旧:キーワードのフォローをやめます。)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool DeleteFollowKeyword(string keyword)
        {
            return Post(string.Format("http://h.hatena.ne.jp/api/keywords/destroy/{0}.xml"
                      , Uri.EscapeUriString(keyword)));
        }

        /// <summary>
        /// 関連キーワードを設定します。word1 の情報を返します。
        /// </summary>
        /// <param name="word1"></param>
        /// <param name="word2"></param>
        /// <returns></returns>
        public bool CreateKeywordsRelation(string word1, string word2)
        {
            var parameters = new KeyValuePair<string, object>[] { new KeyValuePair<string, object>("word1",word1)
                                                                 ,new KeyValuePair<string, object>("word2",word2)};
            return Post("http://h.hatena.ne.jp/api/keywords/relation/create.xml", parameters);
        }

        /// <summary>
        /// 関連キーワードを解除します。word1 の情報を返します。
        /// なお、関連キーワードの設定の削除は自分が設定したものに限られます。
        /// </summary>
        /// <param name="word1"></param>
        /// <param name="word2"></param>
        /// <returns></returns>
        public bool DestroyKeywordsRelation(string word1, string word2)
        {
            var parameters = new KeyValuePair<string, object>[] { new KeyValuePair<string, object>("word1",word1)
                                                                 ,new KeyValuePair<string, object>("word2",word2)};
            return Post("http://h.hatena.ne.jp/api/keywords/relation/destroy.xml", parameters);
        }

        #endregion

        #endregion
    }
}


そのままの利用および改造改変はご自由にどうぞ。
このサンプルを元に、いい感じのHaikuクライアントを作ってもらえたら幸いです。