class RubyMint
Constants
- ACCOUNT_TYPES
- JSON_HEADERS
- VERSION
Public Class Methods
Initialize RubyMint
@param username [String] usually an email address @param password [String]
# File lib/ruby_mint.rb, line 26 def initialize(username, password) @username = username @password = password @request_id = 34 @token = nil end
Public Instance Methods
Get account data
@param account_types [Array<String>] Type of accounts to retrieve. Defaults to all types. @return [Hash]
# File lib/ruby_mint.rb, line 103 def accounts(account_types = ACCOUNT_TYPES) # Use a new request_id @request_id += 1 account_query = { "input" => JSON.dump([{ "args" => { "types" => account_types }, "id" => @request_id.to_s, "service" => "MintAccountService", "task" => "getAccountsSorted", }])} # Use token to get list of accounts results = agent.post("https://wwws.mint.com/bundledServiceController.xevent?legacy=false&token=#{@token}", account_query, JSON_HEADERS) raise RubyMintError.new("Unable to obtain account information. Response code: #{results.code}") if results.code != "200" raise RubyMintError.new("Not logged in.") if results.body.include?('Session has expired.') account_body = JSON.load(results.body) if !account_body || !account_body["response"] || !account_body["response"][@request_id.to_s] || !account_body["response"][@request_id.to_s]["response"] raise RubyMintError.new("Unable to obtain account information (no account information in response).") end account_body["response"][@request_id.to_s]["response"] end
Request that Mint.com refresh its account and transaction data
@param sleep_time [Integer] Num of seconds to wait between calls to refreshing? when block is passed @param block [Block] Code to execute upon completion of of refreshing
# File lib/ruby_mint.rb, line 79 def initiate_account_refresh(sleep_time = 3) agent.post("https://wwws.mint.com/refreshFILogins.xevent", { "token" => @token }, JSON_HEADERS) if block_given? loop{ sleep sleep_time; break if !refreshing? } yield end end
Check if user is logged in already by the presence of a token.
@return [Boolean]
# File lib/ruby_mint.rb, line 71 def logged_in? !@token.nil? end
Login retrieves a user token from Mint.com
@param use_token [String] Use an existing token
# File lib/ruby_mint.rb, line 37 def login(use_token = nil) @token = use_token and return if use_token response = agent.get("https://wwws.mint.com/login.event?task=L") raise RubyMintError.new("Unable to GET Mint login page.") if response.code != "200" response = agent.post("https://wwws.mint.com/getUserPod.xevent", { "username" => @username }, JSON_HEADERS) raise RubyMintError.new("Unable to POST to getUserPod.") if response.code != "200" query = { "username" => @username, "password" => @password, "task" => "L", "browser" => "firefox", "browserVersion" => "27", "os" => "Linux", } response = agent.post("https://wwws.mint.com/loginUserSubmit.xevent", query, JSON_HEADERS) if response.code != "200" || !response.body.include?("token") raise RubyMintError.new("Mint.com login failed. Response code: #{response.code}") end login_body = JSON.load(response.body) if !login_body || !login_body["sUser"] || !login_body["sUser"]["token"] raise RubyMintError.new("Mint.com login failed (no token in login response body).") end @token = login_body["sUser"]["token"] end
Is Mint.com in the process of refreshing its data?
@return [Boolean]
# File lib/ruby_mint.rb, line 90 def refreshing? response = agent.get("https://wwws.mint.com/userStatus.xevent", JSON_HEADERS) if response.code != "200" || !response.body.include?("isRefreshing") raise RubyMintError.new("Unable to check if account is refreshing.") end JSON.parse(response.body)["isRefreshing"] end
Get transactions from mint. Returned as JSON. Paginate
Options:
include_pending [Boolean] default false search_term [String] default ""
@param start_date [Time] get all transactions on or after this date @param end_date [Time] get all transactions up to and including this date @param options [Hash] options hash @returns [Array<Hash>] array of transactions
# File lib/ruby_mint.rb, line 151 def transactions(start_date, end_date = Time.now, options = {}) include_pending = options.fetch('include_pending', false) search_term = options.fetch('search_term', '') offset = 0 results = [] # Convert start and end dates start_date = Time.local(start_date.year, start_date.month, start_date.day) end_date = Time.local(end_date.year, end_date.month, end_date.day) loop do next_page = transaction_page(offset, search_term) break if next_page.empty? # Filter out pending transactions if !include_pending next_page.reject!{ |t| t['isPending'] } end results.concat next_page break if earliest_mint_date(next_page) < start_date offset += next_page.count end # Filter by date results.select do |t| t['date'] >= start_date && t['date'] <= end_date end end
Get transactions from mint. They are returned as CSV and include ALL the transactions available
@return [String] CSV of all transactions
# File lib/ruby_mint.rb, line 133 def transactions_csv results = agent.get("https://wwws.mint.com/transactionDownload.event", JSON_HEADERS) raise RubyMintError.new("Unable to obtain transactions.") if results.code != "200" raise RubyMintError.new("Non-CSV content returned.") if !results.header["content-type"].include?("text/csv") results.body end
Private Instance Methods
# File lib/ruby_mint.rb, line 185 def agent @agent ||= Mechanize.new { |agent| agent.user_agent_alias = 'Linux Firefox' } end
Get the earliest date from this set of transactions. Assumes they are in reverse order.
@params transactions [Array<Hash>] @returns [Time]
# File lib/ruby_mint.rb, line 223 def earliest_mint_date(transactions) transactions.last['date'] end
Queries require a 12-digit random number
# File lib/ruby_mint.rb, line 228 def random_number # They are starting with 14, so I won't break the mold. Get 10 more. nums = (0..9).to_a result = "14" 10.times{ result << nums.sample.to_s } result end
Get a single page of transaction data. Mint always returns 50 transactions per page.
# File lib/ruby_mint.rb, line 192 def transaction_page(offset, search_term) # Example query: https://wwws.mint.com/app/getJsonData.xevent?queryNew=&offset=0&filterType=cash&acctChanged=T&task=transactions&rnd=1436026512488 base_url = "https://wwws.mint.com/app/getJsonData.xevent" search_query = "?queryNew=#{URI.encode(search_term)}" offset_query = "&offset=#{offset}" transaction_query = "&filterType=cash&comparableType=8&task=transactions&rnd=#{random_number}" json_results = agent.get("#{base_url}#{search_query}#{offset_query}#{transaction_query}", JSON_HEADERS) raise RubyMintError.new("Unable to obtain transactions.") if json_results.code != "200" raise RubyMintError.new("Non-JSON content returned.") if !json_results.header["content-type"].include?("text/json") transform_transaction_times JSON.parse(json_results.body)['set'][0]['data'] end
Mint returns transactions in two formats: βMar 8β for this year, β10/08/14β for previous years (month/day/year). Transform dates into ruby time objects.
NOTE: Mint only returns the date of the transaction, there is no time
@param transactions [Array<Hash>] array of transactions
# File lib/ruby_mint.rb, line 212 def transform_transaction_times(transactions) transactions.map do |t| t['date'] = (t['date'] =~ /\d+\/\d+\/\d+/ ? Time.strptime(t['date'], "%D") : Time.parse(t['date'])) t end end