LoginSignup
13
12

More than 5 years have passed since last update.

Qiitaに投稿するRubyスクリプト

Last updated at Posted at 2013-12-16

概要

linuxOS上で書いたテキストをQiitaに投稿したかったので、Qiita APIを使った投稿スクリプトをRubyで書いた。好きなエディタで書いて、このツールを使って投稿して、ブラウザで結果確認、という作業を想定しているので投稿機能しかない。ちなみにこの投稿もそうして書いている。さらにちなむとQiita gemというものの存在は今はじめて知った。まいっか。

動作環境

ruby製。手元では2.0.0で動作させてるけど、1.9でも動くんじゃないかな。標準環境に加えて、簡単にコマンドラインを作るthorライブラリと、例によってIDとパスワードを分離してくれるpitライブラリが必要。
ディレクトリ構造などはUnixライクなものを想定したコードになっているのでWindowsではそのままだと動かないかも。
あとQiita APIを使っているのでgithubやtwitterの認証だけでは駄目で、投稿したいQiitaユーザーにパスワードを設定しておく必要がある。

動作の説明

起動

qiita.thorファイルのある場所でthor qiita コマンドするなりthor install qiita.thorしておいて好きな場所でthor qiita コマンドするなり。

コマンド

コマンドはneweditしかなくてnewは新規作成、editは指定したファイルを編集、もしくは、無指定なら直前に編集したファイルを編集。どちらもエディタが自動で立ちあがる(環境変数 EDITOR必須)ので編集して閉じるとuploadするかどうかを尋ねてくる。作成したファイルは決めうちで~/qiita以下に置かれる。初回アップロード時にはpitの機能でQiitaのユーザー名とパスワードの入力を求められる。ホームディレクトリ直下に平文で保存されて後は使いまわされるので、気になる人は適当に置き換えてください。

投稿設定

タグや公開範囲の設定は、各々のファイルの頭にYAML Front-matterの形で記述しておくとupload時に解釈する。upload時には(新規投稿時に自動設定される)uuidが存在するかどうかで新規か更新かも自動判別。
uploadのみを行う機能はないけど、自分の用途としてはeditでファイル指定して一回開いてからupload
すれば十分なので。

制約

thorを使ってしまったので本来ライブラリに分ける部分も1ファイルにせざるを得なかった。200行足らずなので、まあいいっちゃいいんですけど。thorで外部ファイルのrequireと、システムへのインストールを両立させる方法をご存知の方は教えてください。ただし、画像の投稿はできない。まあしょうがない。

ソース

qiita.thor
# -*- coding: utf-8 -*-
require 'yaml'
require 'pp'
require 'net/http'
require 'net/https'
require 'json'
require 'pit'

QiitaDir = "~/qiita"
QiitaLatest = "#{QiitaDir}/latest"

class Qiita < Thor
  include Thor::Actions

  desc "new", "新規エントリを作成してエディタで開く"
  def new
    file_path = make_entry_file
    edit_and_upload(file_path)
  end

  desc "edit", "ファイル名を指定してエントリを編集する。指定なしなら直前に編集したエントリ"
  def edit(file_path=nil)
    file_path = File.read(File.expand_path(QiitaLatest)).chomp unless file_path
    edit_and_upload(file_path)
  end

  no_commands do
    def make_entry_file
      qiita_dir = QiitaDir
      now = Time.now
      file_title = now.strftime("%Y-%m-%d-%H%M%S")
      year = now.strftime("%Y")
      month = now.strftime("%m")
      sep = '---'
      body = <<EOS
#{sep}
uuid: 
title: 
tags:
- name: 
private: true
#{sep}
EOS
      file_path = "#{qiita_dir}/#{year}/#{month}/#{file_title}.md"
      create_file file_path, body
      file_path
    end

    def qiita_agent
      config = Pit.get("qiita",:require=>{
                         "user" => "user id",
                         "pass" => "password"
                       })
      agent = QiitaAPI.new
      agent.auth(config["user"],config["pass"])
      agent
    end

    def edit_and_upload(file_path)
      system "#{ENV["EDITOR"]} #{file_path}"
      save_latest(file_path)
      puts "edit done"
      if yes?("#{file_path} upload? (y/N)")
        upload(file_path)
        puts "uploaded"
      end
    end

    def upload(file_path)
      path = File.expand_path(file_path)
      str = File.read(path)
      ar = str.split("---\n")
      yaml_str = ar[1]
      body = ar[2]
      config = YAML.load(yaml_str)
      puts yaml_str
      pp config
      config["body"] = body
      qa = qiita_agent

      unless config["uuid"]
        result = qa.post_entry(config)
        config["uuid"] = result["uuid"]
        config.delete("body")
        yaml_str = config.to_yaml
        File.open(path,"w") do |f|
          f.puts yaml_str
          f.puts "---"
          f.puts body
        end
      else
        qa.put_entry(config)
      end

    end

    def save_latest(file_path)
      open(File.expand_path(QiitaLatest),"w") do |f|
        f.puts file_path
      end
    end
  end
end

class QiitaAPI

  ### JSON HTTPS

  def http_setup(url_str)
    url = URI.parse(url_str)
    http = Net::HTTP.new(url.host,url.port)
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    # http.set_debug_output $stderr
    [url,http]
  end

  def get(url_str)
    url, http = http_setup(url_str)
    res = http.get(url.request_uri)
    JSON.parse(res.body)
  end

  def post(url_str,data)
    url, http = http_setup(url_str)
    json_str = JSON.generate(data)
    res = http.post(url.request_uri,
                    json_str,{'Content-Type' =>'application/json'})
    JSON.parse(res.body)
  end

  def put(url_str,data)
    url, http = http_setup(url_str)
    json_str = JSON.generate(data)
    res = http.put(url.request_uri,
                   json_str,{'Content-Type' =>'application/json'})
    JSON.parse(res.body)
  end

  ### main

  def initialize
    @base_url = 'https://qiita.com/api/v1'
  end

  def auth?
    @token != nil
  end

  def auth(user,pass)
    r = post("#{@base_url}/auth",{url_name: user,password: pass})
    @token = r["token"]
  end

  def make_url(child_path)
    path = "#{@base_url}/#{child_path}"
    unless auth?
      path
    else
      "#{path}?token=#{@token}"
    end
  end

  ### methods

  def rate_limit
    get(make_url('rate_limit'))
  end

  def user
    raise "require auth" unless auth?
    get(make_url('user'))
  end

  def users(name)
    get(make_url("users/#{name}"))
  end

  def items
    raise "require auth" unless auth?
    get(make_url('items'))
  end

  def post_entry(data)
    raise "require auth" unless auth?
    post(make_url("items"),data)
  end

  def put_entry(data)
    raise "require auth" unless auth?
    uuid = data["uuid"]
    put(make_url("items/#{uuid}"),data)
  end
end
13
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
12