yuanying
3/25/2011 - 8:01 AM

ガーミンコネクトのデータを Nike+ と同期するスクリプト。

ガーミンコネクトのデータを Nike+ と同期するスクリプト。

#!/usr/bin/env ruby -wKU
require 'rubygems'
require 'rss/1.0'
require 'rss/2.0'
require 'rss/dublincore'
require 'open-uri'
require 'uri'
require 'cgi'
require 'optparse'

garmin_username = nil
nike_email      = nil
nike_password   = nil
proxy_host      = nil
proxy_port      = nil
opts = OptionParser.new
opts.on('-g', '--garmin-username GARMIN_USERNAME') { |v| garmin_username = v }
opts.on('-n', '--nike-email NIKE_PLUS_EMAIL') { |v| nike_email = v }
opts.on('-p', '--nike-password NIKE_PLUS_PASSWORD') { |v| nike_password = v }
opts.on('--proxy-host PROXY_HOST') { |v| proxy_host = v }
opts.on('--proxy-port PROXY_PORT', Integer) { |v| proxy_port = v }
opts.parse!(ARGV) 

tmp = '/tmp/last-garmin-connect-url.txt'
url = nil
if File.exist?(tmp)
  open(tmp) do |io|
    url = io.read.chomp
  end
end

rss = nil
rss_url = "http://connect.garmin.com/feed/rss/activities?feedname=Garmin#{Time.now.strftime('%Y%m%d%H')}&explore=true&activityType=running&eventType=all&activitySummarySumDistance-unit=kilometer&activitySummarySumDuration-unit=hour&activitySummaryGainElevation-unit=meter&owner=#{garmin_username}&sortField=beginTimestamp"
open(rss_url) do |io|
  rss = RSS::Parser.parse(io.read)
end

module Multipart
  class Post
    # We have to pretend like we're a web browser...
    USERAGENT = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6" unless const_defined?(:USERAGENT)
    BOUNDARY = "0123456789ABLEWASIEREISAWELBA9876543210" unless const_defined?(:BOUNDARY)
    CONTENT_TYPE = "multipart/form-data; boundary=#{ BOUNDARY }" unless const_defined?(:CONTENT_TYPE)
    HEADER = { "Content-Type" => CONTENT_TYPE, "User-Agent" => USERAGENT } unless const_defined?(:HEADER)

    def self.prepare_query(params)
      fp = []

      params.each do |k, v|
        fp.push(StringParam.new(k, v))
      end

      # Assemble the request body using the special multipart format
      query = fp.collect {|p| "--" + BOUNDARY + "\r\n" + p.to_multipart }.join("")  + "--" + BOUNDARY + "--"
      return query, HEADER
    end
  end

  private

  # Formats a basic string key/value pair for inclusion with a multipart post
  class StringParam
    attr_accessor :k, :v

    def initialize(k, v)
      @k = k
      @v = v
    end

    def to_multipart
      return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"\r\n\r\n#{v}\r\n"
    end
  end
end

class Tcx2NikePlus
  require 'net/http'
  Net::HTTP.version_1_2
  
  def post activity_id, email, password, proxy_addr=nil, proxy_port=nil
    Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.awsmithson.com', 80) {|http|
      data, headers = ::Multipart::Post.prepare_query(
        "garminActivityId" => activity_id, 
        "chkGps" => 'true',
        "nikeEmail" => email,
        "nikePassword" => password
      )
      response = http.post('/tcx2nikeplus/tcx2nikeplus/convert', data, headers)
      raise "Error: #{response.body}" unless /"success":true/ =~ response.body
    }
  end
end

urls = []
rss.items.each do |item|
  if item.link == url
    break
  end
  urls << item.link
end

urls.reverse.each do |url|
  activity_id = url.split('/').last
  Tcx2NikePlus.new.post(activity_id, nike_email, nike_password, proxy_host, proxy_port)
  
  open(tmp, 'w') do |io|
    io.write url
  end
end