Update app.py
Browse files
app.py
CHANGED
@@ -156,8 +156,11 @@ TEMPLATE = """
|
|
156 |
<meta charset="UTF-8">
|
157 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no, viewport-fit=cover">
|
158 |
<title>Morshen Group</title>
|
|
|
159 |
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
160 |
-
|
|
|
|
|
161 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
162 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
163 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
@@ -596,6 +599,7 @@ TEMPLATE = """
|
|
596 |
}
|
597 |
|
598 |
async function fetchTonBalance(address) {
|
|
|
599 |
const rpcUrl = 'https://rpc.toncenter.com/api/v2/jsonRPC';
|
600 |
try {
|
601 |
const response = await fetch(rpcUrl, {
|
@@ -605,7 +609,7 @@ TEMPLATE = """
|
|
605 |
},
|
606 |
body: JSON.stringify({
|
607 |
jsonrpc: '2.0',
|
608 |
-
id: 1,
|
609 |
method: 'getBalance',
|
610 |
params: {
|
611 |
address: address
|
@@ -616,32 +620,36 @@ TEMPLATE = """
|
|
616 |
const data = await response.json();
|
617 |
if (data.error) throw new Error(`RPC error: ${data.error.message}`);
|
618 |
const balanceNanoTon = parseInt(data.result);
|
619 |
-
const balanceTon = balanceNanoTon / 1e9;
|
620 |
-
return balanceTon.toFixed(4);
|
621 |
} catch (error) {
|
622 |
console.error('Error fetching TON balance:', error);
|
623 |
document.getElementById('ton-error').textContent = `Ошибка получения баланса: ${error.message}`;
|
624 |
document.getElementById('ton-error').style.display = 'block';
|
625 |
-
return 'N/A';
|
626 |
}
|
627 |
}
|
628 |
|
629 |
|
630 |
function initializeWebApp() {
|
|
|
631 |
if (!tg || !tg.initData) {
|
632 |
console.error("Telegram WebApp script not loaded or initData is missing.");
|
633 |
const greetingElement = document.getElementById('greeting');
|
634 |
if(greetingElement) greetingElement.textContent = 'Не удалось связаться с Telegram.';
|
635 |
-
document.body.style.visibility = 'visible';
|
636 |
return;
|
637 |
}
|
638 |
|
|
|
639 |
tg.ready();
|
640 |
tg.expand();
|
641 |
|
|
|
642 |
applyTheme(tg.themeParams);
|
643 |
tg.onEvent('themeChanged', () => applyTheme(tg.themeParams));
|
644 |
|
|
|
645 |
fetch('/verify', {
|
646 |
method: 'POST',
|
647 |
headers: {
|
@@ -665,6 +673,7 @@ TEMPLATE = """
|
|
665 |
console.error('Error sending initData for verification:', error);
|
666 |
});
|
667 |
|
|
|
668 |
const user = tg.initDataUnsafe?.user;
|
669 |
const greetingElement = document.getElementById('greeting');
|
670 |
if (user) {
|
@@ -674,14 +683,17 @@ TEMPLATE = """
|
|
674 |
greetingElement.textContent = 'Добро пожаловать!';
|
675 |
}
|
676 |
|
|
|
677 |
const contactButtons = document.querySelectorAll('.contact-link');
|
678 |
contactButtons.forEach(button => {
|
679 |
button.addEventListener('click', (e) => {
|
680 |
e.preventDefault();
|
|
|
681 |
tg.openTelegramLink('https://t.me/morshenkhan');
|
682 |
});
|
683 |
});
|
684 |
|
|
|
685 |
const modal = document.getElementById("saveModal");
|
686 |
const saveCardBtn = document.getElementById("save-card-btn");
|
687 |
const closeBtn = document.getElementById("modal-close-btn");
|
@@ -706,8 +718,10 @@ TEMPLATE = """
|
|
706 |
});
|
707 |
}
|
708 |
|
|
|
709 |
document.body.style.visibility = 'visible';
|
710 |
|
|
|
711 |
// --- TON Connect Integration ---
|
712 |
const connectTonBtn = document.getElementById('connect-ton-btn');
|
713 |
const disconnectTonBtn = document.getElementById('disconnect-ton-btn');
|
@@ -717,39 +731,45 @@ TEMPLATE = """
|
|
717 |
const tonInfoDiv = document.getElementById('ton-info');
|
718 |
const tonErrorDiv = document.getElementById('ton-error');
|
719 |
|
|
|
720 |
if (connectTonBtn && disconnectTonBtn && tonWalletStatus && tonWalletAddress && tonBalance && tonInfoDiv && tonErrorDiv) {
|
721 |
-
// Ensure TonConnectSDK is available globally
|
722 |
if (!window.TonConnectSDK) {
|
723 |
-
console.error("TON Connect SDK script not loaded correctly.");
|
724 |
tonErrorDiv.textContent = 'Ошибка: TON Connect SDK не загружен.';
|
725 |
tonErrorDiv.style.display = 'block';
|
726 |
-
connectTonBtn.disabled = true;
|
727 |
-
return;
|
728 |
}
|
729 |
|
730 |
try {
|
|
|
731 |
tonConnectSDK = new window.TonConnectSDK.TonConnectSDK({
|
732 |
manifestUrl: window.location.origin + '/tonconnect-manifest.json',
|
733 |
actionsConfiguration: {
|
734 |
-
|
|
|
|
|
735 |
}
|
736 |
});
|
737 |
|
|
|
738 |
tonConnectSDK.onStatusChange(async wallet => {
|
739 |
tonErrorDiv.style.display = 'none'; // Clear previous errors
|
740 |
if (wallet) {
|
|
|
741 |
tonWalletStatus.textContent = 'Статус: Подключен ✅';
|
742 |
tonWalletAddress.textContent = wallet.account.address;
|
743 |
tonInfoDiv.style.display = 'block';
|
744 |
connectTonBtn.style.display = 'none';
|
745 |
disconnectTonBtn.style.display = 'inline-flex';
|
746 |
-
tonBalance.textContent = 'Загрузка...';
|
747 |
const balance = await fetchTonBalance(wallet.account.address);
|
748 |
tonBalance.textContent = balance;
|
749 |
if (tg.HapticFeedback) {
|
750 |
tg.HapticFeedback.notificationOccurred('success');
|
751 |
}
|
752 |
} else {
|
|
|
753 |
tonWalletStatus.textContent = 'Статус: Не подключен';
|
754 |
tonWalletAddress.textContent = '';
|
755 |
tonBalance.textContent = '';
|
@@ -760,37 +780,41 @@ TEMPLATE = """
|
|
760 |
tg.HapticFeedback.notificationOccurred('warning');
|
761 |
}
|
762 |
}
|
763 |
-
//
|
|
|
764 |
});
|
765 |
|
|
|
766 |
connectTonBtn.addEventListener('click', () => {
|
767 |
tonErrorDiv.style.display = 'none'; // Clear error on button click
|
768 |
-
tonConnectSDK.connectWallet();
|
769 |
});
|
770 |
|
771 |
disconnectTonBtn.addEventListener('click', () => {
|
772 |
tonErrorDiv.style.display = 'none'; // Clear error on button click
|
773 |
-
tonConnectSDK.disconnect();
|
774 |
});
|
775 |
|
|
|
776 |
tonConnectSDK.restoreConnection();
|
777 |
|
778 |
} catch (e) {
|
|
|
779 |
console.error("Error initializing TON Connect SDK:", e);
|
780 |
tonErrorDiv.textContent = `Ошибка инициализации TON Connect: ${e.message}`;
|
781 |
tonErrorDiv.style.display = 'block';
|
782 |
-
connectTonBtn.disabled = true; // Disable button on error
|
783 |
}
|
784 |
} else {
|
785 |
-
console.error("TON Wallet section elements not found!");
|
786 |
}
|
787 |
// --- End TON Connect Integration ---
|
788 |
}
|
789 |
|
790 |
-
// Wait for the window to fully load (including
|
791 |
window.addEventListener('load', initializeWebApp);
|
792 |
|
793 |
-
// Fallback
|
794 |
setTimeout(() => {
|
795 |
if (document.body.style.visibility !== 'visible') {
|
796 |
console.error("WebApp initialization fallback timeout triggered.");
|
@@ -798,7 +822,7 @@ TEMPLATE = """
|
|
798 |
if(greetingElement) greetingElement.textContent = 'Ошибка загрузки интерфейса.';
|
799 |
document.body.style.visibility = 'visible';
|
800 |
}
|
801 |
-
}, 5000); // Increased timeout slightly
|
802 |
</script>
|
803 |
</body>
|
804 |
</html>
|
@@ -1064,10 +1088,13 @@ def verify_data():
|
|
1064 |
|
1065 |
@app.route('/tonconnect-manifest.json')
|
1066 |
def tonconnect_manifest():
|
|
|
|
|
1067 |
manifest = {
|
1068 |
"url": request.url_root.rstrip('/') + "/tonconnect-manifest.json",
|
1069 |
"name": "Morshen Group Mini App",
|
1070 |
-
"iconUrl":
|
|
|
1071 |
"termsOfUseUrl": "https://example.com/terms",
|
1072 |
"privacyPolicyUrl": "https://example.com/privacy"
|
1073 |
}
|
@@ -1096,16 +1123,23 @@ def admin_trigger_upload():
|
|
1096 |
|
1097 |
@app.route('/static/<path:filename>')
|
1098 |
def static_files(filename):
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
|
|
|
|
|
1106 |
|
1107 |
|
1108 |
if __name__ == '__main__':
|
|
|
|
|
|
|
|
|
|
|
1109 |
download_data_from_hf()
|
1110 |
load_visitor_data()
|
1111 |
|
|
|
156 |
<meta charset="UTF-8">
|
157 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=no, viewport-fit=cover">
|
158 |
<title>Morshen Group</title>
|
159 |
+
<!-- Telegram WebApp script MUST be loaded first -->
|
160 |
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
161 |
+
<!-- TON Connect SDK script with defer to avoid blocking -->
|
162 |
+
<script src="https://unpkg.com/@tonconnect/[email protected]/dist/tonconnect-sdk.min.js" defer></script>
|
163 |
+
|
164 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
165 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
166 |
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
|
599 |
}
|
600 |
|
601 |
async function fetchTonBalance(address) {
|
602 |
+
// Using public RPC endpoint provided by TON Center
|
603 |
const rpcUrl = 'https://rpc.toncenter.com/api/v2/jsonRPC';
|
604 |
try {
|
605 |
const response = await fetch(rpcUrl, {
|
|
|
609 |
},
|
610 |
body: JSON.stringify({
|
611 |
jsonrpc: '2.0',
|
612 |
+
id: 1, // Request ID, can be anything
|
613 |
method: 'getBalance',
|
614 |
params: {
|
615 |
address: address
|
|
|
620 |
const data = await response.json();
|
621 |
if (data.error) throw new Error(`RPC error: ${data.error.message}`);
|
622 |
const balanceNanoTon = parseInt(data.result);
|
623 |
+
const balanceTon = balanceNanoTon / 1e9; // Convert NanoTON to TON
|
624 |
+
return balanceTon.toFixed(4); // Format to 4 decimal places
|
625 |
} catch (error) {
|
626 |
console.error('Error fetching TON balance:', error);
|
627 |
document.getElementById('ton-error').textContent = `Ошибка получения баланса: ${error.message}`;
|
628 |
document.getElementById('ton-error').style.display = 'block';
|
629 |
+
return 'N/A'; // Return N/A on error
|
630 |
}
|
631 |
}
|
632 |
|
633 |
|
634 |
function initializeWebApp() {
|
635 |
+
// Check if Telegram WebApp is ready
|
636 |
if (!tg || !tg.initData) {
|
637 |
console.error("Telegram WebApp script not loaded or initData is missing.");
|
638 |
const greetingElement = document.getElementById('greeting');
|
639 |
if(greetingElement) greetingElement.textContent = 'Не удалось связаться с Telegram.';
|
640 |
+
document.body.style.visibility = 'visible'; // Show body even on error
|
641 |
return;
|
642 |
}
|
643 |
|
644 |
+
// Initialize Telegram WebApp features
|
645 |
tg.ready();
|
646 |
tg.expand();
|
647 |
|
648 |
+
// Apply theme based on Telegram app theme
|
649 |
applyTheme(tg.themeParams);
|
650 |
tg.onEvent('themeChanged', () => applyTheme(tg.themeParams));
|
651 |
|
652 |
+
// Send initData to backend for verification and logging
|
653 |
fetch('/verify', {
|
654 |
method: 'POST',
|
655 |
headers: {
|
|
|
673 |
console.error('Error sending initData for verification:', error);
|
674 |
});
|
675 |
|
676 |
+
// Display user greeting
|
677 |
const user = tg.initDataUnsafe?.user;
|
678 |
const greetingElement = document.getElementById('greeting');
|
679 |
if (user) {
|
|
|
683 |
greetingElement.textContent = 'Добро пожаловать!';
|
684 |
}
|
685 |
|
686 |
+
// Setup Contact Links to open Telegram chat
|
687 |
const contactButtons = document.querySelectorAll('.contact-link');
|
688 |
contactButtons.forEach(button => {
|
689 |
button.addEventListener('click', (e) => {
|
690 |
e.preventDefault();
|
691 |
+
// Replace 'morshenkhan' with the actual Telegram username
|
692 |
tg.openTelegramLink('https://t.me/morshenkhan');
|
693 |
});
|
694 |
});
|
695 |
|
696 |
+
// Setup Save Card Modal
|
697 |
const modal = document.getElementById("saveModal");
|
698 |
const saveCardBtn = document.getElementById("save-card-btn");
|
699 |
const closeBtn = document.getElementById("modal-close-btn");
|
|
|
718 |
});
|
719 |
}
|
720 |
|
721 |
+
// Show body after initial setup is complete
|
722 |
document.body.style.visibility = 'visible';
|
723 |
|
724 |
+
|
725 |
// --- TON Connect Integration ---
|
726 |
const connectTonBtn = document.getElementById('connect-ton-btn');
|
727 |
const disconnectTonBtn = document.getElementById('disconnect-ton-btn');
|
|
|
731 |
const tonInfoDiv = document.getElementById('ton-info');
|
732 |
const tonErrorDiv = document.getElementById('ton-error');
|
733 |
|
734 |
+
// Check if TON Connect elements exist and TON Connect SDK is loaded
|
735 |
if (connectTonBtn && disconnectTonBtn && tonWalletStatus && tonWalletAddress && tonBalance && tonInfoDiv && tonErrorDiv) {
|
|
|
736 |
if (!window.TonConnectSDK) {
|
737 |
+
console.error("TON Connect SDK script not loaded correctly or window.TonConnectSDK is not defined.");
|
738 |
tonErrorDiv.textContent = 'Ошибка: TON Connect SDK не загружен.';
|
739 |
tonErrorDiv.style.display = 'block';
|
740 |
+
connectTonBtn.disabled = true; // Disable connect button
|
741 |
+
return; // Stop TON Connect initialization
|
742 |
}
|
743 |
|
744 |
try {
|
745 |
+
// Initialize TON Connect SDK
|
746 |
tonConnectSDK = new window.TonConnectSDK.TonConnectSDK({
|
747 |
manifestUrl: window.location.origin + '/tonconnect-manifest.json',
|
748 |
actionsConfiguration: {
|
749 |
+
// !!! IMPORTANT: Replace with your actual bot username and Mini App shortname
|
750 |
+
// Example: 'https://t.me/MyAwesomeBot/MyMiniApp'
|
751 |
+
twaReturnUrl: 'https://t.me/YOUR_BOT_USERNAME/YOUR_MINIAPP_SHORTNAME'
|
752 |
}
|
753 |
});
|
754 |
|
755 |
+
// Subscribe to wallet status changes
|
756 |
tonConnectSDK.onStatusChange(async wallet => {
|
757 |
tonErrorDiv.style.display = 'none'; // Clear previous errors
|
758 |
if (wallet) {
|
759 |
+
// Wallet is connected
|
760 |
tonWalletStatus.textContent = 'Статус: Подключен ✅';
|
761 |
tonWalletAddress.textContent = wallet.account.address;
|
762 |
tonInfoDiv.style.display = 'block';
|
763 |
connectTonBtn.style.display = 'none';
|
764 |
disconnectTonBtn.style.display = 'inline-flex';
|
765 |
+
tonBalance.textContent = 'Загрузка...'; // Show loading state for balance
|
766 |
const balance = await fetchTonBalance(wallet.account.address);
|
767 |
tonBalance.textContent = balance;
|
768 |
if (tg.HapticFeedback) {
|
769 |
tg.HapticFeedback.notificationOccurred('success');
|
770 |
}
|
771 |
} else {
|
772 |
+
// Wallet is disconnected
|
773 |
tonWalletStatus.textContent = 'Статус: Не подключен';
|
774 |
tonWalletAddress.textContent = '';
|
775 |
tonBalance.textContent = '';
|
|
|
780 |
tg.HapticFeedback.notificationOccurred('warning');
|
781 |
}
|
782 |
}
|
783 |
+
// Hide Telegram's MainButton if it might be interfering
|
784 |
+
// tg.MainButton.hide();
|
785 |
});
|
786 |
|
787 |
+
// Add event listeners for connect/disconnect buttons
|
788 |
connectTonBtn.addEventListener('click', () => {
|
789 |
tonErrorDiv.style.display = 'none'; // Clear error on button click
|
790 |
+
tonConnectSDK.connectWallet(); // Trigger wallet connection flow
|
791 |
});
|
792 |
|
793 |
disconnectTonBtn.addEventListener('click', () => {
|
794 |
tonErrorDiv.style.display = 'none'; // Clear error on button click
|
795 |
+
tonConnectSDK.disconnect(); // Trigger wallet disconnection
|
796 |
});
|
797 |
|
798 |
+
// Attempt to restore previous connection on startup
|
799 |
tonConnectSDK.restoreConnection();
|
800 |
|
801 |
} catch (e) {
|
802 |
+
// Catch errors specifically during SDK instantiation
|
803 |
console.error("Error initializing TON Connect SDK:", e);
|
804 |
tonErrorDiv.textContent = `Ошибка инициализации TON Connect: ${e.message}`;
|
805 |
tonErrorDiv.style.display = 'block';
|
806 |
+
connectTonBtn.disabled = true; // Disable button on initialization error
|
807 |
}
|
808 |
} else {
|
809 |
+
console.error("TON Wallet section HTML elements not found!");
|
810 |
}
|
811 |
// --- End TON Connect Integration ---
|
812 |
}
|
813 |
|
814 |
+
// Wait for the window to fully load all resources (including deferred scripts)
|
815 |
window.addEventListener('load', initializeWebApp);
|
816 |
|
817 |
+
// Fallback timeout in case something prevents 'load' or Telegram script
|
818 |
setTimeout(() => {
|
819 |
if (document.body.style.visibility !== 'visible') {
|
820 |
console.error("WebApp initialization fallback timeout triggered.");
|
|
|
822 |
if(greetingElement) greetingElement.textContent = 'Ошибка загрузки интерфейса.';
|
823 |
document.body.style.visibility = 'visible';
|
824 |
}
|
825 |
+
}, 5000); // Increased timeout slightly to 5 seconds
|
826 |
</script>
|
827 |
</body>
|
828 |
</html>
|
|
|
1088 |
|
1089 |
@app.route('/tonconnect-manifest.json')
|
1090 |
def tonconnect_manifest():
|
1091 |
+
# You need to serve your icon file, e.g., place morshengroup.png in a 'static' folder
|
1092 |
+
icon_url = request.url_root.rstrip('/') + "/static/morshengroup.png"
|
1093 |
manifest = {
|
1094 |
"url": request.url_root.rstrip('/') + "/tonconnect-manifest.json",
|
1095 |
"name": "Morshen Group Mini App",
|
1096 |
+
"iconUrl": icon_url,
|
1097 |
+
# !!! IMPORTANT: Replace with actual URLs for your terms of use and privacy policy
|
1098 |
"termsOfUseUrl": "https://example.com/terms",
|
1099 |
"privacyPolicyUrl": "https://example.com/privacy"
|
1100 |
}
|
|
|
1123 |
|
1124 |
@app.route('/static/<path:filename>')
|
1125 |
def static_files(filename):
|
1126 |
+
# Assuming you place your icon file (e.g., morshengroup.png) in a 'static' folder
|
1127 |
+
# relative to your app.py file.
|
1128 |
+
# For the manifest, a PNG is usually required, but the original code used JPG.
|
1129 |
+
# Let's assume you have a morshengroup.png in a static folder.
|
1130 |
+
static_dir = os.path.join(app.root_path, 'static')
|
1131 |
+
return send_from_directory(static_dir, filename)
|
1132 |
+
|
1133 |
+
# Import send_from_directory for the static route
|
1134 |
+
from flask import send_from_directory
|
1135 |
|
1136 |
|
1137 |
if __name__ == '__main__':
|
1138 |
+
# Ensure the static directory exists if you're using it for icons
|
1139 |
+
if not os.path.exists('static'):
|
1140 |
+
os.makedirs('static')
|
1141 |
+
# You should place your morshengroup.png file inside the new 'static' folder
|
1142 |
+
|
1143 |
download_data_from_hf()
|
1144 |
load_visitor_data()
|
1145 |
|