Skip to content

Instantly share code, notes, and snippets.

@yuanying
Created March 25, 2011 08:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yuanying/886519 to your computer and use it in GitHub Desktop.
Save yuanying/886519 to your computer and use it in GitHub Desktop.
ガーミンコネクトのデータを 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment