First Commit

This commit is contained in:
Wong Yiek Heng 2025-10-04 10:16:41 +08:00
commit 0d7f152317
17 changed files with 3153 additions and 0 deletions

55
.dockerignore Normal file
View File

@ -0,0 +1,55 @@
# Git
.git
.gitignore
# Python
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
.mypy_cache
.pytest_cache
.hypothesis
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDE
.vscode
.idea
*.swp
*.swo
*~
# Docker
Dockerfile*
docker-compose*.yml
.dockerignore
# Documentation
README.md
*.md
# Other
.env
.env.local
.env.*.local

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__
.DS_Store

20
Dockerfile.api Normal file
View File

@ -0,0 +1,20 @@
FROM python:3.9-slim
WORKDIR /app
RUN apt-get update
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application files
COPY . .
# Set environment variables
ENV PYTHONUNBUFFERED=1
# Expose port
EXPOSE 3000
# Run the API server
CMD ["python", "cm_api.py"]

17
Dockerfile.telegram Normal file
View File

@ -0,0 +1,17 @@
FROM python:3.9-slim
WORKDIR /app
RUN apt-get update
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application files
COPY . .
# Set environment variables
ENV PYTHONUNBUFFERED=1
# Run the telegram bot
CMD ["python", "cm_telegram.py"]

17
Dockerfile.transfer Normal file
View File

@ -0,0 +1,17 @@
FROM python:3.9-slim
WORKDIR /app
RUN apt-get update
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application files
COPY . .
# Set environment variables
ENV PYTHONUNBUFFERED=1
# Run the API server
CMD ["python", "cm_transfer_credit.py"]

20
Dockerfile.web Normal file
View File

@ -0,0 +1,20 @@
FROM python:3.9-slim
WORKDIR /app
RUN apt-get update
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application files
COPY . .
# Set environment variables
ENV PYTHONUNBUFFERED=1
# Expose port
EXPOSE 8000
# Run the web view
CMD ["python", "cm_web_view.py"]

191
cm_api.py Normal file
View File

@ -0,0 +1,191 @@
import threading
from flask import Flask, jsonify, request
from flask_cors import CORS
from db import DB
class CM_API:
def __init__(self):
self.app = Flask(__name__)
CORS(self.app)
self._register_routes()
def _get_database_connection(self):
"""Create a new database connection for use"""
try:
db = DB()
return db
except Exception as e:
print(f"Database connection failed: {e}")
return None
def _close_database_connection(self, db):
"""Close database connection if it exists"""
if db is not None:
try:
# Assuming DB class has a close method or similar cleanup
if hasattr(db, 'close'):
db.close()
elif hasattr(db, 'connection') and hasattr(db.connection, 'close'):
db.connection.close()
except Exception as e:
print(f"Error closing database connection: {e}")
def _register_routes(self):
# Account routes
self.app.route('/acc/<username>', methods=['GET'])(self.get_account)
self.app.route('/acc/', methods=['GET'])(self.get_account)
# User routes
self.app.route('/user/<username>', methods=['GET'])(self.get_user)
self.app.route('/user/', methods=['GET'])(self.get_user)
# Update routes
self.app.route('/update-acc-data', methods=['POST'])(self.update_acc_data)
self.app.route('/update-user-data', methods=['POST'])(self.update_user_data)
def _check_database_available(self):
db = self._get_database_connection()
if db is None:
return False, None, ("Database not available", 500)
return True, db, None
def _handle_error(self, error, message="An error occurred"):
print(f"Error: {error}")
return message, 500
def get_account(self, username=None):
is_available, db, error_response = self._check_database_available()
if not is_available:
return error_response
try:
if username:
query = "SELECT username, password, status, link FROM acc WHERE username = %s"
query_params = [username]
else:
query = "SELECT username, password, status, link FROM acc"
query_params = []
results = db.query(query, query_params)
return jsonify(results)
except Exception as error:
return self._handle_error(error, "Not Found"), 404
finally:
self._close_database_connection(db)
def get_user(self, username=None):
is_available, db, error_response = self._check_database_available()
if not is_available:
return error_response
try:
if username:
query = "SELECT f_username, f_password, t_username, t_password FROM user WHERE f_username = %s"
query_params = [username]
else:
query = "SELECT f_username, f_password, t_username, t_password, last_update_time FROM user"
query_params = []
results = db.query(query, query_params)
return jsonify(results)
except Exception as error:
return self._handle_error(error, "Not Found"), 404
finally:
self._close_database_connection(db)
def update_acc_data(self):
is_available, db, error_response = self._check_database_available()
if not is_available:
return error_response
try:
data = request.get_json()
username = data.get('username')
password = data.get('password')
status = data.get('status')
link = data.get('link')
if not username:
return jsonify({"error": "Username is required"})
result = db.execute(
"UPDATE acc SET password = %s, status = %s, link = %s WHERE username = %s",
[password, status, link, username]
)
if result:
return jsonify("Data updated successfully")
else:
return jsonify("Error updating data")
except Exception as error:
return self._handle_error(error, "Error updating data"), 500
finally:
self._close_database_connection(db)
def update_user_data(self):
is_available, db, error_response = self._check_database_available()
if not is_available:
return error_response
try:
data = request.get_json()
f_username = data.get('f_username')
f_password = data.get('f_password')
t_username = data.get('t_username')
t_password = data.get('t_password')
if not f_username:
return jsonify({"error": "f_username is required"})
result = db.execute(
"UPDATE user SET f_password = %s, t_password = %s, t_username = %s, last_update_time = CURRENT_TIMESTAMP WHERE f_username = %s",
[f_password, t_password, t_username, f_username]
)
if result:
return jsonify("Data updated successfully")
else:
return jsonify("Error updating data")
except Exception as error:
return self._handle_error(error, "Error updating data")
finally:
self._close_database_connection(db)
def run(self, port=3000, debug=True):
# Test database connection before starting server
test_db = self._get_database_connection()
if test_db is None:
print("Cannot start server: Database not available")
exit(1)
self._close_database_connection(test_db)
print(f'CM Bot DB API Listening at Port : {port}')
self.app.run(host='0.0.0.0', port=port, debug=debug)
def run_in_thread(self, port=3000, debug=False):
"""Run the Flask app in a separate thread"""
# Test database connection before starting server
test_db = self._get_database_connection()
if test_db is None:
print("Cannot start server: Database not available")
return None
self._close_database_connection(test_db)
def run_app():
print(f'CM Bot DB API Listening at Port : {port}')
self.app.run(host='0.0.0.0', port=port, debug=debug, use_reloader=False)
thread = threading.Thread(target=run_app, daemon=True)
thread.start()
return thread
if __name__ == '__main__':
api = CM_API()
api.run(port = 3000)

499
cm_bot.py Normal file
View File

@ -0,0 +1,499 @@
import requests, re
from bs4 import BeautifulSoup
# with open('security_response.html', 'wb') as f:
# f.write(response.content)
class CM_BOT:
def __init__(self):
self.session = requests.Session()
self.base_url = 'https://cm99.net'
self.is_logged_in = False
self._setup_headers()
def _setup_headers(self):
"""Set up default headers for requests."""
self.login_headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'identity',
'accept-language': 'en-GB,en;q=0.8',
'cache-control': 'max-age=0',
'content-type': 'application/x-www-form-urlencoded',
'origin': self.base_url,
'referer': f'{self.base_url}/cm/login',
'sec-ch-ua': '"Not;A=Brand";v="99", "Brave";v="139", "Chromium";v="139"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'same-origin',
'sec-fetch-user': '?1',
'sec-gpc': '1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36'
}
self.get_user_tree_headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'identity',
'accept-language': 'en-GB,en;q=0.8',
'priority': 'u=0, i',
'sec-ch-ua': '"Not;A=Brand";v="99", "Brave";v="139", "Chromium";v="139"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'none',
'sec-fetch-user': '?1',
'sec-gpc': '1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36'
}
self.get_register_form_headers = {
'accept': 'text/html, */*; q=0.01',
'accept-encoding': 'identity',
'accept-language': 'en-GB,en;q=0.8',
'content-length': '0',
'origin': self.base_url,
'priority': 'u=1, i',
'referer': f'{self.base_url}/cm/userMainAction',
'sec-ch-ua': '"Not;A=Brand";v="99", "Brave";v="139", "Chromium";v="139"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'sec-gpc': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
'x-requested-with': 'XMLHttpRequest'
}
self.get_user_credit_headers = {
'accept': 'text/html, */*; q=0.01',
'accept-encoding': 'identity',
'accept-language': 'en-GB,en;q=0.7',
'content-length': '0',
'origin': self.base_url,
'priority': 'u=1, i',
'referer': f'{self.base_url}/cm/mainMenu',
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Brave";v="140"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'sec-gpc': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
'x-requested-with': 'XMLHttpRequest'
}
# self.change_pass_headers = {
# 'accept': '*/*',
# 'accept-encoding': 'identity',
# 'accept-language': 'en-GB,en;q=0.8',
# 'content-length': '889',
# 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
# 'origin': self.base_url,
# 'priority': 'u=1, i',
# 'referer': f'{self.base_url}/cm/userMainAction',
# 'sec-ch-ua': '"Not;A=Brand";v="99", "Brave";v="139", "Chromium";v="139"',
# 'sec-ch-ua-mobile': '?0',
# 'sec-ch-ua-platform': '"macOS"',
# 'sec-fetch-dest': 'empty',
# 'sec-fetch-mode': 'cors',
# 'sec-fetch-site': 'same-origin',
# 'sec-gpc': '1',
# 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
# 'x-requested-with': 'XMLHttpRequest'
# }
self.register_form_headers = {
'accept': '*/*',
'accept-encoding': 'identity',
'accept-language': 'en-GB,en;q=0.8',
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
'origin': self.base_url,
'priority': 'u=1, i',
'referer': f'{self.base_url}/cm/userMainAction',
'sec-ch-ua': '"Not;A=Brand";v="99", "Brave";v="139", "Chromium";v="139"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'sec-gpc': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
'x-requested-with': 'XMLHttpRequest'
}
self.set_security_pin_headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'identity',
'accept-language': 'en-GB,en;q=0.9',
'cache-control': 'max-age=0',
'content-length': '103',
'content-type': 'application/x-www-form-urlencoded',
'origin': self.base_url,
'priority': 'u=0, i',
'referer': f'{self.base_url}/cm/setSecurityPin',
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Brave";v="140"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'same-origin',
'sec-fetch-user': '?1',
'sec-gpc': '1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'
}
self.transfer_search_headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'identity',
'accept-language': 'en-GB,en;q=0.7',
'cache-control': 'max-age=0',
'content-length': '78',
'content-type': 'application/x-www-form-urlencoded',
'origin': self.base_url,
'priority': 'u=0, i',
'referer': f'{self.base_url}/cm/transfer',
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Brave";v="140"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'same-origin',
'sec-fetch-user': '?1',
'sec-gpc': '1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'
}
self.transfer_credit_headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'identity',
'accept-language': 'en-GB,en;q=0.7',
'cache-control': 'max-age=0',
'content-length': '152',
'content-type': 'application/x-www-form-urlencoded',
'origin': self.base_url,
'priority': 'u=0, i',
'referer': f'{self.base_url}/cm/searchTransferUser',
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Brave";v="140"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'same-origin',
'sec-fetch-user': '?1',
'sec-gpc': '1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'
}
def get_register_data(self, token: str, username: str, password: str):
return {
'struts.token.name': 'token',
'token': f'{token}',
'userIsNew': 'true',
'searchContent': '',
'user.username': f'{username}',
'user.companyId': '1',
'user.userRoleId': '4',
'user.password': f'{password}',
'user.confirmPassword': f'{password}',
'user.name': f'{username}',
'user.mobileNumber': '',
'user.email': '',
'user.remarks': '',
'user.accountStatus': 'A',
'user.currencyMYR': 'true',
'__checkbox_user.currencyMYR': 'true',
'user.allowBetHl': 'true',
'__checkbox_user.allowBetHl': 'true',
'user.commission3d4d': '5.00',
'user.commission5d6d': '5.00',
'user.commissionHL': '19.00',
'user.commissionHL6d': '19.00',
'user.commissionNL': '19.00',
'user.commissionNL6d': '19.00',
'checkAllPrizePackages': 'on',
'selectedPrizePackageList': ['6', '1', '5', '3'],
'__multiselect_selectedPrizePackageList': '',
'checkAllPrizePackages5D6D': 'on',
'selectedPrizePackage5D6DList': '2',
'__multiselect_selectedPrizePackage5D6DList': ''
}
def get_security_pin_data(self, token: str, security_pin: str):
return {
'struts.token.name': 'token',
'token': token,
'newPin': security_pin,
'confirmNewPin': security_pin
}
def get_transfer_search_data(self, token: str, username: str):
return {
'struts.token.name': 'token',
'token': token,
'username': username
}
def get_transfer_data(self, token: str, username: str, name: str, toUserId: str, amount: float, security_pin: str):
return {
'struts.token.name': 'token',
'token': token,
'username': username,
'name': name,
'toUserId': toUserId,
'amount': amount,
'securityPin': security_pin
}
# def get_change_pass_data(self, token: str, user_encrypted_id: str, username: str, password: str):
# return {
# 'struts.token.name': 'token',
# 'token': f'{token}',
# 'searchContent': '',
# 'userIsNew': 'false',
# 'user.encryptedId': f'{user_encrypted_id}',
# 'user.username': f'{username}',
# 'user.parentId': '31308',
# 'user.companyId': '1',
# 'user.userRoleId': '4',
# 'user.password': f'{password}',
# 'user.confirmPassword': f'{password}',
# 'user.name': f'{username}',
# 'user.mobileNumber': '',
# 'user.email': '',
# 'user.remarks': '',
# 'user.accountStatus': 'A',
# 'user.currencyMYR': 'true',
# '__checkbox_user.currencyMYR': 'true',
# 'user.allowBetHl': 'true',
# '__checkbox_user.allowBetHl': 'true',
# 'user.balance': '1',
# 'user.outstanding': '1',
# 'user.commission3d4d': '5.00',
# 'user.commission5d6d': '5.00',
# 'user.commissionHL': '19.00',
# 'user.commissionHL6d': '19.00',
# 'user.commissionNL': '19.00',
# 'user.commissionNL6d': '19.00',
# 'selectedPrizePackageList': ['6', '1', '5', '3'],
# '__multiselect_selectedPrizePackageList': '',
# 'selectedPrizePackage5D6DList': '2',
# '__multiselect_selectedPrizePackage5D6DList': ''
# }
def login(self, username: str, password: str):
try:
print("Starting login process...")
login_page = self.session.get(f'{self.base_url}/cm/login')
print(f"Login page status: {login_page.status_code}")
login_data = {
'j_username': username,
'j_password': password
}
login_response = self.session.post(
f'{self.base_url}/cm/j_security_check',
data=login_data,
headers=self.login_headers,
allow_redirects=True
)
if login_response.status_code == 200 and 'login' not in login_response.url.lower():
print("Login successful!")
self.is_logged_in = True
return True
else:
print("Login failed!")
self.is_logged_in = False
return False
except requests.exceptions.RequestException as e:
print(f"Error during login: {e}")
self.is_logged_in = False
return False
def get_max_user_in_pattern(self, prefix_pattern: str = '13c'):
response = self.session.get(f'{self.base_url}/cm/json/generateUserTree?id=0')
regex = f'\\[{prefix_pattern}\\d+]'
matches = re.findall(regex, response.text)
last_match_text = matches[-1]
number_part = last_match_text.replace(f"[{prefix_pattern}", "").replace("]", "")
last_match = int(number_part)
return last_match
def get_register_form_token(self):
try:
response = self.session.post(
f'{self.base_url}/cm/loadUserAccount',
headers=self.get_register_form_headers
)
soup = BeautifulSoup(response.content, 'html.parser')
return soup.find('input', {'name' : "token"})['value']
except requests.exceptions.RequestException as e:
print(f"Error getting register form: {e}")
return None
def get_security_pin_form_token(self):
response = self.session.get(f'{self.base_url}/cm/setSecurityPin')
soup = BeautifulSoup(response.content, 'html.parser')
return soup.find('input', {'name' : "token"})['value']
def register_user(self, user_id, user_password):
try:
print("Creating user account...")
token = self.get_register_form_token()
print(f"Token: {token}")
user_data = self.get_register_data(token, user_id, user_password)
print(user_data)
response = self.session.post(
f'{self.base_url}/cm/saveUserAccount',
data=user_data,
headers=self.register_form_headers
)
if re.search('User created successfully', response.text):
print(f"User account: {user_id} password: {user_password} creation completed!")
else:
print(f"User account: {user_id} creation FAIL!")
except requests.exceptions.RequestException as e:
print(f"Error creating user account: {e}")
return None
# def change_user_password(self, user_id: str, new_user_pass: str):
# try:
# print(f"Changing user: {user_id} password...")
# self.login(self.username, self.password)
# token = self.get_register_form_token()
# headers = self.change_pass_headers
# user_data = self.get_change_pass_data(token, )
# response = self.session.post(
# f'{self.base_url}/cm/saveUserAccount',
# data=user_data,
# headrs=headers
# )
# except requests.exceptions.RequestException as e:
# print(f"Error change user password: {e}")
# return None
def get_register_link(self):
response = self.session.get(f"{self.base_url}/cm/showQrCode")
soup = BeautifulSoup(response.content, 'html.parser')
soup = soup.find('form', {'id': 'qrCodeForm'})
return soup.find('a')['href']
def get_generate_username(self, max_username_index: int):
max_username_index += 1
if max_username_index % 10 == 4:
max_username_index += 1
return max_username_index
def set_security_pin(self, security_pin: str):
token = self.get_security_pin_form_token()
security_data = self.get_security_pin_data(token, security_pin)
response = self.session.post(
f'{self.base_url}/cm/saveSecurityPin',
data=security_data,
headers=self.set_security_pin_headers
)
def transfer_credit(self, t_username: str, t_password: str, amount: float):
token = self.get_transfer_token()
transfer_search_data = self.get_transfer_search_data(token, t_username)
response = self.session.post(
f'{self.base_url}/cm/searchTransferUser',
data=transfer_search_data,
headers=self.transfer_search_headers
)
soup = BeautifulSoup(response.content, 'html.parser')
name = soup.find('input', {'id': "name"})['value']
token = soup.find('input', {'name': "token"})['value']
toUserId = soup.find('input', {'id': "toUserId"})['value']
transfer_data = self.get_transfer_data(token, t_username, name, toUserId, amount, t_password)
response = self.session.post(
f'{self.base_url}/cm/saveTransfer',
data=transfer_data,
headers=self.transfer_credit_headers
)
# with open('transfer_credit.html', 'wb') as f:
# f.write(response.content)
return True if re.search(r'Successfully saved the record\.', response.text) else False
def get_user_credit(self):
response = self.session.post(
f'{self.base_url}/cm/userProfile',
headers=self.get_user_credit_headers
)
soup = BeautifulSoup(response.content, 'html.parser')
return float(soup.find('table', {'class': 'generalContent'}).find(text=re.compile('Credit Available')).parent.parent.find_all('td')[2].text)
def get_transfer_token(self):
response = self.session.get(f'{self.base_url}/cm/transfer')
soup = BeautifulSoup(response.content, 'html.parser')
return soup.find('input', {'name' : "token"})['value']
def logout(self):
"""Logout from the system."""
self.session.close()
self.is_logged_in = False
print("Logged out successfully.")
def main():
# user_id='testing0001'
# user_pass='Qwer1@34'
# # prefix = '13c'
user_manager = CM_BOT()
user_manager.login(
username = '13c4021',
password = 'vX34wUk'
)
user_manager.transfer_credit('m94', 'Sky533535', 0.01)
# last_username = user_manager.get_max_user_in_pattern(prefix)
# user_id = f'{prefix}{user_manager.get_generate_username(last_username)}'
# user_pass = user_manager.get_random_password()
# print(user_id)
# print(user_pass)
# user_manager.register_user(
# user_id=user_id,
# user_password=user_pass
# )
# user_manager.logout()
# user_manager = CM_BOT(
# username = user_id,
# password = user_pass
# )
# user_manager.login(user_id, user_pass)
# print(user_manager.get_register_link())
# user_manager.login(
# username = user_id,
# password = user_pass
# )
# user_manager.set_security_pin(user_pass)
if __name__ == "__main__":
main()

187
cm_bot_hal.py Normal file
View File

@ -0,0 +1,187 @@
import requests, re
from bs4 import BeautifulSoup
from cm_bot import CM_BOT
from db import DB
import secrets, string
class CM_BOT_HAL:
def __init__(self):
self.db = DB()
self.prefix = '13c'
self.agent_id = 'cm13a3'
self.agent_password = 'Sky533535'
self.security_pin = 'Sky533535'
def get_random_password(self):
length = 8
lower_case = string.ascii_lowercase
upper_case = string.ascii_uppercase
digits = string.digits
all_characters = lower_case + upper_case + digits
password_list = [
secrets.choice(lower_case),
secrets.choice(upper_case),
secrets.choice(digits),
secrets.choice(digits),
secrets.choice(digits)
]
while len(password_list) < length:
password_list.append(secrets.choice(all_characters))
secrets.SystemRandom().shuffle(password_list)
return "".join(password_list)
def get_user_data_from_acc(self):
query = "SELECT username, password, link FROM acc WHERE status = %s ORDER BY username ASC LIMIT 1"
query_params = ['']
result = self.db.query(query, query_params)
return result[0] if len(result) else None
def get_max_username(self, prefix: str):
query = "SELECT username FROM acc WHERE username LIKE %s ORDER BY username DESC LIMIT 1"
query_params = [f'{prefix}%']
result = self.db.query(query, query_params)
return result[0]['username']
def get_next_username(self, prefix: str):
last_index = int(self.get_max_username(prefix).replace(prefix, ''))
next_index = last_index + 2 if last_index % 10 == 3 else last_index + 1
return f'{prefix}{next_index}'
def insert_user_to_table_acc(self, user):
query = "INSERT INTO acc (username, password, link) VALUES (%s, %s, %s)"
query_params = [user['username'], user['password'], user['link']]
return self.db.execute(query, query_params)
def insert_user_to_table_user(self, user):
query = "INSERT INTO user (f_username, f_password, t_username, t_password) VALUES (%s, %s, %s, %s)"
query_params = [user['f_username'], user['f_password'], user['t_username'], user['t_password']]
return self.db.execute(query, query_params)
def create_new_acc(self):
cm_bot = CM_BOT()
username = self.get_next_username(self.prefix)
password = self.get_random_password()
cm_bot.login(
username = self.agent_id,
password = self.agent_password
)
cm_bot.register_user(
user_id = username,
user_password = password
)
cm_bot.logout()
cm_bot = CM_BOT()
cm_bot.login(
username = username,
password = password
)
link = cm_bot.get_register_link()
cm_bot.logout()
user = {'username': username, 'password': password, 'link': link}
self.insert_user_to_table_acc(user)
return user
def update_user_status_to_wait(self, username: str):
query = "UPDATE acc SET status = 'wait' WHERE username = %s"
query_params = [username]
return self.db.execute(query, query_params)
def update_user_status_to_done(self, username: str):
query = "UPDATE acc SET status = 'done' WHERE username = %s"
query_params = [username]
return self.db.execute(query, query_params)
def get_user_api(self):
user = self.get_user_data_from_acc()
if user is None:
user = self.create_new_acc()
self.update_user_status_to_wait(user['username'])
return user
def is_whatsapp_url(self, text):
url_pattern = re.compile(
r'^(https?://)?' # Optional http:// or https://
r'((www\.)|([a-zA-Z0-9-]+\.))+' # www. or subdomain.
# r'[a-zA-Z]{2,6}' # Top-level domain (e.g., com, org, net)
r'(whatsapp.com)'
r'(/[a-zA-Z0-9-._~:/?#\[\]@!$&\'()*+,;=]*)?$' # Optional path, query, fragment
)
return bool(url_pattern.search(text))
def get_whatsapp_link_username(self, whatsapp_link: str):
if self.is_whatsapp_url(whatsapp_link) == False:
return None
response = requests.get(whatsapp_link)
soup = BeautifulSoup(response.content, 'html.parser')
username = [x.replace('*', '').replace(' ', '') for x in soup.find('h3', {"class": "_9vd5 _9scr"}).text.split("/")]
return username
def get_user_pass_from_acc(self, username: str):
query = "SELECT password FROM acc WHERE username = %s"
query_params = [username]
result = self.db.query(query, query_params)
return result[0]['password']
def set_security_pin_api(self, whatsapp_link: str):
t_username, f_username = self.get_whatsapp_link_username(whatsapp_link)
password = self.get_user_pass_from_acc(f_username)
cm_bot = CM_BOT()
cm_bot.login(
username = f_username,
password = password
)
cm_bot.set_security_pin(self.security_pin)
cm_bot.logout()
result = self.update_user_status_to_done(f_username)
if result == False:
raise Exception('Failed to update user status to done')
result = self.insert_user_to_table_user(
{
'f_username': f_username,
'f_password': password,
't_username': t_username,
't_password': self.security_pin
}
)
if result == False:
raise Exception('Failed to insert user to table user')
def get_user_credit(self, f_username: str, f_password: str):
cm_bot = CM_BOT()
cm_bot.login(
username = f_username,
password = f_password
)
return float(cm_bot.get_user_credit())
def transfer_credit_api(self, f_username: str, f_password: str, t_username: str, t_password: str):
cm_bot = CM_BOT()
cm_bot.login(
username = f_username,
password = f_password
)
amount = cm_bot.get_user_credit() - 0.01
if cm_bot.transfer_credit(t_username, t_password, amount) == True:
print(f'Successfully transfer amount: {amount} from {f_username} to {t_username}')
else:
print(f'Failed to transfer credit from {f_username} to {t_username}')
if __name__ == '__main__':
bot = CM_BOT_HAL()
# bot.transfer_credit_api('13c4070', 'zU2QoL', '4753kit', 'Sky533535')
# print(bot.get_next_username('13c'))
# print(bot.get_random_password())
# bot.get_user_api()
# bot.insert_user_to_table_acc({'username': 'test0001', 'password': 'test0001', 'link': 'test0001'})
# print(bot.get_whatsapp_link_username('https://chat.whatsapp.com/DZDWcicr6MTFrR4kBfnJXO'))
# print(bot.get_user_pass_from_acc('13c4151'))

85
cm_telegram.py Normal file
View File

@ -0,0 +1,85 @@
import logging
from telegram import ForceReply, Update
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters
from cm_bot_hal import CM_BOT_HAL
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
async def menu_cmd_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
menu = [
'MENU',
'/1 - Get Acc',
'/2 <link> - Set Security Pin'
]
await update.message.reply_text('\n'.join(menu))
async def get_acc_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text('Start Getting CM Account ...')
try:
bot = CM_BOT_HAL()
user = bot.get_user_api()
msg = [
f'Username: {user["username"]}',
f'Password: {user["password"]}',
f'Link: {user["link"]}'
]
await update.message.reply_text('\n'.join(msg))
except Exception as e:
await update.message.reply_text(f'Error: {e}')
async def set_security_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if len(context.args) == 0 or len(context.args) > 1:
await update.message.reply_text('CMD is wrong, please check and retry ...')
return
bot = CM_BOT_HAL()
if bot.is_whatsapp_url(context.args[0]) == False:
await update.message.reply_text('Link Format Wrong, please check and retry ...')
return
await update.message.reply_text('Start Setting Security Pin ...')
try:
bot.set_security_pin_api(context.args[0])
del bot
await update.message.reply_text('Security Pin Set Done!')
except Exception as e:
await update.message.reply_text(f'Error: {e}')
async def insert_to_user_table_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
if len(context.args) == 0 or len(context.args) != 4:
await update.message.reply_text('CMD is wrong, please check and retry ...')
return
bot = CM_BOT_HAL()
f_username, f_password, t_username, t_password = context.args
bot.insert_user_to_table_user(
{
'f_username': f_username,
'f_password': f_password,
't_username': t_username,
't_password': t_password
}
)
await update.message.reply_text(f'Done insert {f_username} into user table.')
def main() -> None:
"""Start the bot."""
# application = Application.builder().token("5327571437:AAFlowwnAysTEMx6LtYQNTevGCboKDZoYzY").build()
application = Application.builder().token("5315819168:AAH31xwNgPdnk123x97XalmTW6fQV5EUCFU").build()
application.add_handler(CommandHandler("menu", menu_cmd_handler))
application.add_handler(CommandHandler("1", get_acc_handler))
application.add_handler(CommandHandler("2", set_security_handler))
application.add_handler(CommandHandler("3", insert_to_user_table_handler))
# application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
# Start the Telegram bot
print("Starting Telegram bot...")
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()

37
cm_transfer_credit.py Normal file
View File

@ -0,0 +1,37 @@
from cm_bot_hal import CM_BOT_HAL
import logging, time, requests, json
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from tqdm import tqdm
logging.basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
)
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
api_url = 'https://api.luckytown888.net'
max_threading = 1
def transfer(data: dict):
bot = CM_BOT_HAL()
logger.info(f'[Start] Transfer Credit from {data['f_username']} to {data['t_username']}')
bot.transfer_credit_api(data['f_username'], data['f_password'], data['t_username'], data['t_password'])
logger.info(f'[DONE] {data['f_username']} transfer done!')
del bot
time.sleep(0.5)
while True:
weekday = int(datetime.now().strftime("%w"))
hour = int(datetime.now().strftime("%H"))
minutes = int(datetime.now().strftime("%M"))
if weekday == 3 and (hour >= 21 and hour < 22) and minutes >= 32:
response = requests.get(f'{api_url}/user')
items = json.loads(response.text)
total_items = len(items) if isinstance(items, list) else 0
if total_items == 0:
logger.info("No items to process.")
else:
with ThreadPoolExecutor(max_workers=max_threading) as executor:
list(tqdm(executor.map(transfer, items), total=total_items, desc="Processing data"))
time.sleep(1)

745
cm_web_view.py Normal file
View File

@ -0,0 +1,745 @@
from flask import Flask, render_template_string, request, jsonify
from flask_cors import CORS
import requests
import json
app = Flask(__name__)
CORS(app)
# API base URL - use environment variable for Docker Compose
import os
API_BASE_URL = os.getenv('API_BASE_URL', 'http://localhost:3000')
print("API: ", API_BASE_URL)
# Beautiful HTML template with modern styling
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CM Bot Database Viewer</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 30px;
color: white;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
}
.tabs {
display: flex;
justify-content: center;
margin-bottom: 30px;
}
.tab {
background: rgba(255, 255, 255, 0.1);
border: none;
color: white;
padding: 15px 30px;
margin: 0 10px;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.tab:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
.tab.active {
background: rgba(255, 255, 255, 0.3);
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.content {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
backdrop-filter: blur(10px);
}
.table-container {
overflow-x: auto;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 15px;
overflow: hidden;
}
th {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
text-align: left;
font-weight: 600;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
}
td {
padding: 18px 20px;
border-bottom: 1px solid #f0f0f0;
font-size: 0.95rem;
color: #333;
}
tr:hover {
background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%);
transform: scale(1.01);
transition: all 0.2s ease;
}
tr:last-child td {
border-bottom: none;
}
.status-badge {
padding: 6px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
}
.status-active {
background: #d4edda;
color: #155724;
}
.status-inactive {
background: #f8d7da;
color: #721c24;
}
.editable {
cursor: pointer;
position: relative;
transition: all 0.2s ease;
}
.editable:hover {
background: #e3f2fd !important;
border-radius: 4px;
}
.editable.editing {
background: #fff3e0 !important;
border: 2px solid #ff9800;
border-radius: 4px;
}
.edit-input {
width: 100%;
border: none;
background: transparent;
padding: 4px 8px;
font-size: inherit;
font-family: inherit;
outline: none;
}
.edit-buttons {
display: inline-flex;
gap: 5px;
margin-left: 10px;
}
.edit-btn {
padding: 4px 8px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
transition: all 0.2s ease;
}
.save-btn {
background: #4caf50;
color: white;
}
.save-btn:hover {
background: #45a049;
}
.cancel-btn {
background: #f44336;
color: white;
}
.cancel-btn:hover {
background: #da190b;
}
.edit-icon {
opacity: 0;
transition: opacity 0.2s ease;
margin-left: 5px;
color: #666;
}
.editable:hover .edit-icon {
opacity: 1;
}
.sort-indicator {
margin-left: 5px;
font-size: 0.8rem;
}
.sortable {
cursor: pointer;
user-select: none;
}
.sortable:hover {
background: rgba(255, 255, 255, 0.1);
}
.loading {
text-align: center;
padding: 50px;
color: #666;
}
.loading i {
font-size: 2rem;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error {
background: #f8d7da;
color: #721c24;
padding: 20px;
border-radius: 10px;
text-align: center;
margin: 20px 0;
}
.refresh-btn {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
color: white;
border: none;
padding: 12px 25px;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
margin-bottom: 20px;
transition: all 0.3s ease;
}
.refresh-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 25px;
border-radius: 15px;
text-align: center;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.stat-card h3 {
font-size: 2rem;
margin-bottom: 10px;
}
.stat-card p {
opacity: 0.9;
font-size: 1rem;
}
@media (max-width: 768px) {
.header h1 {
font-size: 2rem;
}
.tabs {
flex-direction: column;
align-items: center;
}
.tab {
margin: 5px 0;
width: 200px;
}
.content {
padding: 20px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1><i class="fas fa-database"></i> CM Bot Database Viewer</h1>
<p>Real-time view of accounts and users data</p>
</div>
<div class="tabs">
<button class="tab active" onclick="showTab('acc')">
<i class="fas fa-user-circle"></i> Accounts
</button>
<button class="tab" onclick="showTab('user')">
<i class="fas fa-users"></i> Users
</button>
</div>
<div class="content">
<button class="refresh-btn" onclick="refreshData()">
<i class="fas fa-sync-alt"></i> Refresh Data
</button>
<div id="stats" class="stats" style="display: none;">
<div class="stat-card">
<h3 id="acc-count">0</h3>
<p>Total Accounts</p>
</div>
<div class="stat-card">
<h3 id="user-count">0</h3>
<p>Total Users</p>
</div>
</div>
<div id="acc-content" class="tab-content">
<div class="loading">
<i class="fas fa-spinner"></i>
<p>Loading accounts...</p>
</div>
</div>
<div id="user-content" class="tab-content" style="display: none;">
<div class="loading">
<i class="fas fa-spinner"></i>
<p>Loading users...</p>
</div>
</div>
</div>
</div>
<script>
let currentTab = 'acc';
let accData = [];
let userData = [];
let editingCell = null;
let originalValue = null;
function showTab(tab) {
// Update tab buttons
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
event.target.classList.add('active');
// Show/hide content
document.getElementById('acc-content').style.display = tab === 'acc' ? 'block' : 'none';
document.getElementById('user-content').style.display = tab === 'user' ? 'block' : 'none';
currentTab = tab;
// Load data if not already loaded
if (tab === 'acc' && accData.length === 0) {
loadAccData();
} else if (tab === 'user' && userData.length === 0) {
loadUserData();
}
}
async function loadAccData() {
try {
const response = await fetch('/api/acc/');
if (!response.ok) throw new Error('Failed to fetch accounts');
accData = await response.json();
displayAccData();
updateStats();
} catch (error) {
document.getElementById('acc-content').innerHTML =
`<div class="error">Error loading accounts: ${error.message}</div>`;
}
}
async function loadUserData() {
try {
const response = await fetch('/api/user/');
if (!response.ok) throw new Error('Failed to fetch users');
userData = await response.json();
displayUserData();
updateStats();
} catch (error) {
document.getElementById('user-content').innerHTML =
`<div class="error">Error loading users: ${error.message}</div>`;
}
}
function displayAccData() {
const container = document.getElementById('acc-content');
if (accData.length === 0) {
container.innerHTML = '<div class="error">No accounts found</div>';
return;
}
// Sort data with 13c prefix priority
const sortedData = sortData([...accData], 'acc');
const table = `
<div class="table-container">
<table>
<thead>
<tr>
<th class="sortable"><i class="fas fa-user"></i> Username</th>
<th><i class="fas fa-lock"></i> Password <i class="fas fa-edit edit-icon"></i></th>
<th><i class="fas fa-info-circle"></i> Status <i class="fas fa-edit edit-icon"></i></th>
<th><i class="fas fa-link"></i> Link <i class="fas fa-edit edit-icon"></i></th>
</tr>
</thead>
<tbody>
${sortedData.map((acc, index) => `
<tr>
<td><strong>${acc.username || 'N/A'}</strong></td>
<td class="editable" onclick="startEdit(this, 'password', 'acc', ${accData.findIndex(a => a.username === acc.username)})">
${acc.password || 'N/A'}
</td>
<td class="editable" onclick="startEdit(this, 'status', 'acc', ${accData.findIndex(a => a.username === acc.username)})">
<span class="status-badge ${acc.status === 'active' ? 'status-active' : 'status-inactive'}">
${acc.status || 'N/A'}
</span>
</td>
<td class="editable" onclick="startEdit(this, 'link', 'acc', ${accData.findIndex(a => a.username === acc.username)})">
${acc.link || 'N/A'}
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
container.innerHTML = table;
}
function displayUserData() {
const container = document.getElementById('user-content');
if (userData.length === 0) {
container.innerHTML = '<div class="error">No users found</div>';
return;
}
// Sort data with 13c prefix priority and by update time
const sortedData = sortData([...userData], 'user');
const table = `
<div class="table-container">
<table>
<thead>
<tr>
<th class="sortable"><i class="fas fa-user"></i> From Username</th>
<th><i class="fas fa-lock"></i> From Password <i class="fas fa-edit edit-icon"></i></th>
<th><i class="fas fa-user"></i> To Username <i class="fas fa-edit edit-icon"></i></th>
<th><i class="fas fa-lock"></i> To Password <i class="fas fa-edit edit-icon"></i></th>
<th class="sortable"><i class="fas fa-clock"></i> Last Update</th>
</tr>
</thead>
<tbody>
${sortedData.map((user, index) => `
<tr>
<td><strong>${user.f_username || 'N/A'}</strong></td>
<td class="editable" onclick="startEdit(this, 'f_password', 'user', ${userData.findIndex(u => u.f_username === user.f_username)})">
${user.f_password || 'N/A'}
</td>
<td class="editable" onclick="startEdit(this, 't_username', 'user', ${userData.findIndex(u => u.f_username === user.f_username)})">
<strong>${user.t_username || 'N/A'}</strong>
</td>
<td class="editable" onclick="startEdit(this, 't_password', 'user', ${userData.findIndex(u => u.f_username === user.f_username)})">
${user.t_password || 'N/A'}
</td>
<td>${user.last_update_time || 'N/A'}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
container.innerHTML = table;
}
function updateStats() {
document.getElementById('acc-count').textContent = accData.length;
document.getElementById('user-count').textContent = userData.length;
document.getElementById('stats').style.display = 'grid';
}
function refreshData() {
if (currentTab === 'acc') {
loadAccData();
} else {
loadUserData();
}
}
// Sorting functions
function sortData(data, type) {
if (type === 'acc') {
return data.sort((a, b) => {
// 13c prefix always on top
const aIs13c = a.username && a.username.startsWith('13c');
const bIs13c = b.username && b.username.startsWith('13c');
if (aIs13c && !bIs13c) return -1;
if (!aIs13c && bIs13c) return 1;
// If both are 13c or both are not 13c, sort by username in descending order
if (aIs13c && bIs13c) {
return (b.username || '').localeCompare(a.username || '');
} else {
return (b.username || '').localeCompare(a.username || '');
}
});
} else if (type === 'user') {
return data.sort((a, b) => {
// 13c prefix always on top
const aIs13c = a.f_username && a.f_username.startsWith('13c');
const bIs13c = b.f_username && b.f_username.startsWith('13c');
if (aIs13c && !bIs13c) return -1;
if (!aIs13c && bIs13c) return 1;
// Then sort by last_update_time (newest first)
const aTime = new Date(a.last_update_time || 0);
const bTime = new Date(b.last_update_time || 0);
return bTime - aTime;
});
}
return data;
}
// Editing functions
function startEdit(cell, field, type, index) {
if (editingCell) {
cancelEdit();
}
editingCell = cell;
originalValue = cell.textContent.trim();
const input = document.createElement('input');
input.type = 'text';
input.className = 'edit-input';
input.value = originalValue;
const buttons = document.createElement('div');
buttons.className = 'edit-buttons';
buttons.innerHTML = `
<button class="edit-btn save-btn" onclick="saveEdit('${field}', '${type}', ${index})">
<i class="fas fa-check"></i>
</button>
<button class="edit-btn cancel-btn" onclick="cancelEdit()">
<i class="fas fa-times"></i>
</button>
`;
cell.innerHTML = '';
cell.appendChild(input);
cell.appendChild(buttons);
cell.classList.add('editing');
input.focus();
input.select();
}
function cancelEdit() {
if (editingCell) {
editingCell.textContent = originalValue;
editingCell.classList.remove('editing');
editingCell = null;
originalValue = null;
}
}
async function saveEdit(field, type, index) {
if (!editingCell) return;
const input = editingCell.querySelector('.edit-input');
const newValue = input.value.trim();
try {
if (type === 'acc') {
const data = {
username: accData[index].username,
password: field === 'password' ? newValue : accData[index].password,
status: field === 'status' ? newValue : accData[index].status,
link: field === 'link' ? newValue : accData[index].link
};
const response = await fetch('/api/update-acc-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (response.ok) {
accData[index][field] = newValue;
editingCell.textContent = newValue;
editingCell.classList.remove('editing');
editingCell = null;
originalValue = null;
} else {
throw new Error('Failed to update account data');
}
} else if (type === 'user') {
const data = {
f_username: userData[index].f_username,
f_password: field === 'f_password' ? newValue : userData[index].f_password,
t_username: field === 't_username' ? newValue : userData[index].t_username,
t_password: field === 't_password' ? newValue : userData[index].t_password
};
const response = await fetch('/api/update-user-data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (response.ok) {
userData[index][field] = newValue;
userData[index].last_update_time = new Date().toUTCString();
editingCell.textContent = newValue;
editingCell.classList.remove('editing');
editingCell = null;
originalValue = null;
} else {
throw new Error('Failed to update user data');
}
}
} catch (error) {
alert('Error updating data: ' + error.message);
cancelEdit();
}
}
// Load initial data
loadAccData();
// Auto-refresh every 30 seconds
setInterval(() => {
if (currentTab === 'acc') {
loadAccData();
} else {
loadUserData();
}
}, 30000);
</script>
</body>
</html>
"""
@app.route('/')
def index():
return render_template_string(HTML_TEMPLATE)
@app.route('/api/acc/')
def proxy_acc():
try:
response = requests.get(f"{API_BASE_URL}/acc/")
return jsonify(response.json())
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/user/')
def proxy_user():
try:
response = requests.get(f"{API_BASE_URL}/user/")
return jsonify(response.json())
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/update-acc-data', methods=['POST'])
def proxy_update_acc():
try:
data = request.get_json()
response = requests.post(f"{API_BASE_URL}/update-acc-data", json=data)
return jsonify(response.json()), response.status_code
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/update-user-data', methods=['POST'])
def proxy_update_user():
try:
data = request.get_json()
response = requests.post(f"{API_BASE_URL}/update-user-data", json=data)
return jsonify(response.json()), response.status_code
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
print("Starting CM Web View...")
print("Web interface will be available at: http://localhost:8000")
print("Make sure the API server is running on port 3000")
app.run(host='0.0.0.0', port=8000, debug=True)

99
db.py Normal file
View File

@ -0,0 +1,99 @@
import mysql.connector
from mysql.connector import Error
class DB:
def __init__(self):
self.config = {
'host': '192.168.0.210',
'user': 'rex_cm',
'password': 'hengserver',
'database': 'rex_cm',
'port': 3306
}
self.init_database()
def get_connection(self):
"""Get MySQL database connection."""
try:
connection = mysql.connector.connect(**self.config)
return connection
except Error as e:
print(f"Error connecting to MySQL: {e}")
return None
def init_database(self):
"""Initialize the database connection."""
connection = self.get_connection()
if connection is None:
raise Exception("Failed to connect to database")
try:
cursor = connection.cursor()
# Test connection by checking if required tables exist
cursor.execute("SHOW TABLES LIKE 'acc'")
if not cursor.fetchone():
raise Exception("Table 'acc' does not exist")
cursor.execute("SHOW TABLES LIKE 'user'")
if not cursor.fetchone():
raise Exception("Table 'user' does not exist")
print("Database connection verified - required tables exist")
except Error as e:
print(f"Error verifying database: {e}")
raise Exception(f"Database verification failed: {e}")
finally:
if connection.is_connected():
cursor.close()
connection.close()
def query(self, query, params=None):
"""Execute a query and return results."""
connection = self.get_connection()
if connection is None:
return []
try:
cursor = connection.cursor(dictionary=True)
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
results = cursor.fetchall()
return results
except Error as e:
print(f"Error executing query: {e}")
return []
finally:
if connection.is_connected():
cursor.close()
connection.close()
def execute(self, query, params=None):
"""Execute a query that modifies data (INSERT, UPDATE, DELETE) and return success status."""
connection = self.get_connection()
if connection is None:
return False
try:
cursor = connection.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
connection.commit()
return True
except Error as e:
print(f"Error executing query: {e}")
return False
finally:
if connection.is_connected():
cursor.close()
connection.close()

71
docker-compose.yml Normal file
View File

@ -0,0 +1,71 @@
services:
# Telegram Bot Service
telegram-bot:
build:
context: .
dockerfile: Dockerfile.telegram
container_name: cm-telegram-bot
restart: unless-stopped
environment:
- PYTHONUNBUFFERED=1
volumes:
- .:/app
networks:
- cm-network
depends_on:
- api-server
# API Server Service
api-server:
build:
context: .
dockerfile: Dockerfile.api
container_name: cm-api-server
restart: unless-stopped
ports:
- "3000:3000"
environment:
- PYTHONUNBUFFERED=1
volumes:
- .:/app
networks:
- cm-network
# Web View Service
web-view:
build:
context: .
dockerfile: Dockerfile.web
container_name: cm-web-view
restart: unless-stopped
ports:
- "8001:8000"
environment:
- PYTHONUNBUFFERED=1
- API_BASE_URL=http://api-server:3000
volumes:
- .:/app
networks:
- cm-network
depends_on:
- api-server
transfer-bot:
build:
context: .
dockerfile: Dockerfile.transfer
container_name: cm-transfer-bot
restart: unless-stopped
environment:
- PYTHONUNBUFFERED=1
volumes:
- .:/app
networks:
- cm-network
depends_on:
- api-server
networks:
cm-network:
driver: bridge

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
Flask==2.3.3
mysql-connector-python==8.1.0
flask-cors==4.0.0
python-telegram-bot==22.4
requests==2.32.5
beautifulsoup4==4.13.5
tqdm==4.67.1

542
transfer_credit.html Normal file
View File

@ -0,0 +1,542 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<!-- HTTP 1.1 -->
<meta http-equiv="Cache-Control" content="no-store"/>
<!-- HTTP 1.0 -->
<meta http-equiv="Pragma" content="no-cache"/>
<!-- Prevents caching at the Proxy Server -->
<meta http-equiv="Expires" content="0"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="AppFuse 2.0" /> <!-- leave this for stats please -->
<link rel="icon" href="/cm/images/favicon.ico"/>
<script type="text/javascript" src="/cm/struts/js/base/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/cm/struts/js/base/jquery.ui.core.min.js?s2j=3.3.3"></script>
<script type="text/javascript" src="/cm/struts/js/plugins/jquery.subscribe.min.js"></script>
<script type="text/javascript" src="/cm/struts/js/struts2/jquery.struts2-3.3.3.min.js"></script>
<script type="text/javascript">
$(function() {
jQuery.struts2_jquery.version="3.3.3";
jQuery.scriptPath = "/cm/struts/";
jQuery.struts2_jquery.gridLocal = "en";
jQuery.struts2_jquery.timeLocal = "en";
jQuery.ajaxSettings.traditional = true;
jQuery.ajaxSetup ({
cache: false
});
jQuery.struts2_jquery.require("js/struts2/jquery.ui.struts2-3.3.3.min.js");
});
</script>
<link id="jquery_theme_link" rel="stylesheet" href="/cm/struts/themes/ui-lightness/jquery-ui.css?s2j=3.3.3" type="text/css"/>
<title>Transfer | </title>
<link rel="stylesheet" type="text/css" media="all" href="/cm/styles/cm/theme.css" />
<link rel="stylesheet" type="text/css" media="print" href="/cm/styles/cm/print.css" />
<script type="text/javascript" src="/cm/scripts/global.js"></script>
<script type="text/javascript" src="/cm/scripts/clipboard.js"></script>
<script type="text/javascript" src="/cm/scripts/jquery.blockUI.js"></script>
<script type="text/javascript" src="/cm/scripts/date.format.js"></script>
<meta name="heading" content="Transfer"/>
<meta name="menu" content="CMDepositWithdrawal"/>
<script type="text/javascript">
function submitForm(){
document.getElementById("button_transfer").disabled = true;
document.transferForm.submit();
}
function searchUser(){
document.transferForm.action = "searchTransferUser";
document.transferForm.submit();
}
</script>
<script language="JavaScript">
function readCookie(name){
return(document.cookie.match('(^|; )'+name+'=([^;]*)')||0)[2];
}
//document.onscroll= document.cookie='ypos=' + window.pageYOffset;
$(window).scroll(function () {
document.cookie = 'ypos=' + getYOffset();
});
function getYOffset() {
var pageY;
if(typeof(window.pageYOffset)=='number') {
pageY=window.pageYOffset;
}
else {
pageY=document.documentElement.scrollTop;
}
return pageY;
}
</script>
</head>
<body onload="window.scrollTo(0,readCookie('ypos'));">
<div id="page">
<div id="header" class="clearfix">
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<div id="branding">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td><span id="spanTime" class="text-hdr-time-11"></span></td>
</tr>
</table>
</div>
<div id="scrollingMsg">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td><marquee behavior="scroll" direction="left" scrollamount="2" onmouseover="this.stop();" onmouseout="this.start();">
CM C/S Wsap: +601156938483
Special Draw 28.10.2025
Bayaran Baru(3600 - 5100)
CM Wsap: +601156938483 | FB:www.facebook.com/cmcashmarket
Web:www.cmcashmarket.net
</marquee>
</td>
</tr>
</table>
</div>
<div id="branding2">
<strong><span class="text-hdr-time-11">Welcome!,&nbsp;13c4021</span></strong>
&nbsp;
<a id="english" href="mainMenu?locale=en"><img height="10" width="16" src="/cm/images/hdr_lag_en.gif"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a id="chinese" href="mainMenu?locale=zh"><img height="10" width="16" src="/cm/images/hdr_lag_cn.gif"></a>
</div>
<hr>
<div id="header-login">
</div>
</div>
<div id="content" class="clearfix">
<div id="main">
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<div class="message" id="successMessages">
<img src="/cm/images/iconInformation.gif" alt="Information" class="icon" />
Successfully saved the record.<br />
</div>
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<form id="transferForm" method="post" action="saveTransfer" name="transferForm">
<input type="hidden" name="struts.token.name" value="token" />
<input type="hidden" name="token" value="GIO9ZJV4RUCFC48PHWDWLX7RX6TYZ6DQ" />
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td height="40" bgcolor="#FFFFFF">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="20">&nbsp;</td>
<td align="left" class="text-tt-18">Transfer</td>
</tr>
</table>
</td>
</tr>
<tr>
<td height="54" bgcolor="#f3f3f3" align="center">
<br>
<table id="depositTable" class="searchContent" border="0" style="width: 50%">
<tbody>
<tr>
<td class="bold" width="20%">Username : </td>
<td>
<input type="text" class="text medium" id="username" value="" name="username" >
<input id="search" class="button" type="button" onclick="javascript:searchUser();return false;" value="Search" name="button.search">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</form>
<script type="text/javascript">
$(".numberOnly").bind("keyup paste", function(){
var val = $(this).val();
if(isNaN(val)){
val = val.replace(/[^0-9\.]/g, '');
if(val.split('.').length>2) val =val.replace(/\.+$/,"");
this.value = val;
}
});
</script>
</div>
<!--
-->
<div id="nav">
<div class="wrapper">
<h2 class="accessibility">Navigation</h2>
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td class="bg-hdr-bar-blue" height="35px">
<div style="padding-left:30px; padding-right:30px">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td width="10%">
&nbsp;
</td>
<td width="90%" align="right">
<div id='cssmenu' class="menuList">
<ul>
<li>
<a href="/cm/mainMenu" title="Main Menu" class="text-mn-12">Main Menu</a>
</li>
<li class="has-sub">
<a href="javascript:void(0)" title="Cash Wallet" class="current" class="text-mn-12">Cash Wallet</a>
<ul>
<li>
<a href="/cm/transfer" title="Transfer" class="text-mn-12">Transfer</a>
</li>
<li class="last">
<a href="/cm/history/transferTransactions" title="Transfer Transactions" class="text-mn-12">Transfer Transactions</a>
</li>
</ul>
</li>
<li class="has-sub">
<a href="javascript:void(0)" title="Reports" class="text-mn-12">Reports</a>
<ul>
<li>
<a href="/cm/report/simpleWinLossDetail" title="Win Loss Report" class="text-mn-12">Win Loss Report</a>
</li>
<li>
<a href="/cm/report/betStatistic" title="Bet Statistic" class="text-mn-12">Bet Statistic</a>
</li>
<li>
<a href="/cm/report/drawResult" title="Draw Result" class="text-mn-12">Draw Result</a>
</li>
<li class="last">
<a href="/cm/report/memberReport" title="Member Report" class="text-mn-12">Member Report</a>
</li>
</ul>
</li>
<li class="has-sub">
<a href="javascript:void(0)" title="Account" class="text-mn-12">Account</a>
<ul>
<li>
<a href="/cm/showQrCode" title="QR Code" class="text-mn-12">QR Code</a>
</li>
<li>
<a href="/cm/register" title="Member Tree" class="text-mn-12">Member Tree</a>
</li>
<li>
<a href="/cm/changePassword" title="Change Password" class="text-mn-12">Change Password</a>
</li>
<li>
<a href="/cm/setSecurityPin" title="Change Security Pin" class="text-mn-12">Change Security Pin</a>
</li>
<li class="last">
<a href="/cm/userPackageAction" title="Prize Package" class="text-mn-12">Prize Package</a>
</li>
</ul>
</li>
<li>
<a href="/cm/logout" title="Logout" class="text-mn-12">Logout</a>
</li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<tr>
<td class="bg-hdr-bar-shadow">&nbsp;</td>
</tr>
</table>
</div>
<hr/>
</div><!-- end nav -->
</div>
<div id="footer" class="clearfix">
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tr>
<td height="30" valign="top" bgcolor="#f6f6f6">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td height="25" width="30" valign="bottom">&nbsp;</td>
<td valign="bottom" align="left" class="text-ftr-11">
Version 1.1.010
</td>
<td valign="bottom" align="right" class="text-ftr-11">
&copy; 2017 <font color="#2881c1"><a href="https://cm99.net">cm99.net</a></font></td>
<td width="30" valign="bottom">&nbsp;</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</div>
</div>
</body>
<script>
var timeServer = new Date(1759324187639);
//Users time
var timeLocal = new Date();
//Calculate the difference (returns milliseconds)
millDiff = timeLocal - timeServer;
//The ticking clock function
function TimeTick() {
//grab updated time
timeLocal = new Date();
//add time difference
timeLocal.setMilliseconds(timeLocal.getMilliseconds() - millDiff);
//display the value
// document.getElementById("spanTime").innerHTML = timeLocal.toLocaleFormat('%A, %d-%b-%Y %I:%M:%S%p');
document.getElementById("spanTime").innerHTML = timeLocal.format("dddd, dd-mmm-yyyy hh:MM:ssTT");
}
setInterval('TimeTick()', 500);
</script>
</html>

559
transfer_search.html Normal file
View File

@ -0,0 +1,559 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<!-- HTTP 1.1 -->
<meta http-equiv="Cache-Control" content="no-store"/>
<!-- HTTP 1.0 -->
<meta http-equiv="Pragma" content="no-cache"/>
<!-- Prevents caching at the Proxy Server -->
<meta http-equiv="Expires" content="0"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="AppFuse 2.0" /> <!-- leave this for stats please -->
<link rel="icon" href="/cm/images/favicon.ico"/>
<script type="text/javascript" src="/cm/struts/js/base/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="/cm/struts/js/base/jquery.ui.core.min.js?s2j=3.3.3"></script>
<script type="text/javascript" src="/cm/struts/js/plugins/jquery.subscribe.min.js"></script>
<script type="text/javascript" src="/cm/struts/js/struts2/jquery.struts2-3.3.3.min.js"></script>
<script type="text/javascript">
$(function() {
jQuery.struts2_jquery.version="3.3.3";
jQuery.scriptPath = "/cm/struts/";
jQuery.struts2_jquery.gridLocal = "en";
jQuery.struts2_jquery.timeLocal = "en";
jQuery.ajaxSettings.traditional = true;
jQuery.ajaxSetup ({
cache: false
});
jQuery.struts2_jquery.require("js/struts2/jquery.ui.struts2-3.3.3.min.js");
});
</script>
<link id="jquery_theme_link" rel="stylesheet" href="/cm/struts/themes/ui-lightness/jquery-ui.css?s2j=3.3.3" type="text/css"/>
<title>Transfer | </title>
<link rel="stylesheet" type="text/css" media="all" href="/cm/styles/cm/theme.css" />
<link rel="stylesheet" type="text/css" media="print" href="/cm/styles/cm/print.css" />
<script type="text/javascript" src="/cm/scripts/global.js"></script>
<script type="text/javascript" src="/cm/scripts/clipboard.js"></script>
<script type="text/javascript" src="/cm/scripts/jquery.blockUI.js"></script>
<script type="text/javascript" src="/cm/scripts/date.format.js"></script>
<meta name="heading" content="Transfer"/>
<meta name="menu" content="CMDepositWithdrawal"/>
<script type="text/javascript">
function submitForm(){
document.getElementById("button_transfer").disabled = true;
document.transferForm.submit();
}
function searchUser(){
document.transferForm.action = "searchTransferUser";
document.transferForm.submit();
}
</script>
<script language="JavaScript">
function readCookie(name){
return(document.cookie.match('(^|; )'+name+'=([^;]*)')||0)[2];
}
//document.onscroll= document.cookie='ypos=' + window.pageYOffset;
$(window).scroll(function () {
document.cookie = 'ypos=' + getYOffset();
});
function getYOffset() {
var pageY;
if(typeof(window.pageYOffset)=='number') {
pageY=window.pageYOffset;
}
else {
pageY=document.documentElement.scrollTop;
}
return pageY;
}
</script>
</head>
<body onload="window.scrollTo(0,readCookie('ypos'));">
<div id="page">
<div id="header" class="clearfix">
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<div id="branding">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td><span id="spanTime" class="text-hdr-time-11"></span></td>
</tr>
</table>
</div>
<div id="scrollingMsg">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td><marquee behavior="scroll" direction="left" scrollamount="2" onmouseover="this.stop();" onmouseout="this.start();">
CM C/S Wsap: +601156938483
Special Draw 28.10.2025
Bayaran Baru(3600 - 5100)
CM Wsap: +601156938483 | FB:www.facebook.com/cmcashmarket
Web:www.cmcashmarket.net
</marquee>
</td>
</tr>
</table>
</div>
<div id="branding2">
<strong><span class="text-hdr-time-11">Welcome!,&nbsp;13c4069</span></strong>
&nbsp;
<a id="english" href="mainMenu?locale=en"><img height="10" width="16" src="/cm/images/hdr_lag_en.gif"></a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a id="chinese" href="mainMenu?locale=zh"><img height="10" width="16" src="/cm/images/hdr_lag_cn.gif"></a>
</div>
<hr>
<div id="header-login">
</div>
</div>
<div id="content" class="clearfix">
<div id="main">
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<form id="transferForm" method="post" action="saveTransfer" name="transferForm">
<input type="hidden" name="struts.token.name" value="token" />
<input type="hidden" name="token" value="IGMNWENN2PM86EB7X30UG781VK1N3VDJ" />
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td height="40" bgcolor="#FFFFFF">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="20">&nbsp;</td>
<td align="left" class="text-tt-18">Transfer</td>
</tr>
</table>
</td>
</tr>
<tr>
<td height="54" bgcolor="#f3f3f3" align="center">
<br>
<table id="depositTable" class="searchContent" border="0" style="width: 50%">
<tbody>
<tr>
<td class="bold" width="20%">Username : </td>
<td>
<input type="text" class="text medium" id="username" value="wby321" name="username" readonly="readonly">
</td>
</tr>
<tr>
<td class="bold" width="20%">Name : </td>
<td>
<input type="text" class="text medium" id="name" value="Danny" name="name" readonly="readonly">
<input id="toUserId" type="hidden" value="6RbBr1holqKIJb6N1XzJGA==" name="toUserId">
</td>
</tr>
<tr>
<td class="bold" width="20%">Amount : </td>
<td>
<input type="text" class="text medium numberOnly" id="amount" value="" name="amount">
</td>
</tr>
<tr>
<td class="bold" width="20%">Security Pin : </td>
<td>
<input type="password" class="text medium" id="securityPin" value="" name="securityPin">
</td>
</tr>
<tr>
<td class="bold" width="20%"></td>
<td>
<input id="button_transfer" class="button" type="submit" onclick="javascript:submitForm();return false;" value="Transfer" name="button.transfer">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</form>
<script type="text/javascript">
$(".numberOnly").bind("keyup paste", function(){
var val = $(this).val();
if(isNaN(val)){
val = val.replace(/[^0-9\.]/g, '');
if(val.split('.').length>2) val =val.replace(/\.+$/,"");
this.value = val;
}
});
</script>
</div>
<!--
-->
<div id="nav">
<div class="wrapper">
<h2 class="accessibility">Navigation</h2>
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td class="bg-hdr-bar-blue" height="35px">
<div style="padding-left:30px; padding-right:30px">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td width="10%">
&nbsp;
</td>
<td width="90%" align="right">
<div id='cssmenu' class="menuList">
<ul>
<li>
<a href="/cm/mainMenu" title="Main Menu" class="text-mn-12">Main Menu</a>
</li>
<li class="has-sub">
<a href="javascript:void(0)" title="Cash Wallet" class="current" class="text-mn-12">Cash Wallet</a>
<ul>
<li>
<a href="/cm/transfer" title="Transfer" class="text-mn-12">Transfer</a>
</li>
<li class="last">
<a href="/cm/history/transferTransactions" title="Transfer Transactions" class="text-mn-12">Transfer Transactions</a>
</li>
</ul>
</li>
<li class="has-sub">
<a href="javascript:void(0)" title="Reports" class="text-mn-12">Reports</a>
<ul>
<li>
<a href="/cm/report/simpleWinLossDetail" title="Win Loss Report" class="text-mn-12">Win Loss Report</a>
</li>
<li>
<a href="/cm/report/betStatistic" title="Bet Statistic" class="text-mn-12">Bet Statistic</a>
</li>
<li>
<a href="/cm/report/drawResult" title="Draw Result" class="text-mn-12">Draw Result</a>
</li>
<li class="last">
<a href="/cm/report/memberReport" title="Member Report" class="text-mn-12">Member Report</a>
</li>
</ul>
</li>
<li class="has-sub">
<a href="javascript:void(0)" title="Account" class="text-mn-12">Account</a>
<ul>
<li>
<a href="/cm/showQrCode" title="QR Code" class="text-mn-12">QR Code</a>
</li>
<li>
<a href="/cm/register" title="Member Tree" class="text-mn-12">Member Tree</a>
</li>
<li>
<a href="/cm/changePassword" title="Change Password" class="text-mn-12">Change Password</a>
</li>
<li>
<a href="/cm/setSecurityPin" title="Change Security Pin" class="text-mn-12">Change Security Pin</a>
</li>
<li class="last">
<a href="/cm/userPackageAction" title="Prize Package" class="text-mn-12">Prize Package</a>
</li>
</ul>
</li>
<li>
<a href="/cm/logout" title="Logout" class="text-mn-12">Logout</a>
</li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<tr>
<td class="bg-hdr-bar-shadow">&nbsp;</td>
</tr>
</table>
</div>
<hr/>
</div><!-- end nav -->
</div>
<div id="footer" class="clearfix">
<!-- taglib uri="/struts-dojo-tags" prefix="sx" -->
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tr>
<td height="30" valign="top" bgcolor="#f6f6f6">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td height="25" width="30" valign="bottom">&nbsp;</td>
<td valign="bottom" align="left" class="text-ftr-11">
Version 1.1.010
</td>
<td valign="bottom" align="right" class="text-ftr-11">
&copy; 2017 <font color="#2881c1"><a href="https://cm99.net">cm99.net</a></font></td>
<td width="30" valign="bottom">&nbsp;</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</div>
</div>
</body>
<script>
var timeServer = new Date(1759323694667);
//Users time
var timeLocal = new Date();
//Calculate the difference (returns milliseconds)
millDiff = timeLocal - timeServer;
//The ticking clock function
function TimeTick() {
//grab updated time
timeLocal = new Date();
//add time difference
timeLocal.setMilliseconds(timeLocal.getMilliseconds() - millDiff);
//display the value
// document.getElementById("spanTime").innerHTML = timeLocal.toLocaleFormat('%A, %d-%b-%Y %I:%M:%S%p');
document.getElementById("spanTime").innerHTML = timeLocal.format("dddd, dd-mmm-yyyy hh:MM:ssTT");
}
setInterval('TimeTick()', 500);
</script>
</html>