MovableTypeに投稿

30分プログラム、その136。MovableTypeに投稿するRubyプログラム。

はてダラMovableType版を作りたいので、それの基本となる部分。コマンドラインオプションが多すぎて使いずらいので、これを使ったラッパをそのうち書こう。

使い方

次のようなsample.txtを用意する。

タイトル
Tag:: a,b,c
Category:: 日記

こんにちは、こんにちは。
$ ruby movabletype.rb \
         --entry=http://example.com/mt-xmlrpc.cgi \
         --username=mzp \
         --password=hogehoge \
         --blogid 5 \
         sample.txt

ソースコード

#! /opt/local/bin/ruby -w
# -*- mode:ruby; coding:utf-8 -*-
#
# movabletype.rb -
#
# Copyright(C) 2007 by mzp
# Author: MIZUNO Hiroki <hiroki1124@gmail.com> 
# http://mzp.sakura.ne.jp/
#
# Timestamp: 2007/09/17 15:13:30
#
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Ruby itself.
#
require 'xmlrpc/client'
module MovableType
  Entry = Struct.new 'Entry',:postid

  class Blog
    def initialize(entry,blogid,username,password)
      @xml = XMLRPC::Client.new2 entry
      @blogid   = blogid
      @username = username
      @password = password
    end

    def post(title,body,option={})
      id = @xml.call('metaWeblog.newPost',
                     @blogid,@username,@password,
                     make_content(title,body,option),
                     0)
      Entry.new id
    end

    def edit(entry,title,body,option={})
      id = @xml.call('metaWeblog.editPost',
                     entry.postid,
                     @username,@password,
                     make_content(title,body,option),
                     1)
      Entry.new id
    end

    def make_content(title,body,option)
      {
        'title'=>title,
        'description'=>body,
        'mt_tags'=>option[:tag].join(','),
        'mt_convert_breaks'=>option[:format]
      }
    end
    private :make_content

    def delete(entry)
      @xml.call 'blogger.deletePost',0,entry.postid,@username,@password,1
    end

    def category_list
      unless defined?(@cat) then
        @cat = {}
        @xml.call('mt.getCategoryList',@blogid,@username,@password).each{|s|
          @cat[s['categoryName']] = s['categoryId'].to_i
        }
      end
      @cat
    end

    def set_category(entry,category)
      @xml.call('mt.setPostCategories',
                entry.postid,
                @username,
                @password,
                category.map{|id| {'categoryId'=>id } })
    end

    def publish(entry)
      @xml.call 'mt.publishPost',entry.postid,@username,@password
    end
  end
end

if __FILE__ == $0 then
  def parse(io)
    title = io.gets.chomp

    option = {}
    until (s = io.gets.chomp).empty? do
      name,value = s.chomp.split('::',2) # like Perl
      option[name.downcase.to_sym] = value.strip
    end

    body = io.read

    [title,body,option]
  end

  require 'optparse'

  entry = username = password = blogid = nil
  type = :post # or :edit/:delete
  postid = nil # require at :edit/:delete

  opt = OptionParser.new
  opt.on('-e ENTRY'   ,'--entry ENTRY'){|entry|}
  opt.on('-u USERNAME','--username USERNAME'){|username|}
  opt.on('-p PASSWORD','--password PASSWORD'){|password|}
  opt.on('-b BLOG_ID' ,'--blogid BLOG_ID'){|blogid|}
  opt.on('-t TYPE','--type TYPE','post/edit/delete'){|t| type = t.to_sym }
  opt.on('-i ID'  ,'--postid ID','post id'){|postid|}
  opt.parse! ARGV

  unless entry or username or password or blogid then
    abort 'require entry/username/password/blogid'
  end

  if (type == :edit or type == :delete) and postid == nil then
    abort 'require postid when edit or delete'
  end

  title = body = option = nil
  if type == :post or type == :edit then
    io = unless ARGV.empty? then File.open(ARGV.shift) else $stdin end
    title,body,option = parse io
  end

  mt = MovableType::Blog.new entry,blogid,username,password
  case type
  when :post
    e = mt.post(title,
                body,
                {:tag=>option[:tag].split(','),:format=>'markdown'})
    mt.set_category e,[mt.category_list[option[:category]]]
    mt.publish e
    p e
  when :edit
    puts mt.edit(MovableType::Entry.new(postid),
                 title,
                 body,
                 {:tag=>option[:tag].split(','),:format=>'markdown'})
    mt.set_category entry,[mt.category_list[option[:category]]]
    mt.publish entry
  when :delete
    mt.delete MovableType::Entry.new(postid)
  end
end