55import hashlib
66import typing
77import aiohttp
8- from .capcha_ocr import CapchaProcessing , CapchaOCR
98from .main import MBBankError , MBBank
109from .wasm_helper import wasm_encrypt
1110from .main import headers_default
@@ -30,10 +29,10 @@ class MBBankAsync(MBBank):
3029 username (str): MBBank Account Username
3130 password (str): MBBank Account Password
3231 proxy (str, optional): Proxy url. Example: "http://127.0.0.1:8080". Defaults to None.
33- ocr_class (CapchaProcessing, optional): CapchaProcessing class. Defaults to TesseractOCR ().
32+ ocr_class (CapchaProcessing, optional): CapchaProcessing class. Defaults to CapchaOCR ().
3433 """
3534
36- def __init__ (self , * , username : str , password : str , proxy : dict = None , ocr_class = None ):
35+ def __init__ (self , * , username : str , password : str , proxy : dict = None , ocr_class = None ):
3736 super ().__init__ (username = username , password = password , proxy = proxy , ocr_class = ocr_class )
3837 # convert proxy dict by requests to aiohttp format
3938 if len (self .proxy .values ()):
@@ -50,54 +49,77 @@ async def _get_wasm_file(self):
5049 self ._wasm_cache = await r .read ()
5150 return self ._wasm_cache
5251
52+ async def get_capcha_image (self ) -> bytes :
53+ """
54+ Get capcha image as bytes
55+
56+ Returns:
57+ success (bytes): capcha image as bytes
58+ """
59+ rid = f"{ self ._userid } -{ get_now_time ()} "
60+ json_data = {
61+ 'sessionId' : "" ,
62+ 'refNo' : rid ,
63+ 'deviceIdCommon' : self .deviceIdCommon ,
64+ }
65+ headers = headers_default .copy ()
66+ headers ["X-Request-Id" ] = rid
67+ headers ["Deviceid" ] = self .deviceIdCommon
68+ headers ["Refno" ] = rid
69+ async with aiohttp .ClientSession () as s :
70+ async with s .post ("https://online.mbbank.com.vn/retail-web-internetbankingms/getCaptchaImage" ,
71+ headers = headers , json = json_data , proxy = self .proxy ) as r :
72+ data_out = await r .json ()
73+ return base64 .b64decode (data_out ["imageString" ])
74+
75+ async def login (self , captcha_text : str ):
76+ """
77+ Login to MBBank account
78+
79+ Args:
80+ captcha_text (str): capcha text from capcha image
81+
82+ Raises:
83+ MBBankError: if api response not ok
84+ """
85+ payload = {
86+ "userId" : self ._userid ,
87+ "password" : hashlib .md5 (self ._password .encode ()).hexdigest (),
88+ "captcha" : captcha_text ,
89+ 'sessionId' : "" ,
90+ 'refNo' : f'{ self ._userid } -{ get_now_time ()} ' ,
91+ 'deviceIdCommon' : self .deviceIdCommon ,
92+ "ibAuthen2faString" : self .FPR ,
93+ }
94+ wasm_bytes = await self ._get_wasm_file ()
95+ loop = asyncio .get_running_loop ()
96+ data_encrypt = await loop .run_in_executor (pool , wasm_encrypt , wasm_bytes , payload )
97+ async with aiohttp .ClientSession () as s :
98+ async with s .post ("https://online.mbbank.com.vn/retail_web/internetbanking/doLogin" ,
99+ headers = headers_default , json = {"dataEnc" : data_encrypt }, proxy = self .proxy ) as r :
100+ data_out = await r .json ()
101+ if data_out ["result" ]["ok" ]:
102+ self .sessionId = data_out ["sessionId" ]
103+ self ._userinfo = data_out
104+ return
105+ else :
106+ raise MBBankError (data_out ["result" ])
107+
53108 async def _authenticate (self ):
54109 while True :
55110 self ._userinfo = None
56111 self .sessionId = None
57112 self ._temp = {}
58- rid = f"{ self ._userid } -{ get_now_time ()} "
59- json_data = {
60- 'sessionId' : "" ,
61- 'refNo' : rid ,
62- 'deviceIdCommon' : self .deviceIdCommon ,
63- }
64- headers = headers_default .copy ()
65- headers ["X-Request-Id" ] = rid
66- headers ["Deviceid" ] = self .deviceIdCommon
67- headers ["Refno" ] = rid
68- async with aiohttp .ClientSession () as s :
69- async with s .post ("https://online.mbbank.com.vn/retail-web-internetbankingms/getCaptchaImage" ,
70- headers = headers , json = json_data , proxy = self .proxy ) as r :
71- data_out = await r .json ()
72- img_bytes = base64 .b64decode (data_out ["imageString" ])
113+ img_bytes = await self .get_capcha_image ()
73114 text = await asyncio .get_event_loop ().run_in_executor (
74115 pool , self .ocr_class .process_image , img_bytes
75116 )
76- payload = {
77- "userId" : self ._userid ,
78- "password" : hashlib .md5 (self ._password .encode ()).hexdigest (),
79- "captcha" : text ,
80- 'sessionId' : "" ,
81- 'refNo' : f'{ self ._userid } -{ get_now_time ()} ' ,
82- 'deviceIdCommon' : self .deviceIdCommon ,
83- "ibAuthen2faString" : self .FPR ,
84- }
85- wasm_bytes = await self ._get_wasm_file ()
86- loop = asyncio .get_running_loop ()
87- dataEnc = await loop .run_in_executor (pool , wasm_encrypt , wasm_bytes , payload )
88- async with aiohttp .ClientSession () as s :
89- async with s .post ("https://online.mbbank.com.vn/retail_web/internetbanking/doLogin" ,
90- headers = headers_default , json = {"dataEnc" : dataEnc }, proxy = self .proxy ) as r :
91- data_out = await r .json ()
92- if data_out ["result" ]["ok" ]:
93- self .sessionId = data_out ["sessionId" ]
94- self ._userinfo = data_out
95- return
96- elif data_out ["result" ]["responseCode" ] == "GW283" :
97- pass
98- else :
99- err_out = data_out ["result" ]
100- raise Exception (f"{ err_out ['responseCode' ]} | { err_out ['message' ]} " )
117+ try :
118+ return await self .login (text )
119+ except MBBankError as e :
120+ if e .code == "GW283" :
121+ continue # capcha error, try again
122+ raise e
101123
102124 async def _req (self , url , * , json = None , headers = None ):
103125 if headers is None :
@@ -275,7 +297,6 @@ async def getSavingDetail(self, accNo: str, accType: typing.Literal["OSA", "SBA"
275297 data_out = await self ._req ("https://online.mbbank.com.vn/api/retail_web/saving/getDetail" , json = json_data )
276298 return data_out
277299
278-
279300 async def getLoanList (self ):
280301 """
281302 Get all loan list from your account
@@ -362,3 +383,8 @@ async def userinfo(self):
362383 else :
363384 await self .getBalance ()
364385 return self ._userinfo
386+
387+ async def getBanks (self ):
388+ data_out = await self ._req ("https://online.mbbank.com.vn/api/retail_web/common/getBankList" )
389+ return data_out
390+
0 commit comments