atomutilを使ってrubyでアメブロに投稿する方法

これまで、はてなダイアリーatomutilを使ってrubyではてなダイアリーに投稿する方法 - 水清無魚(すいせいむぎょ))に引き続き、ライブドアのブログ投稿(atomutilを使ってrubyでライブドアのブログに投稿する方法 - 水清無魚(すいせいむぎょ))しました。

こんどは本丸というべきアメブロに投稿してみたいと思います。というのが、結構皆さん苦労されているみたいなんです。

はてなダイアリーライブドアのブログと同じようにAtomPubの仕様ではあるのですが、ちょっと癖があって、パスワードをMD5ハッシュに変換しないといけないとか、nonceに使用できる文字列に制限があったりします。

しかし、カテゴリの設定に関しては結局わかりませんでした。Twitterのつぶやきを自動投稿するツールで設定されていたような気がするのですが・・・

とりあえず、タイトルと本文の投稿のみの現状を公開しておきます。

# -*- coding: utf-8 -*-

# @see : http://d.hatena.ne.jp/lyokato/20071211/1197353609
# gem install atomutil
require 'atomutil'

# Livedoorブログやアメブロ対策のためオーバーライドし、戻り値としてLocationではなくステータスコードを返却するようにする
class Atompub::Client
  def create_entry(post_uri, entry, slug=nil)
    unless entry.kind_of?(Atom::Entry)
      entry = Atom::Entry.new :stream => entry
    end
    service = @service_info.get(post_uri)
    # Livedoorブログの場合、serviceが取得できなくてservice.allows_category?でエラーになるので、if serviceでくくる
    if service
      unless entry.categories.all?{ |c| service.allows_category?(c) }
        raise RequestError, "Forbidden Category"
      end
    end
    create_resource(post_uri, entry.to_s, Atom::MediaType::ENTRY.to_s, slug)
    return @res.code
  rescue Atompub::ResponseError #=> rs
    # アメブロでは正常に投稿できても@res['Location']が空になってResponseErrorになるので捕獲する
    return @res.code
  end
end

# アメブロでnonceに英数以外が含まれるとエラーになるのでオーバーライド
class Atompub::Auth::Wsse
  def gen_token
    # 以下で生成される文字列ではAmebloの場合エラーとなる
    #nonce = Array.new(10){rand(0x100000000)}.pack('I*')
    nonce = [*1..9, *'A'..'Z', *'a'..'z'].sample(16).join
    nonce_base64 = [nonce].pack('m').chomp
    now = Time.now.utc.iso8601
    digest = [Digest::SHA1.digest(nonce + now + @password)].pack('m').chomp
    sprintf(%Q<UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s">,
      @username, digest, nonce_base64, now)
  end
end
    
def entry(id, password, title, content, category=nil)
  # アメブロに限り、パスワード文字列のmd5ハッシュ値化必要
  # @see : http://senpai4.blog29.fc2.com/blog-entry-452.html
  password = Digest::MD5.hexdigest(password)
  auth = Atompub::Auth::Wsse.new :username => id, :password => password
  client = Atompub::Client.new :auth => auth
  
  service = client.get_service("http://atomblog.ameba.jp/servlet/_atom/blog")
  ns = Atom::Namespace.new(:uri => 'http://purl.org/atom/ns#')
  links = service.getlist(ns, 'link')
  href = nil
  links.each do |link|
    if link.attributes['rel'] == 'service.post'
      href = link.attributes['href']
      break
    end
  end
  # 自動改行にはならないので(ブログ側の設定次第?)
  content = content.gsub(/\n/, '<br/>');

  # カテゴリの設定方法は不明
  new_entry =<<EOM
<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://purl.org/atom/ns#">
  <title>#{title}</title>
  <content type="application/xhtml+xml">
    <![CDATA[#{content}]]>
  </content>
</entry>
EOM
  return client.create_entry(href, new_entry)
end

title = "テスト"
content =<<EOM
<b>太字ですか?</b>
改行されていますか?
EOM

puts entry('<ログインID>', '<パスワード>', title, content)

以下のサイトを参考にしました。ありがとうございました。