Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 4 additions & 12 deletions nlbcli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys
import os
import re
from . import formatter
from datetime import date, timedelta

# local files
Expand Down Expand Up @@ -41,15 +42,8 @@ def main():
url = 'https://www.nlbklik.com.mk/Retail/Account'
_, soup = nlb_post(url, data)
table_cells = soup.select('.dps-content')
print('Account Id:', parsed_args.account_id)
print('Account owner:', table_cells[0].text)
print('Status:', table_cells[1].text)
print('Current balance:', table_cells[3].text)
print('Available balance:', table_cells[5].text)
print('Allowed overdraft:', table_cells[7].text)
print('Reserved funds:', table_cells[9].text)
print('Last change:', table_cells[13].text)
print('Last interest:', table_cells[15].text)
# Print the data in the desired output format
formatter.OutputResult(parsed_args, table_cells, "accounts")

elif parsed_args.accounts_subparser_name == 'transactions':
# direction explanation:
Expand Down Expand Up @@ -95,9 +89,7 @@ def main():

# we print all cells except the "Details" link/button
# todo: parse the 0th column to extract the unique transaction id
for tr in soup.select('tbody > tr'):
tds = tr.select('td')[1:]
print('\t'.join(td.text.strip() for td in tds))
formatter.OutputResult(parsed_args, soup.select('tbody > tr'), "transactions")

elif parsed_args.accounts_subparser_name == 'reservations':
url = 'https://www.nlbklik.com.mk/Retail/ReservationList'
Expand Down
4 changes: 4 additions & 0 deletions nlbcli/args_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
month_ago = today - timedelta(days=30)

parser = argparse.ArgumentParser()
# FIXME: this would need to be enabled so that every command has the --format argument, instead of just nlbcli accounts <id> balance
#parser.add_argument('--format', nargs="?", choices=['tab','csv','json'], required=False, default='tab')
main_subparsers = parser.add_subparsers(dest='subparser_name')
login_parser = main_subparsers.add_parser(
'login', help='Log in and save your credentials.')
Expand All @@ -30,10 +32,12 @@
transactions_parser.add_argument(
'--type', choices=['in', 'out'], required=False)
transactions_parser.add_argument('--name', required=False)
transactions_parser.add_argument('--format', nargs="?", choices=['tab','csv','json'], required=False, default='tab')

# ACCOUNTS [id] balance
balance_parser = accounts_subparsers.add_parser(
'balance', help="Show the balance on the specified account")
balance_parser.add_argument('--format', nargs="?", choices=['tab','csv','json'], required=False, default='tab')

# ACCOUNtS [id] reservations
reservations_parser = accounts_subparsers.add_parser(
Expand Down
77 changes: 77 additions & 0 deletions nlbcli/formatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import re
import csv
import json
import sys


def OutputResult(parsed_args, table_cells, input_type):
output_format = parsed_args.format
if output_format == 'tab':
if input_type == "accounts":
print('Account Id:', parsed_args.account_id)
print('Account owner:', table_cells[0].text)
print('Status:', table_cells[1].text)
print('Current balance:', table_cells[3].text)
print('Available balance:', table_cells[5].text)
print('Allowed overdraft:', table_cells[7].text)
print('Reserved funds:', table_cells[9].text)
print('Last change:', table_cells[13].text)
print('Last interest:', table_cells[15].text)
elif input_type == "transactions":
for tr in table_cells:
tds = tr.select('td')[1:]
print('\t'.join(td.text.strip() for td in tds))
elif output_format == 'json':
if input_type == "accounts":
json_output = json.dumps(
{
'account-id': parsed_args.account_id,
'account-owner': table_cells[0].text,
'account-status': table_cells[1].text,
'current-balance': table_cells[3].text,
'available-balance': table_cells[5].text,
'allowed-overdraft': table_cells[7].text,
'reserved-funds': table_cells[9].text,
'last-change': table_cells[13].text,
'last-interest': table_cells[15].text
},indent=4
)
elif input_type == "transactions":
txns = []
for tr in table_cells:
tds = tr.select('td')[1:]
dict_txn = {}
dict_txn['date'] = tds[0].text.strip()
dict_txn['recipient_name'] = tds[1].text.strip()
dict_txn['txn_description'] = tds[2].text.strip()
dict_txn['status'] = tds[3].text.strip()
dict_txn['amount'] = tds[4].text.strip()
dict_txn['txn_fee'] = tds[5].text.strip()
dict_txn['balance'] = tds[6].text.strip()
txns.append(dict_txn)
json_output = json.dumps(txns,indent=4)
print(json_output)
elif output_format == 'csv':
if input_type == "accounts":
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you handle transactions here also? The nlbcli accounts XXX transactions --format csv doesn't print anything.

field_names = ['account-id',
'account-owner',
'account-status',
'current-balance',
'available-balance',
'allowed-overdraft',
'reserved-funds',
'last-change',
'last-interest']
csv_writer = csv.DictWriter(sys.stdout, field_names)
csv_writer.writeheader()
csv_writer.writerow({
'account-id': parsed_args.account_id,
'account-owner': table_cells[0].text,
'account-status': table_cells[1].text,
'current-balance': table_cells[3].text,
'available-balance': table_cells[5].text,
'allowed-overdraft': table_cells[7].text,
'reserved-funds': table_cells[9].text,
'last-change': table_cells[13].text,
'last-interest': table_cells[15].text
})
110 changes: 65 additions & 45 deletions nlbcli/nlbklik-chain.pem
Original file line number Diff line number Diff line change
@@ -1,49 +1,69 @@
-----BEGIN CERTIFICATE-----
MIIEiTCCA3GgAwIBAgIQAlqK7xlvfg1sIQSyGuZwKzANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0xNzExMDYxMjIzNTJaFw0yNzExMDYxMjIzNTJaMFwxCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xGzAZBgNVBAMTElRoYXd0ZSBSU0EgQ0EgMjAxODCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMoIXuVTipccHkMvtoqnVumLhEOorJ16VYJ6FEuGty+P
Up8cyrEgW2+6It2mnC142ukGCE6+E6bry7s+uQUMPkrh8DIfE071BsVHc4k+gKOL
8QEkm6OZZpJraK0NLbTNcqL0+ThaZaa0jFPBCBqE+P0u8xF1btxqMSmsDYfMk2B4
3yW6JlmRxoNSNabKnLgoGs7XHO4Uv3ZcZas4HnnpfMxJIyaiUlBm0Flh/6D+mkwM
n/nojt4Ji7gVwaQITCacewbb/Yp0W1h+zWOkkS9F8Ho8lAuKfLIFqWeTn2jllWNg
2FiVX+BV75OnETt85pLYZkTgq72nj82khXhBJFTn2AMCAwEAAaOCAUAwggE8MB0G
A1UdDgQWBBSjyF5lVOUweMEF6gcKalnMuf7eWjAfBgNVHSMEGDAWgBQD3lA1VtFM
u2bwo+IbG8OXsj3RVTAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm
MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDsw
OTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFs
Um9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0
cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzANBgkqhkiG9w0BAQsFAAOCAQEARE2F
5d0cgozhZNWokCLfdhhl6mXSOyU3SoPamYcWfLH1CzMwD8a1+pFvwHIQfvlwXFH8
MrjB3C+jVobNbVWRrgqS3Jsa0ltRH/Ffs6ZTgP4WJYm1SNpUbgR7LWUD2F+PTvKB
M/gf9eSyqP4OiJslYaa38NU1aVAxZI15o+4xX4RZMqKXIIBTG2V+oPBjQ1oPmHGA
C/yWt2eThvb8/re7OpSpUdJyfGf97XeM4PiJAl6+4HQXhjwN7ZPZKrQv9Ay33Mgm
YLVQA+x9HONZXx9vvy8pl9bu+NVYWKGxzGxBK0CBozmVUCeXQPJKPTZleYuNM18p
U1P8Xh1CDguM+ZEoew==
MIIGzjCCBbagAwIBAgIQDUG2N7MQ1k8yapHLr+yiyDANBgkqhkiG9w0BAQsFADBC
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRwwGgYDVQQDExNU
aGF3dGUgRVYgUlNBIENBIEcyMB4XDTIzMDQwNjAwMDAwMFoXDTI0MDQwNTIzNTk1
OVowgZ8xEzARBgsrBgEEAYI3PAIBAxMCTUsxHTAbBgNVBA8MFFByaXZhdGUgT3Jn
YW5pemF0aW9uMRAwDgYDVQQFEwc0NjY0NTMxMQswCQYDVQQGEwJNSzEPMA0GA1UE
BxMGU2tvcGplMRwwGgYDVQQKExNOTEIgQmFua2EgQUQgU2tvcGplMRswGQYDVQQD
ExJ3d3cubmxia2xpay5jb20ubWswggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCcFO0Sqxp4su64m6CMRrNzrEt5ui+5mbpi3TjXTJ34V+iDk+xnhFOosQ+P
FNM0doK2+LDEBdVwTpTf7vS3ftEFi8ZedeEd1I8g3z+edTz3bRurjKA+83I59bmS
BbiTHU4TqPKUb7H4iNV0MDrXWDJPhaF82djZVFzOFHrkwoVMBD/UomFw9kwIOHt9
ytf4/k80TpH2PGveCXBoPlin+4WAf8sMwZTEG94Il3Xc4XCxhnIFjJFHVVrIuMKE
piuyNskgk4iZABwdZikDVc7YKZdYwsJl2LIeWNez24z5Y+6SDZbsVgeB3v1bfKAA
cLDkzqDi4MSVUiTtYqBcnqNAq4T/AgMBAAGjggNgMIIDXDAfBgNVHSMEGDAWgBRs
LuRhtMO5vfDKrabBaHq41MwdoDAdBgNVHQ4EFgQUBwTNMc+POWYqXj+XEE9krVCQ
+ZYwLQYDVR0RBCYwJIISd3d3Lm5sYmtsaWsuY29tLm1rgg5ubGJrbGlrLmNvbS5t
azAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MHEGA1UdHwRqMGgwMqAwoC6GLGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9UaGF3
dGVFVlJTQUNBRzIuY3JsMDKgMKAuhixodHRwOi8vY3JsNC5kaWdpY2VydC5jb20v
VGhhd3RlRVZSU0FDQUcyLmNybDBKBgNVHSAEQzBBMAsGCWCGSAGG/WwCATAyBgVn
gQwBATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMw
cQYIKwYBBQUHAQEEZTBjMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
dC5jb20wOwYIKwYBBQUHMAKGL2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9U
aGF3dGVFVlJTQUNBRzIuY3J0MAkGA1UdEwQCMAAwggF9BgorBgEEAdZ5AgQCBIIB
bQSCAWkBZwB2AO7N0GTV2xrOxVy3nbTNE6Iyh0Z8vOzew1FIWUZxH7WbAAABh1Wy
26QAAAQDAEcwRQIgJkzMXrDb+paUpqqt3cTNO/Kj2GtDq8Ajs3nIHGZkXqsCIQDN
F19KiqiQF/lhi1UnJQVDUbBjucL1ZB842m3UuExwowB1AHPZnokbTJZ4oCB9R53m
ssYc0FFecRkqjGuAEHrBd3K1AAABh1Wy3A0AAAQDAEYwRAIgCcDm4RkYp/b79nMO
YCwjlyHDaJKypf0356fZA/NIjaUCIBXkIrFypBVdR4tA6GVy1ebJMMHOsfAYR0at
vjJLS+WPAHYASLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMAAAGHVbLb
0wAABAMARzBFAiEAscugz4Z6j0mOBGmUruu+F4l2BwOUugU7HrGTTj84MNICIFCp
npiEy1CEZAyCAUXvUzX1yXaPExizQZW3sZuakro2MA0GCSqGSIb3DQEBCwUAA4IB
AQBRPkQKRTIstnu8FgPm4DuHYMfdIvTguo6b7g29pdVCLTye1AVMeNqCFAzNERF5
W8BDdeJBZRsgghJsiFpjuZlIXx5R8XT1i3F9oZWEFp567LUG5JQAPsEbAaM/0eEF
SPbQEjUPnW7qpT8vvvow9YcN1qTuc19xjhv+veCgSmny5zCk+HtdQxnYfOdrpvhd
qvnPykMiHyMyIqyfKDDgJHlJIaJxVCVHvbuTwS3l0vDozugIMkq8Gen5VJRBkbXb
S0qPqhs2Pmdu2qdrUH9xxvR8JWz/iQh73rBPV98CEeyuvBG7JOAJuBq0mc7AP0bb
hnXLjgr9rirDyUzvqK+/DYeE
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MIIFOjCCBCKgAwIBAgIQB8LG0yxvDgqrrA3Q+fzVszANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0yMDA3MDIxMjQzMDJaFw0zMDA3MDIxMjQzMDJaMEIxCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxHDAaBgNVBAMTE1RoYXd0ZSBFViBSU0Eg
Q0EgRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ6jsIHs3bmIoe
7DvnuSvGX375jpWKv25Gf7uz8GZQT3DFVHeRS0NKn5WosqMVNYJKKEImR1Gb2dxl
90/GDypngguJeBVxdTtgGWPpGnuKPXfc6Qy8yKS1SRQsEs2q2EHl0tEU+/vyNro0
DDZcox947My8htb1dLx0Q/y9KjEuagQ5AXeLidtiaiAyKnThZRslD8EK/EcHRvkP
AVMSfGyCVmho3VLBP7LAlLA/RyrAX282OfK8lPZqsASNsOQhmsZPT3IuQhXz7RXc
nCpBM+GfZoFSP6+uO5j8TZMkqLTAuVAsSyGY8zNbzYupA7QmPcIfAwqG1oD9dGah
2GgKoeTnAgMBAAGjggILMIICBzAdBgNVHQ4EFgQUbC7kYbTDub3wyq2mwWh6uNTM
HaAwHwYDVR0jBBgwFoAUTiJUIBiV5uNu5g/6+rkS7QYXjzkwDgYDVR0PAQH/BAQD
AgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAG
AQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au
ZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj
ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwN6A1oDOGMWh0dHA6Ly9j
cmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RHMi5jcmwwgc4GA1Ud
IASBxjCBwzCBwAYEVR0gADCBtzAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln
aWNlcnQuY29tL0NQUzCBigYIKwYBBQUHAgIwfgx8QW55IHVzZSBvZiB0aGlzIENl
cnRpZmljYXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFJlbHlpbmcg
UGFydHkgQWdyZWVtZW50IGxvY2F0ZWQgYXQgaHR0cHM6Ly93d3cuZGlnaWNlcnQu
Y29tL3JwYS11YTANBgkqhkiG9w0BAQsFAAOCAQEADf6H4Rxu128yUZjIR/vkWEv0
PnYKWUQkGXwbqaioq/zjVC/+LrIwBKwEH2aBQyBO/uvMwF+4PSThZKinw51Pfp8w
PcaBMeuQR1PEIYjQN3+NjRpMIFb2CUtbnMsHybKqKSW3rsTT2r2EIBtDrZ3EfeEg
5bfneV6PKvGqcQRZq5BBNTH11tedhcLJB+7F7GRVz8cKCTz0INpQk8gthjBND1+7
CR+ZHTWPqdXGRqiFJ09NTVmohj4dgL3VyOIArpmPbigODbp80fxzoQJepkHFef6q
mniWnIl7HApTrTIV/UN22gbCcL5dosGv69wnOVQomQsalcOkujetA5BruTiB9A==
-----END CERTIFICATE-----
7 changes: 3 additions & 4 deletions nlbcli/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def _login(username, password):
"X-Requested-With": "XMLHttpRequest"}
url = 'https://www.nlbklik.com.mk/Account/LoginUserNamePassword'
login_res = new_session.post(
url, data=data, verify=constants.VERIFY_SSL, allow_redirects=False)
url, data=data, allow_redirects=False)
login_json_body = login_res.json()

# Response always contains 'ErrorMessage' key and has status code 200, unfortunately.
Expand Down Expand Up @@ -120,7 +120,7 @@ def nlb_post(url, data):
Returns a tuple: the response and the parsed html body."""
def req_fn(sess):
return sess.post(
url, data=data, verify=constants.VERIFY_SSL, allow_redirects=False)
url, data=data, allow_redirects=False)
response = _fetch_with_autorenewal(req_fn)
soup = bs.BeautifulSoup(response.text, 'html.parser')
return (response, soup)
Expand All @@ -132,8 +132,7 @@ def nlb_get(url):
Returns a tuple: the response and the parsed html body.
"""
def req_fn(sess):
return sess.get(url, verify=constants.VERIFY_SSL,
allow_redirects=False)
return sess.get(url, allow_redirects=False)
response = _fetch_with_autorenewal(req_fn)
soup = bs.BeautifulSoup(response.text, 'html.parser')
return (response, soup)