Compare commits

..

11 Commits

Author SHA1 Message Date
a97d52bc59 update user agent
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 47s
2025-01-10 09:31:04 +05:00
80eb74c65c add jq to keep alive
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 1m20s
2025-01-10 08:24:12 +05:00
470df69c56 auto build
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 1m50s
2025-01-10 08:19:47 +05:00
c2ab2ea996 add the cookie server script to docker.... 2025-01-10 08:18:24 +05:00
37dfe7e261 update docs 2025-01-10 08:10:59 +05:00
a72f22b9a4 build cookie server 2025-01-10 07:59:41 +05:00
0e60f139ef added support for cookie server 2025-01-10 07:52:09 +05:00
618fc9371d no more cookie hunting, totp generated automatically 2025-01-10 07:38:28 +05:00
9decf2ed2b no more cookie hunting 2025-01-10 07:37:41 +05:00
ee57150807 Add support for profile selection 2025-01-10 07:36:06 +05:00
1264d92a04 cookie api works, but need to select profile 2025-01-10 06:35:58 +05:00
14 changed files with 218 additions and 37 deletions

View File

@@ -9,3 +9,8 @@ services:
context: ../../ context: ../../
dockerfile: .build/prod/api.Dockerfile dockerfile: .build/prod/api.Dockerfile
image: git.shihaam.dev/shihaam/mib-payment-verify/api image: git.shihaam.dev/shihaam/mib-payment-verify/api
cookieserver:
build:
context: ../../
dockerfile: .build/prod/cookieserver.Dockerfile
image: git.shihaam.dev/shihaam/mib-payment-verify/cookieserver

View File

@@ -0,0 +1,10 @@
FROM python:3.9.20-slim-bookworm
RUN pip install beautifulsoup4 pyotp Flask python-dotenv requests
RUN apt-get update && apt-get install -y curl jq && apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY getcookie.py .
CMD python getcookie.py

View File

@@ -1,6 +1,6 @@
FROM debian:bullseye-slim FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y curl && apt-get clean && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y curl jq && apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
COPY keepalive.sh . COPY keepalive.sh .

View File

@@ -1,14 +1,13 @@
USERNAME= USERNAME=
PASSWORD= PASSWORD=
TOTP_SEED= TOTP_SEED=
## username, password and totp_seed is not unused for now, please use cookie instead
#Cookie
QL_0=
IBSID=
PROFILE_ID=
PROFILE_TYPE=
ACCOUNT_NUMBER= ACCOUNT_NUMBER=
COOKIE_SERVER=http://cookieserver:5000
APP_DEBUG=true APP_DEBUG=true
## This valu is for time difference between request json and actual time of the transacation (+ or -) in mins, 5 or 10 is probably a good value ## This valu is for time difference between request json and actual time of the transacation (+ or -) in mins, 5 or 10 is probably a good value

View File

@@ -0,0 +1,37 @@
name: Build and Push Docker Images
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
jobs:
docker:
name: Build and Push Docker Images
runs-on: builder
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Build Docker images
working-directory: .build/prod
run: docker compose build
- name: Login to Docker Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ vars.DOCKER_REGISTRY_URL }}
username: ${{ vars.DOCKER_REGISTRY_USER }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
- name: Push Docker images
if: github.event_name != 'pull_request'
working-directory: .build/prod
run: docker compose push

View File

@@ -8,15 +8,20 @@
## How to deploy/get started (with docker/podman) ## How to deploy/get started (with docker/podman)
1. Login to MIB web [https://faisanet.mib.com.mv/](https://faisanet.mib.com.mv/) and get the cookie: 1. Login to MIB web [https://faisanet.mib.com.mv/](https://faisanet.mib.com.mv/) and get the your profile id and type:
![get_cookie_env.jpg](get_cookie_env.jpg) ![select_profile.jpg](select_profile.jpg)
2. copy .env.example to .env and fill `QL_0` and `IBSID` with contents from step 1, Make sure to fill `ACCOUNT_NUMBER` with your account number. 2. copy .env.example to .env and fill your MIB Username, Password and TOTP Seed, `PROFILE_ID` and `PROFILE_TYPE` needs to be filled with contents from step 1, Make sure to fill `ACCOUNT_NUMBER` with your account number.
3. Create `compose.yml` with the follwing contents: 3. Create `compose.yml` with the follwing contents:
```yaml ```yaml
services: services:
keepalive: keepalive:
env_file: .env env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/keepalive image: git.shihaam.dev/shihaam/mib-payment-verify/keepalive
cookieserver:
env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/cookieserver
api: api:
env_file: .env env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/api image: git.shihaam.dev/shihaam/mib-payment-verify/api
@@ -24,7 +29,7 @@ services:
- 5000:5000 - 5000:5000
``` ```
3. Run `docker compose up -d` and that is all. 4. Run `docker compose up -d` and that is all.
## Using the API with curl examples: ## Using the API with curl examples:

View File

@@ -2,6 +2,9 @@ services:
keepalive: keepalive:
env_file: .env env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/keepalive image: git.shihaam.dev/shihaam/mib-payment-verify/keepalive
cookieserver:
env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/cookieserver
api: api:
env_file: .env env_file: .env
image: git.shihaam.dev/shihaam/mib-payment-verify/api image: git.shihaam.dev/shihaam/mib-payment-verify/api

View File

@@ -4,6 +4,10 @@ source .env 2> /dev/null
accountNo=$1 accountNo=$1
COOKIE=$(curl -s $COOKIE_SERVER/getcookie)
IBSID=$(echo $COOKIE | jq -r .IBSID)
QL_0=$(echo $COOKIE | jq -r .ql_0)
curl -s 'https://faisanet.mib.com.mv/ajaxBeneficiary/getAccountName' \ curl -s 'https://faisanet.mib.com.mv/ajaxBeneficiary/getAccountName' \
-H "cookie: ql_0=${QL_0}; IBSID=${IBSID}" \ -H "cookie: ql_0=${QL_0}; IBSID=${IBSID}" \
--data-raw "accountNo=${accountNo}" --data-raw "accountNo=${accountNo}"

View File

@@ -1,6 +0,0 @@
import pyotp
import sys
seed = sys.argv[1]
totp = pyotp.TOTP(seed)
print(totp.now())

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -1,29 +1,42 @@
from flask import Flask, jsonify, request
import os import os
import pyotp import pyotp
import time
from urllib.parse import urlencode from urllib.parse import urlencode
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import requests import requests
from dotenv import load_dotenv from dotenv import load_dotenv
import json
class MIBLogin: class MIBLogin:
def __init__(self): def __init__(self):
self.session = requests.Session()
self.base_url = "https://faisanet.mib.com.mv" self.base_url = "https://faisanet.mib.com.mv"
self.headers = { self.headers = {
"User-Agent": "Mozilla/5.0 (give-api) ismath-owes-me-real-api/give-real-api AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36", "User-Agent": "Mozilla/5.0 (ismath-said-will) give-real-api/so-i-wont-have-to-do-this AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36",
"Accept": "*/*" "Accept": "*/*"
} }
self.last_cookie = None
self.last_renewal_time = 0
self.create_new_session()
def create_new_session(self):
self.session = requests.Session()
def get_login_page(self): def get_login_page(self):
response = self.session.get(f"{self.base_url}/auth", headers=self.headers) response = self.session.get(f"{self.base_url}/auth", headers=self.headers)
soup = BeautifulSoup(response.text, 'html.parser') soup = BeautifulSoup(response.text, 'html.parser')
return soup.find('input', {'name': 'rTag'})['value'] rtag_input = soup.find('input', {'name': 'rTag'})
if not rtag_input:
raise ValueError("Failed to find rTag input field in login page.")
return rtag_input['value']
def get_auth_type(self, rtag, username, retain=1): def get_auth_type(self, rtag, username, retain=1):
data = {'rTag': rtag, 'pgf01': username, 'retain': retain} data = {'rTag': rtag, 'pgf01': username, 'retain': retain}
headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'} headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'}
response = self.session.post(f"{self.base_url}/aAuth/getAuthType", headers=headers, data=urlencode(data)) response = self.session.post(
f"{self.base_url}/aAuth/getAuthType",
headers=headers,
data=urlencode(data)
)
return response.json() return response.json()
def login(self, username, password, retain=1): def login(self, username, password, retain=1):
@@ -32,7 +45,11 @@ class MIBLogin:
data = {'rTag': rtag, 'pgf01': username, 'pgf02': password, 'retain': retain} data = {'rTag': rtag, 'pgf01': username, 'pgf02': password, 'retain': retain}
headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'} headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'}
self.session.post(f"{self.base_url}/aAuth", headers=headers, data=urlencode(data)) self.session.post(
f"{self.base_url}/aAuth",
headers=headers,
data=urlencode(data)
)
def auth_2fa(self, totp_seed): def auth_2fa(self, totp_seed):
self.session.get(f"{self.base_url}/auth2FA", headers=self.headers) self.session.get(f"{self.base_url}/auth2FA", headers=self.headers)
@@ -40,18 +57,102 @@ class MIBLogin:
data = {'otpType': 3, 'otp': totp.now()} data = {'otpType': 3, 'otp': totp.now()}
headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'} headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'}
self.session.post(f"{self.base_url}/aAuth2FA/verifyOTP", headers=headers, data=urlencode(data)) self.session.post(
f"{self.base_url}/aAuth2FA/verifyOTP",
headers=headers,
data=urlencode(data)
)
return {cookie.name: cookie.value for cookie in self.session.cookies if cookie.name in ['IBSID', 'ql_0']} cookies = {
cookie.name: cookie.value
for cookie in self.session.cookies
if cookie.name in ['IBSID', 'ql_0']
}
self.last_cookie = cookies
self.last_renewal_time = time.time()
return cookies
def main(): def get_profile_rtag(self):
load_dotenv() url = f"{self.base_url}/profiles"
mib = MIBLogin() response = self.session.get(url, headers=self.headers)
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
profile_card = soup.find('div', {'class': 'card profile-card smooth smooth-shadow'})
if profile_card:
return profile_card.get('data-rt')
else:
raise ValueError("Failed to fetch profile rTag.")
else:
raise ValueError(f"Failed to retrieve profiles page. Status code: {response.status_code}")
def switch_profile(self, profile_id, profile_type):
rtag = self.get_profile_rtag()
url = f"{self.base_url}/aProfileHandler/switchProfile"
data = {'rTag': rtag, 'profileId': profile_id, 'profileType': profile_type}
headers = {**self.headers, 'Content-Type': 'application/x-www-form-urlencoded'}
response = self.session.post(url, headers=headers, data=urlencode(data))
if response.status_code == 200:
result = response.json()
if result.get("success"):
print("Profile switched successfully.")
else:
raise ValueError("Failed to switch profile: " + result.get("reasonText", "Unknown error"))
else:
raise ValueError(f"Profile switch failed with status code: {response.status_code}")
def clear_cookie(self):
self.last_cookie = None
self.create_new_session()
# Initialize Flask app
app = Flask(__name__)
mib = MIBLogin()
load_dotenv()
def renew_cookie_internal():
try:
print(f"Starting cookie renewal process...")
mib.clear_cookie()
mib.login(os.getenv('USERNAME'), os.getenv('PASSWORD')) mib.login(os.getenv('USERNAME'), os.getenv('PASSWORD'))
cookies = mib.auth_2fa(os.getenv('TOTP_SEED')) cookies = mib.auth_2fa(os.getenv('TOTP_SEED'))
# Print the cookies in JSON format # Profile selection after successful login and 2FA
print(json.dumps(cookies, indent=4)) mib.switch_profile(os.getenv('PROFILE_ID'), os.getenv('PROFILE_TYPE'))
print(f"Cookie renewal and profile switch completed successfully")
return cookies
except Exception as e:
print(f"Error during cookie renewal: {str(e)}")
return {"error": str(e)}
if __name__ == "__main__": @app.route('/getcookie', methods=['GET'])
main() def get_cookie():
client_ip = request.remote_addr
print(f"Providing cookie to: {client_ip}")
if mib.last_cookie:
return jsonify(mib.last_cookie)
else:
cookies = renew_cookie_internal()
if "error" in cookies:
return jsonify(cookies), 500
return jsonify(cookies)
@app.route('/newcookie', methods=['GET'])
def new_cookie():
client_ip = request.remote_addr
current_time = time.time()
# Check if less than 60 seconds since last renewal
if current_time - mib.last_renewal_time < 60:
print(f"Providing existing cookie to {client_ip} (last renewal was {int(current_time - mib.last_renewal_time)} seconds ago)")
return jsonify(mib.last_cookie)
print(f"Renewing cookie as requested by: {client_ip}")
cookies = renew_cookie_internal()
if "error" in cookies:
return jsonify(cookies), 500
return jsonify(cookies)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

View File

@@ -1,15 +1,34 @@
#!/bin/bash #!/bin/bash
request_new_cookie(){
curl -s $COOKIE_SERVER/newcookie > /dev/null
}
while true; do while true; do
source .env 2> /dev/null source .env 2> /dev/null
# Get current date and time # Get current date and time
CURRENT_TIME=$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ') CURRENT_TIME=$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')
COOKIE=$(curl -s $COOKIE_SERVER/getcookie)
IBSID=$(echo $COOKIE | jq -r .IBSID)
QL_0=$(echo $COOKIE | jq -r .ql_0)
# Make the keep-alive request # Make the keep-alive request
KEEP_ALIVE=$(curl -s 'https://faisanet.mib.com.mv/aProfile/keepAlive' \ KEEP_ALIVE=$(curl -s 'https://faisanet.mib.com.mv/aProfile/keepAlive' \
-X 'POST' \ -X 'POST' \
-H "cookie: ql_0=${QL_0}; IBSID=${IBSID}") -H "cookie: ql_0=${QL_0}; IBSID=${IBSID}")
# Check for session expiration
SUCCESS=$(echo "$KEEP_ALIVE" | jq -r .success)
RESPONSE_CODE=$(echo "$KEEP_ALIVE" | jq -r .responseCode)
if [[ "$SUCCESS" == "false" && "$RESPONSE_CODE" == "3" ]]; then
echo "Session expired. Requesting new cookie..."
request_new_cookie
continue # Restart the loop
fi
# Remove the last closing brace from KEEP_ALIVE # Remove the last closing brace from KEEP_ALIVE
KEEP_ALIVE_MODIFIED=${KEEP_ALIVE%?} KEEP_ALIVE_MODIFIED=${KEEP_ALIVE%?}

BIN
select_profile.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

4
tx.sh
View File

@@ -2,6 +2,10 @@
source .env 2> /dev/null source .env 2> /dev/null
COOKIE=$(curl -s $COOKIE_SERVER/getcookie)
IBSID=$(echo $COOKIE | jq -r .IBSID)
QL_0=$(echo $COOKIE | jq -r .ql_0)
curl 'https://faisanet.mib.com.mv/ajaxAccounts/trxHistory' \ curl 'https://faisanet.mib.com.mv/ajaxAccounts/trxHistory' \
-H "cookie: ql_0=${QL_0}; IBSID=${IBSID}" \ -H "cookie: ql_0=${QL_0}; IBSID=${IBSID}" \
--data-raw "accountNo=${ACCOUNT_NUMBER}&trxNo=&trxType=0&sortTrx=date&sortDir=desc&fromDate=&toDate=&start=1&end=20&includeCount=1" -s \ --data-raw "accountNo=${ACCOUNT_NUMBER}&trxNo=&trxType=0&sortTrx=date&sortDir=desc&fromDate=&toDate=&start=1&end=20&includeCount=1" -s \