<commit_before>require 'json'
require 'base64'
require 'RMagick'
require 'tempfile'

module BankScrap
  class Ing < Bank

    BASE_ENDPOINT = 'https://ing.ingdirect.es/'
    LOGIN_ENDPOINT     = BASE_ENDPOINT + 'genoma_login/rest/session'
    POST_AUTH_ENDPOINT = BASE_ENDPOINT + 'genoma_api/login/auth/response'
    CLIENT_ENDPOINT    = BASE_ENDPOINT + 'genoma_api/rest/client'
    PRODUCTS_ENDPOINT  = BASE_ENDPOINT + 'genoma_api/rest/products'

    SAMPLE_WIDTH  = 30
    SAMPLE_HEIGHT = 30

    def initialize(user, password, log: false, debug: false, extra_args:)
      @dni      = user
      @password = <PASSWORD>
      @birthday = extra_args['birthday']
      @log      = log
      @debug    = debug

      initialize_connection
      bundled_login
      get_products
    end

    def get_balance
      log 'get_balance'
      balance = {}
      @data.each do |item|
        balance[item['name']] = item['balance']
      end

      balance
    end

    def raw_product_data
      @data
    end

    private

    def bundled_login
      selected_positions = login
      ticket = pass_pinpad(selected_positions)
      post_auth(ticket)
    end

    def login
      set_headers({
        "Accept"       => 'application/json, text/javascript, */*; q=0.01',
        'Content-Type' => 'application/json; charset=utf-8'
      })

      param = '{' +
                '"loginDocument":{' +
                  '"documentType":0,"document":"' + @dni.to_s +
                '"},' +
                '"birthday":"' + @birthday.to_s + '",' +
                '"companyDocument":null,' +
                '"device":"desktop"}' +
              '}'

      response = JSON.parse(post(LOGIN_ENDPOINT, param))
      positions = response['pinPositions']
      pinpad    = response['pinpad']

      pinpad_numbers_paths = save_pinpad_numbers(pinpad)
      pinpad_numbers = recognize_pinpad_numbers(pinpad_numbers_paths)

      get_correct_positions(pinpad_numbers, positions)
    end

    def pass_pinpad(positions)
      set_headers({
          "Accept"       => 'application/json, text/javascript, */*; q=0.01',
          'Content-Type' => 'application/json; charset=utf-8'
        })

      param = "{\"pinPositions\": #{positions}}"
      response = put(LOGIN_ENDPOINT, param)
      JSON.parse(response)['ticket']
    end

    def post_auth(ticket)
        set_headers({
          "Accept"       => 'application/json, text/javascript, */*; q=0.01',
          'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'
        })

        param = "ticket=#{ticket}&device=desktop"
        post(POST_AUTH_ENDPOINT, param)
    end

    def get_products
      set_headers({
        "Accept"       => '*/*',
        'Content-Type' => 'application/json; charset=utf-8'
      })

      @data = JSON.parse(get(PRODUCTS_ENDPOINT))
    end

    def save_pinpad_numbers(pinpad)
      pinpad_numbers_paths = []
      pinpad.each_with_index do |digit,index|
        tmp = Tempfile.new(["pinpad_number_#{index}__", '.png'])
        File.open(tmp.path, 'wb'){ |f| f.write(Base64.decode64(digit)) }
        pinpad_numbers_paths << tmp.path
      end

      pinpad_numbers_paths
    end

    def recognize_pinpad_numbers(pinpad_numbers_paths)
      pinpad_numbers = []
      pinpad_images = Magick::ImageList.new(*pinpad_numbers_paths)
      0.upto(9) do |i|
        single_number = pinpad_images[i]
        differences = []
        0.upto(9) do |j|
          pinpad_pixels_sample = single_number.get_pixels(0,0, SAMPLE_WIDTH, SAMPLE_HEIGHT)

          img = Magick::ImageList.new(File.join(File.dirname(__FILE__), "/ing/numbers/pinpad#{j}.png")).first
          number_pixels_sample = img.get_pixels(0, 0, SAMPLE_WIDTH, SAMPLE_HEIGHT)
          diff = 0
          pinpad_pixels_sample.each_with_index do |pixel, index|<commit_msg>Support BankScrap::Account in ING integration<commit_after>require 'json'
require 'base64'
require 'RMagick'
require 'tempfile'

module BankScrap
  class Ing < Bank

    BASE_ENDPOINT = 'https://ing.ingdirect.es/'
    LOGIN_ENDPOINT     = BASE_ENDPOINT + 'genoma_login/rest/session'
    POST_AUTH_ENDPOINT = BASE_ENDPOINT + 'genoma_api/login/auth/response'
    CLIENT_ENDPOINT    = BASE_ENDPOINT + 'genoma_api/rest/client'
    ACCOUNTS_ENDPOINT  = BASE_ENDPOINT + 'genoma_api/rest/products'

    SAMPLE_WIDTH  = 30
    SAMPLE_HEIGHT = 30

    def initialize(user, password, log: false, debug: false, extra_args:)
      @dni      = user
      @password = <PASSWORD>
      @birthday = extra_args['birthday']
      @log      = log
      @debug    = debug

      initialize_connection
      bundled_login

      super
    end

    def get_balance
      log 'get_balance'
      balances = {}
      total_balance = 0
      @accounts.each do |account|
        balances[account.description] = account.balance
        total_balance += account.balance
      end

      balances['TOTAL'] = total_balance
      balances
    end

    def raw_accounts_data
      @raw_accounts_data
    end

    def fetch_accounts
      set_headers({
        "Accept"       => '*/*',
        'Content-Type' => 'application/json; charset=utf-8'
      })

      @raw_accounts_data = JSON.parse(get(ACCOUNTS_ENDPOINT))

      @raw_accounts_data.collect do |account|
        if account['iban']
          Account.new(
            bank: self,
            id: account['uuid'],
            name: account['name'],
            balance: account['balance'],
            currency: 'EUR',
            available_balance: account['availableBalance'],
            description: (account['alias'] || account['name']),
            iban: account['iban'],
            bic: account['bic']
          )
        end
      end.compact
    end

    def fetch_transactions_for(account)
      raise Exception.new('Not implemented yet')
    end

    private

    def bundled_login
      selected_positions = login
      ticket = pass_pinpad(selected_positions)
      post_auth(ticket)
    end

    def login
      set_headers({
        "Accept"       => 'application/json, text/javascript, */*; q=0.01',
        'Content-Type' => 'application/json; charset=utf-8'
      })

      param = '{' +
                '"loginDocument":{' +
                  '"documentType":0,"document":"' + @dni.to_s +
                '"},' +
                '"birthday":"' + @birthday.to_s + '",' +
                '"companyDocument":null,' +
                '"device":"desktop"}' +
              '}'

      response = JSON.parse(post(LOGIN_ENDPOINT, param))
      positions = response['pinPositions']
      pinpad    = response['pinpad']

      pinpad_numbers_paths = save_pinpad_numbers(pinpad)
      pinpad_numbers = recognize_pinpad_numbers(pinpad_numbers_paths)

      get_correct_positions(pinpad_numbers, positions)
    end

    def pass_pinpad(positions)
      set_headers({
          "Accept"       => 'application/json, text/javascript, */*; q=0.01',
          'Content-Type' => 'application/json; charset=utf-8'
        })

      param = "{\"pinPositions\": #{positions}}"
      response = put(LOGIN_ENDPOINT, param)
      JSON.parse(response)['ticket']
    end

    def post_auth(ticket)
        set_headers({
          "Accept"       => 'application/json, text/javascript, */*; q=0.01',
          'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'
        })

        param = "ticket=#{ticket}&device=desktop"
        post(POST_AUTH_ENDPOINT, param)
    end

    def save_pinpad_numbers(pinpad)
      pinpad_numbers_paths = []
      pinpad.each_with_index do |digit,index|
        tmp = Tempfile.new(["pinpad_number_#{index}__", '.png'])
        File.open(tmp.path, 'wb'){ |f| f.write(Base64.decode64(digit)) }
        pinpad_numbers_paths << tmp.path
      end

      pinpad_numbers_paths
    end

    def recognize_pinpad_numbers(pinpad_numbers_paths)
      pinpad_numbers = []
      pinpad_images = Magick::ImageList.new(*pinpad_numbers_paths)
      0.upto(9) do |i|
        single_number = pinpad_images[i]
        differences = []
        0.upto(9) do |j|
          pinpad_pixels_sample = single_number.get_pixels(0,0, SAMPLE_WIDTH, SAMPLE_HEIGHT)

          img = Magick::ImageList.new(File.join(File.dirname(__FILE__), "/ing/numbers/pinpad#{j}.png")).first
          number_pixels_sample = img.get_pixels(0, 0, SAMPLE_WIDTH, SAMPLE_HEIGHT)
          diff = 0
          pinpad_pixels_sample.each_with_index do |pixel, index|
{'fraction_non_alphanumeric': 0.12316680577083582, 'fraction_numerical': 0.007988553714081316, 'mean_word_length': 2.4069861900893583, 'pattern_counts': {'":': 14, '<': 11, '<?xml version=': 0, '>': 21, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6171847', 'n_tokens_mistral': 2816, 'n_tokens_neox': 2626, 'n_words': 451}