#! /usr/bin/env ruby Main { # name "asana2flowdock" # description <<-__ asana2flowdock relays asana events into flowdock awesomely __ # config <<-__ asana: token: YOUR_ASANA_API_TOKEN workspace: YOUR_ASANA_WORKSPACE_OR_ORGANIZATION_NAME flowdock: token: YOUR_FLOWDOCK_API_TOKEN __ # db { create_table('state') do primary_key 'id' String 'key' String 'value' index :key, :unique=>true index :value end unless table_exists?('state') create_table('digests') do primary_key 'id' String 'digest' index :digest, :unique=>true end unless table_exists?('digests') } # option('--quiet', '-q') # daemonizes! # def run # lock! # trap('INT'){ exit!(0) } # loop do begin process_stories! rescue => e STDERR.puts("#{ e.message }(#{ e.class })\n#{ Array(e.backtrace).join(10.chr) }") end sleep(rand(STDIN.tty? ? 10 : 30)) end end def process_stories! # asana = Asana.api_for config.get(:asana, :token) #asana.debug = true #asana.logger = logger # workspace = asana.my.workspaces.detect{|workspace| workspace.name == config.get(:asana, :workspace)} unless workspace workspaces = asana.my.workspaces.map{|workspace| workspace.name} abort "unable to find your workspace=#{ config.get(:asana, :workspace).inspect } in workspaces=#{ workspaces.join(', ') }" end # last_run_time = last_run_time! # logger.info "last_run_time: #{ last_run_time }" # started_at = Time.now window = (last_run_time.to_f ... started_at.to_f) # workspace.projects.each do |project| project.tasks(:modified_since => last_run_time.iso8601).each do |task| next if task.name =~ /:\s*$/ # skip sections... next if task.name.strip.empty? # skip shite batch = Hash.new task.stories(:opt_fields => 'id,created_at,type,text,created_by.name,created_by.email').map do |story| created_at = Coerce.time(story[:created_at]) next unless window.include?(created_at.to_f) created_by = story[:created_by] next unless created_by batch[task] ||= {} batch[task][created_by] ||= [] batch[task][created_by].push(story) end # batch.each do |task, created_by_stories| created_by_stories.each do |created_by, stories| # url = "https://app.asana.com/0/#{ project.id }/#{ task.id }/f" email = created_by[:email] # from = Map.new from[:name] = created_by[:name] from[:address] = created_by[:email] # flow = Flowdock::Flow.new( :api_token => config.get(:flowdock, :token), :source => "asana", :project => project.name, :from => from ) # activity = [] br = "
" hr = "
" stories.each do |story| activity.push( "#{ br }#{ story[:text] }#{ br * 2 }:: #{ story[:type] } activity#{ hr }" ) end activity.push(hr) activity.push(br) activity.push(url) content = activity.join # msg = Map.new msg[:format] = "html" msg[:subject] = "#{ project.name } :: #{ task.name }" msg[:tags] = ["asana"] msg[:content] = content # digest = Digest::MD5.hexdigest(msg.to_json) next if db[:digests].where(:digest => digest).first # pushed = unless params[:quiet].given? flow.push_to_team_inbox(msg) else true end # db[:digests].insert(:digest => digest) if pushed # logger.info "relayed #{ created_by[:email] } activity for #{ url }" end end end end # set_last_run_time!(started_at) end def last_run_time! if db[:state].where(:key => 'last_run_time').first.nil? hr = 60 * 60 value = (Time.now - hr).utc.iso8601(3) db[:state].insert(:key => 'last_run_time', :value => value) end Time.parse(db[:state].where(:key => 'last_run_time').first[:value]) end def set_last_run_time!(*args) value = if args.empty? Time.now else args.first end unless value.is_a?(String) value = value.utc.iso8601(3) end Time.parse(value) begin db[:state].insert(:key => 'last_run_time', :value => value) rescue db[:state].where(:key => 'last_run_time').update(:value => value) end Time.parse(db[:state].where(:key => 'last_run_time').first[:value]) end # mode(:shell){ description <<-__ drop into the shell. fuck with the db. __ def run require 'pry' Pry.config.hooks.delete_hooks(:before_session) prompt = "~ >> " Pry.config.prompt = proc{|*a| prompt } binding.pry end } # mode(:config){ description <<-__ display the configuration. __ def run config.to_hash.to_yaml.display end } # def lock!(which = :default) FileUtils.mkdir_p(dotdir) path = File.join(dotdir, "#{ which }.lock") FileUtils.touch(path) rescue nil lock = open(path, 'ab+') rescue open(path, 'wb+') status = lock.flock(File::LOCK_EX|File::LOCK_NB) unless status == 0 warn "another process is already running..." exit(42) unless status == 0 end (@locks ||= []).push(lock) end } BEGIN { # require_relative '../lib/asana2flowdock.rb' Asana = Asana2Flowdock::Asana # STDOUT.sync = true STDERR.sync = true }