flpolprojects's picture
Update app.py
27a053f verified
from flask import Flask, render_template_string, jsonify, request
import json
import os
import math
from datetime import datetime, timedelta
app = Flask(__name__)
# Инициализация файла JSON (если файла нет – создаётся пустой список)
def init_db():
if not os.path.exists('markers.json'):
with open('markers.json', 'w', encoding='utf-8') as f:
json.dump([], f, ensure_ascii=False, indent=4)
# Загрузка меток из файла JSON
def load_markers():
with open('markers.json', 'r', encoding='utf-8') as f:
return json.load(f)
# Сохранение меток в файл JSON
def save_markers(markers):
with open('markers.json', 'w', encoding='utf-8') as f:
json.dump(markers, f, ensure_ascii=False, indent=4)
# Получение всех меток
def get_all_markers():
return load_markers()
# Добавление новой метки в "базу" (JSON-файл)
def add_marker_to_db(name, description, telegram_link, logo_link, latitude, longitude, delete_password):
markers = load_markers()
new_id = max((marker["id"] for marker in markers), default=0) + 1
new_marker = {
"id": new_id,
"name": name,
"description": description,
"telegram_link": telegram_link,
"logo_link": logo_link,
"latitude": latitude,
"longitude": longitude,
"delete_password": delete_password,
"premium": 0,
"premium_start_date": None
}
markers.append(new_marker)
save_markers(markers)
return new_id
# Удаление метки из JSON-файла
def remove_marker_from_db(marker_id):
markers = load_markers()
markers = [marker for marker in markers if marker["id"] != marker_id]
save_markers(markers)
# Активация премиум-статуса для метки
def activate_premium(marker_id):
markers = load_markers()
current_date = datetime.now().isoformat()
for marker in markers:
if marker["id"] == marker_id:
marker["premium"] = 1
marker["premium_start_date"] = current_date
break
save_markers(markers)
# Проверка и сброс премиум-статуса по истечении 30 дней
def check_premium_status():
markers = load_markers()
updated = False
for marker in markers:
if marker["premium"] == 1 and marker["premium_start_date"]:
premium_start_date = datetime.fromisoformat(marker["premium_start_date"])
current_date = datetime.now()
if (current_date - premium_start_date).days > 30:
marker["premium"] = 0
marker["premium_start_date"] = None
updated = True
if updated:
save_markers(markers)
# HTML-шаблон с футуристическим дизайном
html_template = '''
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGram 2070</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css">
<style>
/* Общие стили */
body {
font-family: 'Roboto', sans-serif;
background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
margin: 0;
padding: 0;
color: #fff;
overflow-x: hidden;
}
h1 {
text-align: center;
padding: 20px;
margin: 0;
background: linear-gradient(135deg, #ff00cc, #333399);
color: white;
font-size: 32px;
letter-spacing: 4px;
text-shadow: 0 0 10px rgba(255, 0, 204, 0.8), 0 0 20px rgba(255, 0, 204, 0.8);
}
#map {
height: 90vh;
width: 100%;
margin-bottom: 20px;
}
/* Поиск */
#search-container {
display: flex;
justify-content: center;
align-items: center;
margin: 20px;
}
#search-input {
padding: 15px;
width: 300px;
border: none;
border-radius: 30px 0 0 30px;
font-size: 16px;
background: #24243e;
color: #fff;
outline: none;
transition: background 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
}
#search-input:focus {
background: #302b63;
}
#search-button {
padding: 15px 30px;
background: linear-gradient(135deg, #ff00cc, #333399);
color: white;
border: none;
border-radius: 0 30px 30px 0;
cursor: pointer;
font-size: 16px;
transition: background 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
}
#search-button:hover {
background: linear-gradient(135deg, #333399, #ff00cc);
}
/* Результаты поиска */
#search-results {
margin: 20px;
text-align: center;
}
.result-item {
background: linear-gradient(135deg, #24243e, #302b63);
border: 1px solid rgba(255, 0, 204, 0.5);
border-radius: 15px;
margin: 10px auto;
padding: 20px;
max-width: 500px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
}
.result-item:hover {
transform: translateY(-5px);
box-shadow: 0 0 20px rgba(255, 0, 204, 0.8);
}
.result-item b {
color: #ff00cc;
font-size: 18px;
}
.result-item p {
color: #ecf0f1;
margin: 5px 0;
}
.result-item a {
color: #ff00cc;
text-decoration: none;
font-weight: bold;
}
.result-item a:hover {
text-decoration: underline;
}
.result-item button {
background: linear-gradient(135deg, #ff00cc, #333399);
color: white;
border: none;
padding: 10px 20px;
border-radius: 30px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
}
.result-item button:hover {
background: linear-gradient(135deg, #333399, #ff00cc);
}
/* Окно добавления метки */
.popup-form {
background: linear-gradient(135deg, #24243e, #302b63);
border-radius: 15px;
padding: 20px;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
width: 300px;
}
.popup-form input,
.popup-form textarea {
padding: 12px;
margin-bottom: 10px;
border: none;
border-radius: 10px;
font-size: 14px;
background: #302b63;
color: #fff;
outline: none;
transition: background 0.3s ease;
}
.popup-form input:focus,
.popup-form textarea:focus {
background: #ff00cc;
}
.popup-form button {
background: linear-gradient(135deg, #ff00cc, #333399);
color: white;
border: none;
padding: 12px;
border-radius: 30px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
}
.popup-form button:hover {
background: linear-gradient(135deg, #333399, #ff00cc);
}
/* Попапы на карте */
.leaflet-popup-content-wrapper {
background: linear-gradient(135deg, #24243e, #302b63);
border-radius: 15px;
padding: 15px;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
}
.leaflet-popup-content {
color: #fff;
}
.leaflet-popup-content b {
color: #ff00cc;
font-size: 18px;
display: block;
margin-bottom: 5px;
}
.leaflet-popup-content a {
color: #ff00cc;
text-decoration: none;
}
.leaflet-popup-content a:hover {
text-decoration: underline;
}
.leaflet-popup-content button {
background: linear-gradient(135deg, #ff00cc, #333399);
color: white;
border: none;
padding: 10px 20px;
border-radius: 30px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
}
.leaflet-popup-content button:hover {
background: linear-gradient(135deg, #333399, #ff00cc);
}
/* Маркеры */
.marker-logo {
width: 30px;
height: 30px;
border-radius: 50%;
border: 2px solid #ff00cc;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);
}
</style>
</head>
<body>
<h1>GeoGram 2070</h1>
<div id="search-container">
<input type="text" id="search-input" placeholder="Поиск по названию или описанию" />
<button id="search-button">Поиск</button>
</div>
<div id="search-results"></div>
<div id="map"></div>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script>
const map = L.map('map').setView([55.75, 37.61], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
}).addTo(map);
let userLatitude = 55.75;
let userLongitude = 37.61;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
userLatitude = position.coords.latitude;
userLongitude = position.coords.longitude;
map.setView([userLatitude, userLongitude], 13);
L.marker([userLatitude, userLongitude]).addTo(map)
.bindPopup('<span style="color:white;">Вы находитесь здесь</span>').openPopup();
}, function() {
alert('Не удалось получить ваше местоположение.');
});
} else {
alert('Ваш браузер не поддерживает геолокацию.');
}
let markersOnMap = {};
fetch('/get_markers')
.then(response => response.json())
.then(data => {
data.forEach(marker => {
const customIcon = L.divIcon({
className: 'custom-icon',
html: `<img src="${marker.logo_link || 'https://simpleicon.com/wp-content/uploads/map-marker-1.png'}" class="marker-logo"/>`,
iconSize: [30, 30],
iconAnchor: [15, 30]
});
const markerObj = L.marker([marker.latitude, marker.longitude], {icon: customIcon}).addTo(map);
markersOnMap[marker.id] = markerObj;
let premiumLabel = '';
if (marker.premium) {
const premiumStartDate = new Date(marker.premium_start_date);
const currentDate = new Date();
const daysLeft = Math.max(0, Math.floor((premiumStartDate.getTime() + (30 * 24 * 60 * 60 * 1000) - currentDate.getTime()) / (1000 * 60 * 60 * 24)));
premiumLabel = `<span style="color: gold;">Премиум метка (${daysLeft} дней осталось)</span><br>`;
}
markerObj.bindPopup(`
<div class="leaflet-popup-content">
${premiumLabel}
<b>${marker.name}</b>
<p>${marker.description}</p>
<button style="background: linear-gradient(135deg, #ff00cc, #333399);
color: white;
border: none;
padding: 10px 20px;
border-radius: 30px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);"
onclick="window.open('https://t.me/${marker.telegram_link}', '_blank')">
Перейти в Telegram
</button><br>
<button onclick="buyPremium(${marker.id})">Купить премиум</button>
<button onclick="removeMarker(${marker.id}, '${marker.delete_password}')">Удалить</button>
</div>
`);
});
});
function buyPremium(markerId) {
const activationCode = prompt("Введите код активации премиума:");
if (activationCode === "morshenadmin87132") {
fetch('/activate_premium', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({id: markerId})
}).then(() => {
alert("Премиум активирован!");
location.reload();
});
} else {
alert("Неверный код активации!");
}
}
function addMarker(name, description, telegram_link, logo_link, position) {
const deletePassword = prompt("Введите пароль для удаления метки:");
const finalLogoLink = logo_link || 'https://icons.veryicon.com/png/o/miscellaneous/high-icon-library/geo-fence.png';
fetch('/add_marker', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: name,
description: description,
telegram_link: telegram_link,
logo_link: finalLogoLink,
position: [position.lat, position.lng],
delete_password: deletePassword
})
})
.then(response => response.json())
.then(data => {
const customIcon = L.divIcon({
className: 'custom-icon',
html: `<img src="${data.logo_link}" class="marker-logo"/>`,
iconSize: [30, 30],
iconAnchor: [15, 30]
});
const markerObj = L.marker([data.latitude, data.longitude], {icon: customIcon}).addTo(map);
markersOnMap[data.id] = markerObj;
let premiumLabel = data.premium ? '<span style="color: gold;">Премиум метка</span><br>' : '';
markerObj.bindPopup(`
<div class="leaflet-popup-content">
${premiumLabel}
<b>${data.name}</b>
<p>${data.description}</p>
<button style="background: linear-gradient(135deg, #ff00cc, #333399);
color: white;
border: none;
padding: 10px 20px;
border-radius: 30px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);"
onclick="window.open('https://t.me/${data.telegram_link}', '_blank')">
Перейти в Telegram
</button><br>
<button onclick="buyPremium(${data.id})">Купить премиум</button>
<button onclick="removeMarker(${data.id}, '${data.delete_password}')">Удалить</button>
</div>
`);
});
}
map.on('click', function(e) {
const popupContent = document.createElement('div');
popupContent.classList.add('popup-form');
const inputName = document.createElement('input');
inputName.type = 'text';
inputName.placeholder = 'Название ';
popupContent.appendChild(inputName);
const inputDescription = document.createElement('textarea');
inputDescription.placeholder = 'Ваши товары/услуги';
popupContent.appendChild(inputDescription);
const inputTelegram = document.createElement('input');
inputTelegram.type = 'text';
inputTelegram.placeholder = 'Telegram канал (без @)';
popupContent.appendChild(inputTelegram);
const inputLogoLink = document.createElement('input');
inputLogoLink.type = 'text';
inputLogoLink.placeholder = 'Ссылка на логотип (необязательно)';
popupContent.appendChild(inputLogoLink);
const submitButton = document.createElement('button');
submitButton.textContent = 'Добавить метку';
submitButton.onclick = function() {
const name = inputName.value;
const description = inputDescription.value;
const telegram_link = inputTelegram.value;
const logo_link = inputLogoLink.value;
if(name && description && telegram_link) {
addMarker(name, description, telegram_link, logo_link, e.latlng);
}
map.closePopup();
};
popupContent.appendChild(submitButton);
L.popup()
.setLatLng(e.latlng)
.setContent(popupContent)
.openOn(map);
});
function removeMarker(markerId, markerPassword) {
const universalPassword = "morshenadmin87132";
const passwordInput = prompt("Введите пароль для удаления метки:");
if (passwordInput === universalPassword || passwordInput === markerPassword) {
fetch('/remove_marker', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({id: markerId})
}).then(() => {
location.reload();
});
} else {
alert("Неверный пароль!");
}
}
// Обновленная функция поиска с приоритетом премиум меток
document.getElementById('search-button').addEventListener('click', function() {
const searchTerm = document.getElementById('search-input').value.toLowerCase();
fetch('/get_markers')
.then(response => response.json())
.then(data => {
const resultsContainer = document.getElementById('search-results');
resultsContainer.innerHTML = '';
// Вычисление расстояния до каждого маркера
data.forEach(marker => {
marker.distance = calculateDistance(userLatitude, userLongitude, marker.latitude, marker.longitude);
});
// Фильтрация результатов по поисковому запросу
const results = data.filter(marker =>
marker.name.toLowerCase().includes(searchTerm) ||
marker.description.toLowerCase().includes(searchTerm)
);
// Сортировка результатов: сначала премиум метки, затем обычные,
// и внутри каждой категории сортировка по расстоянию
results.sort((a, b) => {
if (a.premium !== b.premium) {
return b.premium - a.premium; // Премиум метки идут первыми
}
return a.distance - b.distance; // Сортировка по расстоянию
});
// Отображение результатов
results.forEach(marker => {
const resultItem = document.createElement('div');
resultItem.classList.add('result-item');
let premiumLabel = '';
if (marker.premium) {
const premiumStartDate = new Date(marker.premium_start_date);
const currentDate = new Date();
const daysLeft = Math.max(0, Math.floor((premiumStartDate.getTime() + (30 * 24 * 60 * 60 * 1000) - currentDate.getTime()) / (1000 * 60 * 60 * 24)));
premiumLabel = ` (${daysLeft} дней осталось)`;
}
resultItem.innerHTML = `
<b>${marker.name}</b>
<p>${marker.description}</p>
<p>Расстояние: ${marker.distance.toFixed(2)} км</p>
<p>Статус: ${marker.premium ? 'Премиум' + premiumLabel : 'Обычная'}</p>
<button style="background: linear-gradient(135deg, #ff00cc, #333399);
color: white;
border: none;
padding: 10px 20px;
border-radius: 30px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
box-shadow: 0 0 10px rgba(255, 0, 204, 0.8);"
onclick="window.open('https://t.me/${marker.telegram_link}', '_blank')">
Перейти в Telegram
</button>
<button onclick="showOnMap(${marker.id}, ${marker.latitude}, ${marker.longitude}); hideResults()">Показать на карте</button>`;
resultsContainer.appendChild(resultItem);
});
if (results.length === 0) {
resultsContainer.innerHTML = '<p style="color:white;">Нет результатов</p>';
}
});
});
function showOnMap(markerId, lat, lng) {
map.setView([lat, lng], 13);
if (markersOnMap[markerId]) {
markersOnMap[markerId].openPopup();
}
}
function hideResults() {
const resultsContainer = document.getElementById('search-results');
resultsContainer.innerHTML = '';
}
function calculateDistance(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLon = (lon2 - lon1) * Math.PI / 180;
const a =
0.5 - Math.cos(dLat) / 2 +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
(1 - Math.cos(dLon)) / 2;
return R * 2 * Math.asin(Math.sqrt(a));
}
</script>
</body>
</html>
'''
# Маршрут для главной страницы
@app.route('/')
def index():
check_premium_status()
return render_template_string(html_template)
# Маршрут для получения всех меток
@app.route('/get_markers')
def get_markers_route():
markers = get_all_markers()
# Если для метки не указан логотип, используем изображение по умолчанию
for marker in markers:
if not marker.get("logo_link"):
marker["logo_link"] = "https://simpleicon.com/wp-content/uploads/map-marker-1.png"
return jsonify(markers)
# Маршрут для добавления метки
@app.route('/add_marker', methods=['POST'])
def add_marker():
data = request.json
name = data['name']
description = data['description']
telegram_link = data['telegram_link']
logo_link = data['logo_link'] if data['logo_link'] else 'https://simpleicon.com/wp-content/uploads/map-marker-1.png'
latitude, longitude = data['position']
delete_password = data['delete_password']
marker_id = add_marker_to_db(name, description, telegram_link, logo_link, latitude, longitude, delete_password)
return jsonify({
'id': marker_id,
'name': name,
'description': description,
'telegram_link': telegram_link,
'logo_link': logo_link,
'latitude': latitude,
'longitude': longitude,
'delete_password': delete_password,
'premium': 0,
'premium_start_date': None
})
# Маршрут для удаления метки
@app.route('/remove_marker', methods=['POST'])
def remove_marker_route():
data = request.json
marker_id = data['id']
remove_marker_from_db(marker_id)
return jsonify({'success': True})
# Маршрут для активации премиум-статуса
@app.route('/activate_premium', methods=['POST'])
def activate_premium_route():
data = request.json
marker_id = data['id']
activate_premium(marker_id)
return jsonify({'success': True})
if __name__ == '__main__':
init_db()
app.run(debug=True, host='0.0.0.0', port=7860)