﻿#!/usr/bin/env python
# coding: utf-8

from dateutil.relativedelta import relativedelta
import datetime
import os
import csv

from selenium import webdriver
#from selenium.webdriver.chrome.options import Options

from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import NoSuchFrameException
from selenium.common.exceptions import NoSuchWindowException
from selenium.common.exceptions import NoSuchAttributeException
from selenium.common.exceptions import NoAlertPresentException
from selenium.common.exceptions import ElementNotVisibleException
from selenium.common.exceptions import ElementNotSelectableException
from selenium.common.exceptions import TimeoutException

from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from time import sleep
from random import randint

#chrome_options = Options()
Options = webdriver.FirefoxOptions()
# ヘッドレス化
Options.headless = True
# 必須
Options.add_argument('--no-sandbox')
Options.add_argument('--disable-gpu')
# エラーの許容
Options.add_argument('--ignore-certificate-errors')
Options.add_argument('--allow-running-insecure-content')
Options.add_argument('--disable-web-security')
# headless では不要そうな機能を除外
Options.add_argument('--disable-desktop-notifications')
Options.add_argument("--disable-extensions")
# UA設定 （なくてもいい）
Options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0')
# 言語 (なくてもいいが言語によって表示されるページが変わる可能性があるので念のため)
Options.add_argument('--lang=ja')
# 画像を読み込まないで軽くする
Options.add_argument('--blink-settings=imagesEnabled=false')



USER_AGENTS = [
 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
 "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
 "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
 "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
 "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
 "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
 "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
 "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
 "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
 "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
 "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
 "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
 "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
 "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
]
random_agent = USER_AGENTS[randint(0, len(USER_AGENTS)-1)]

MUFG_TOP_URL = 'https://entry11.bk.mufg.jp/ibg/dfw/APLIN/loginib/login?_TRANID=AA000_001'
INFORMATION_TITLE = 'お知らせ - 三菱東京ＵＦＪ銀行'
# MUFG_ID&Pass
ACCOUNT_ID = os.environ['ACCOUNT_ID']
IB_PASSWORD = os.environ['IB_PASSWORD']
caps = DesiredCapabilities.PHANTOMJS
caps["phantomjs.page.settings.userAgent"] = random_agent

##driver = webdriver.PhantomJS( executable_path='./phantomjs',  desired_capabilities=caps)
##driver = webdriver.Chrome(options=chrome_options)

# import os をしていなければしておく
log_path = os.path.join(os.path.dirname(__file__), 'geckodriver.log')
driver = webdriver.Firefox(options=Options, service_log_path=log_path)


def login(account_id: str, ib_password: str):
	global str_error
	str_error = None
	for x in range(0, 5):  # try 4 times
		try:
			error_num = 0
			## r = 1/0
			##print("====10===")
			driver.get(MUFG_TOP_URL)
			sleep(12)
			##print("====11===")
			error_num = 1000
			driver.find_element_by_id('tx-contract-number').send_keys(account_id)
			##print("====12===")
			error_num = 10000
			driver.find_element_by_id('tx-ib-password').send_keys(ib_password)
			##print("====13===")
			#driver.find_element_by_xpath('//img[@alt="ログイン"]').click()
			error_num = 100000
			driver.find_element_by_xpath('//button[@class="gonext"]').click()
			##print("====14===")
			error_num = 0
		except NoSuchElementException as ex:
			str_error  = ex
			error_num += 1
		except NoSuchFrameException as ex:
			str_error  = ex
			error_num += 2
		except NoSuchWindowException as ex:
			str_error  = ex
			error_num += 3
		except NoSuchAttributeException as ex:
			str_error  = ex
			error_num += 4
		except NoAlertPresentException as ex:
			str_error  = ex
			error_num += 5
		except ElementNotVisibleException as ex:
			str_error  = ex
			error_num += 6
		except ElementNotSelectableException as ex:
			str_error  = ex
			error_num += 7
		except TimeoutException as ex:
			str_error  = ex
			error_num += 8
		except Exception as ex:
			str_error  = ex
			error_num += 99
		if error_num>0:
			if x == 0:
				dataline = '------------------{}----------------'.format(datetime.datetime.now().strftime('%Y-%m-%d  %H:%M:%S'))
				print(dataline)
			print("{}{}".format("login error_num=", error_num))
			##driver.navigate().refresh();
			sleep(2)  # wait for 2 seconds before trying to fetch the data again
			if x == 4:
				dataline = '------------------{}----------------'.format(datetime.datetime.now().strftime('%Y-%m-%d  %H:%M:%S'))
				
				error_path ='{}/{}'.format( os.environ['BKLIST_PATH'], 'error.txt')
				fp_error= open(error_path, "w", encoding="utf-8")
				fp_error.write("{}:{};{}{}".format("login error_num", error_num,str_error,'\n'))
				###fp_error.write(str_error.message + ':'  + str_error.args + '\n')
				###fp_error.write( err.args + '\n')
				fp_error.write(dataline + '\n')
				fp_error.flush()
				fp_error.close()
				status_path ='{}/{}'.format( os.environ['BKLIST_PATH'], 'status.txt')
				status_book = open(status_path, "w", encoding="utf-8")
				status_book.write(dataline + '\n')
				status_book.write("ng" + '\n' )
				status_book.flush()
				status_book.close()
				exit()
		else:
			break


def logout():
	# <li class="logout"><a href="#" onClick="JavaScript:logoutFromAD001(); return false;">ログアウト</a></li>
	driver.find_element_by_link_text('ログアウト').click()


def read_information():
	"""
	お知らせがある場合は既読にします
	:return:
	"""
	while True:
		if not driver.title == INFORMATION_TITLE:
			return
		information = driver.find_element_by_xpath(
			'//table[@class="data"]/tbody/tr')
		information.find_element_by_name('hyouzi').click()
		driver.find_element_by_xpath('//img[@alt="トップページへ"]').click()


def select_selectbox_value(element_name: str, value: str):
	"""
	セレクトボックスの値を選択します
	:param element_name:
	:param value:
	:return:
	"""
	selectbox = Select(driver.find_element_by_name(element_name))
	#print(str(value))
	selectbox.select_by_visible_text(str(value))


def select_time_period(_from: datetime, _to: datetime):
	"""
	指定した表示期間を選択します
	:param _from:
	:param _to:
	:return:
	"""

	wait = WebDriverWait(driver, 30)
	wait.until(ec.presence_of_element_located((By.ID, 'appoint')))

	driver.find_element_by_id('appoint').click()
	# driver.find_element_by_name('SHOUKAIKIKAN_RADIO').click()

	select_selectbox_value('SHOUKAIKIKAN_FROM_Y', str(_from.year))
	select_selectbox_value('SHOUKAIKIKAN_FROM_M', str(_from.month))
	select_selectbox_value('SHOUKAIKIKAN_FROM_D', str(_from.day))
	select_selectbox_value('SHOUKAIKIKAN_TO_Y', str(_to.year))
	select_selectbox_value('SHOUKAIKIKAN_TO_M', str(_to.month))
	select_selectbox_value('SHOUKAIKIKAN_TO_D', str(_to.day))


def show_details(_fp_list, _fp_book, _last: str,  _dataline: str):
	banking_list = driver.find_elements_by_xpath(
		'//table[@class="data memo yen_nyushutsukin_003"]/tbody/tr')
	flg = 0
	lastflg = 0
	for banking in banking_list:
		detail = banking.find_elements_by_tag_name('td')

		date = datetime.datetime.strptime(detail[0].text.replace('\n', ''), '%Y年%m月%d日')

		#payment = in_or_out_payment(detail[1].text, detail[2].text)
		content = detail[3].text
		conlist = content.split("　")
		cont1=conlist[0]
		del conlist[0]
		cont2=''.join(conlist)
		total = to_number(detail[4].text)
		pay = to_number(detail[1].text)
		income = to_number(detail[2].text)
		line = '{}\t{}\t{}\t{}\t{}\t{}'.format(date.strftime('%Y-%m-%d'),cont1, cont2, pay, income , total )
		_fp_list.write(line + '\n')
		print(line)
		
		if flg == 1:
			if lastflg == 0:
				#print(dataline)
				_fp_book.write(_dataline + '\n')
			_fp_book.write(line + '\n')
			lastflg = flg
		else:
			lastflg = flg
			if _last == '':
				flg=1
			if _last == line + '\n':
				flg=1

	try:
		driver.find_element_by_xpath('//a/img[@alt="新しい明細"]').click()
	except NoSuchElementException:
		return
	else:
		# 例外がない(=次のページがある)から次のページへ
		show_details()


def to_number(yen: str) -> int:
	"""
	xxx,xxx,xxx円からカンマと「円」を削除します
	:param yen:
	:return: int:
	"""
	if yen == '':
		return ''
	return int(yen.replace(',', '').replace('円', ''))


def in_or_out_payment(_out: str, _in: str) -> int:
	"""
	入出金の判定を行います
	:param _out:
	:param _in:
	:return:
	"""
	if not _out == '':
		# 出金
		return to_number(_out) * -1
	# 入金
	return to_number(_in)


def main(_from: datetime, _to: datetime):
	global str_error
	try:
		#account_id = os.environ['ACCOUNT_ID']
		#ib_password = os.environ['IB_PASSWORD']
		account_id = ACCOUNT_ID
		ib_password = IB_PASSWORD

		str_error = None
		login(account_id, ib_password)
		
		for x in range(0, 5):  # try 4 times
			try:
				##driver.find_element_by_link_text('入出金明細').click()
				driver.find_element_by_xpath('//div[@class="detail"]').click()
				str_error = None
				error_num = 0
			except NoSuchElementException as ex:
				str_error  = ex
				error_num = 1
			except NoSuchFrameException as ex:
				str_error  = ex
				error_num = 2
			except NoSuchWindowException as ex:
				str_error  = ex
				error_num = 3
			except NoSuchAttributeException as ex:
				str_error  = ex
				error_num = 4
			except NoAlertPresentException as ex:
				str_error  = ex
				error_num = 5
			except ElementNotVisibleException as ex:
				str_error  = ex
				error_num = 6
			except ElementNotSelectableException as ex:
				str_error  = ex
				error_num = 7
			except TimeoutException as ex:
				str_error  = ex
				error_num = 8
			except Exception as ex:
				str_error  = ex
				error_num = 99
				pass
			if error_num>0:
				print("{}{}".format("main error_num=", error_num))
				##driver.navigate().refresh();
				if x == 4:
					dataline = '------------------{}----------------'.format(datetime.datetime.now().strftime('%Y-%m-%d  %H:%M:%S'))
					print(dataline)
					error_path ='{}/{}'.format( os.environ['BKLIST_PATH'], 'error.txt')
					fp_error= open(error_path, "w", encoding="utf-8")
					fp_error.write(dataline + '\n')
					fp_error.write("{}:{};{}{}".format("login error_num", error_num,str_error,'\n'))
					fp_error.flush()
					fp_error.close()
					status_path ='{}/{}'.format( os.environ['BKLIST_PATH'], 'status.txt')
					status_book = open(status_path, "w", encoding="utf-8")
					status_book.write(dataline + '\n')
					status_book.write("ng" + '\n' )
					status_book.flush()
					status_book.close()
					exit()
			else:
				break


		# お知らせ画面がある場合は既読にする
		read_information()
		
		

		# 残高を取得
		#total = driver.find_element_by_id('setAmountDisplay')
		total = driver.find_element_by_xpath('//td[@class="number"]/strong')
		print(total.text )
		total_amount = to_number(total.text)
		##total = driver.find_element_by_id('account')
		##total_amount = to_number(total.text)

		print('{:,d}円'.format(total_amount))

		##driver.find_element_by_xpath('//img[@alt="入出金明細をみる"]').click()

		# 期間を指定する
		select_time_period(_from, _to)
		driver.find_element_by_xpath('//img[@alt="照会"]').click()
		flist_path ='{}/{}'.format( os.environ['BKLIST_PATH'], os.environ['BKLIST_NAME'])
		fbook_path ='{}/{}'.format( os.environ['BKLIST_PATH'], os.environ['BKBOOK_NAME'])
		
		targetLine = ""
		if os.path.isfile(fbook_path):
			fcheck = open(fbook_path, "r", encoding="utf-8")
			while 1:
				mLine = fcheck.readline();
				if not mLine:
					break;
				targetLine = mLine;
			fcheck.close()
		fp_list = open(flist_path, "a", encoding="utf-8")
		fp_book = open(fbook_path, "a", encoding="utf-8")
		
		dataline = '------------------{}----------------'.format(datetime.datetime.now().strftime('%Y-%m-%d  %H:%M:%S'))
		print(dataline)
		fp_list.write(dataline + '\n' )
		show_details(fp_list, fp_book, targetLine, dataline)
		fp_book.flush()
		fp_book.close()
		
		fp_list.flush()
		fp_list.close()
		##driver.find_element_by_xpath('//img[@alt="明細をダウンロード"]').click()
		##
		##select_time_period(_from, _to)
		##driver.find_element_by_xpath('//img[@alt="ダウンロード（CSV形式）"]').click

		logout()
		status_path ='{}/{}'.format( os.environ['BKLIST_PATH'], 'status.txt')
		status_book = open(status_path, "w", encoding="utf-8")
		status_book.write(dataline + '\n')
		status_book.write("ok" + '\n' )
		status_book.flush()
		status_book.close()
	finally:
		driver.close()

if __name__ == '__main__':
	td_1m =relativedelta(months=1) 
	end = datetime.datetime.now()
	tmp_start = end - td_1m
	start = datetime.datetime(year=tmp_start.year, month=tmp_start.month, day=1)
	main(start, end)
