mirror of
https://github.com/Dvorinka/PPve.git
synced 2026-06-03 20:12:59 +00:00
hjh
This commit is contained in:
+64
-63
@@ -127,14 +127,6 @@ async function checkAchievements() {
|
||||
unlockAchievement('super_fan');
|
||||
localStorage.setItem('super_fan_' + visitorId, 'true');
|
||||
}
|
||||
|
||||
// Apply highest unlocked achievement theme
|
||||
const unlocked = Array.from(unlockedAchievements);
|
||||
if (unlocked.length > 0) {
|
||||
const highestAchievement = unlocked[unlocked.length - 1];
|
||||
currentTheme = ACHIEVEMENTS[highestAchievement].theme;
|
||||
applyTheme();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking achievements:', error);
|
||||
}
|
||||
@@ -142,72 +134,84 @@ async function checkAchievements() {
|
||||
|
||||
// Unlock achievement and show toast
|
||||
function unlockAchievement(achievement) {
|
||||
const achievementId = Object.keys(ACHIEVEMENTS).find(key =>
|
||||
ACHIEVEMENTS[key].name === achievement.name
|
||||
);
|
||||
|
||||
if (!unlockedAchievements.has(achievementId)) {
|
||||
unlockedAchievements.add(achievementId);
|
||||
showAchievementToast(achievement);
|
||||
}
|
||||
}
|
||||
|
||||
// Show only unlocked achievements
|
||||
function showAchievements() {
|
||||
const achievementsDisplay = document.getElementById('achievementsDisplay');
|
||||
if (achievementsDisplay) {
|
||||
// Clear existing achievements
|
||||
achievementsDisplay.innerHTML = '';
|
||||
|
||||
// Show only unlocked achievements
|
||||
Array.from(unlockedAchievements).forEach(achievementId => {
|
||||
const achievement = ACHIEVEMENTS[achievementId];
|
||||
const achievementItem = document.createElement('div');
|
||||
achievementItem.className = 'achievement-item flex items-center p-3 rounded-lg mb-2';
|
||||
achievementItem.style.backgroundColor = achievement.theme.backgroundColor;
|
||||
achievementItem.style.color = achievement.theme.textColor;
|
||||
|
||||
achievementItem.innerHTML = `
|
||||
<i class="fas ${achievement.icon} achievement-icon ${achievement.color}"></i>
|
||||
<div>
|
||||
<h4 class="font-bold">${achievement.name}</h4>
|
||||
<p class="text-sm">${achievement.description}</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
achievementsDisplay.appendChild(achievementItem);
|
||||
});
|
||||
|
||||
achievementsDisplay.style.display = 'block';
|
||||
}
|
||||
unlockedAchievements.add(achievement);
|
||||
showAchievementToast(achievement);
|
||||
showAchievements();
|
||||
}
|
||||
|
||||
// Show achievement toast
|
||||
function showAchievementToast(achievement) {
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `fixed bottom-4 right-4 bg-white rounded-lg shadow-lg p-4 w-64 flex items-center ${achievement.color}`;
|
||||
|
||||
toast.className = 'fixed bottom-4 right-4 bg-white rounded-lg shadow-lg p-4 w-64 flex items-center text-green-500';
|
||||
toast.innerHTML = `
|
||||
<div class="flex-1">
|
||||
<h3 class="font-bold text-lg">${achievement.name}</h3>
|
||||
<p class="text-gray-600">${achievement.description}</p>
|
||||
<h3 class="font-bold text-lg">Achievement Unlocked!</h3>
|
||||
<p class="text-gray-600">${ACHIEVEMENTS[achievement].name}</p>
|
||||
</div>
|
||||
<i class="fas ${achievement.icon} text-2xl ml-4"></i>
|
||||
<i class="fas fa-trophy text-2xl ml-4"></i>
|
||||
`;
|
||||
|
||||
document.body.appendChild(toast);
|
||||
|
||||
// Remove toast after 3 seconds
|
||||
setTimeout(() => {
|
||||
toast.remove();
|
||||
}, 5000);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Show achievements display
|
||||
// Show only unlocked achievements
|
||||
function showAchievements() {
|
||||
const achievementsDisplay = document.getElementById('achievementsDisplay');
|
||||
if (achievementsDisplay) {
|
||||
achievementsDisplay.style.display = 'block';
|
||||
const achievementItems = achievementsDisplay.querySelectorAll('.achievement-item');
|
||||
|
||||
achievementItems.forEach(item => {
|
||||
const achievementId = item.querySelector('.achievement-icon').classList[1];
|
||||
const achievement = ACHIEVEMENTS[achievementId.replace('achievement-icon-', '')];
|
||||
if (achievement && unlockedAchievements.has(achievementId.replace('achievement-icon-', ''))) {
|
||||
item.style.display = 'block';
|
||||
// Apply achievement theme
|
||||
Object.entries(achievement.theme).forEach(([key, value]) => {
|
||||
item.classList.add(value);
|
||||
});
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Show achievements display if any achievements are unlocked
|
||||
if (unlockedAchievements.size > 0) {
|
||||
achievementsDisplay.classList.remove('hidden');
|
||||
} else {
|
||||
achievementsDisplay.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Apply highest unlocked achievement theme to the page
|
||||
applyHighestAchievementTheme();
|
||||
}
|
||||
|
||||
// Apply highest unlocked achievement theme
|
||||
function applyHighestAchievementTheme() {
|
||||
const unlocked = Array.from(unlockedAchievements);
|
||||
if (unlocked.length === 0) return;
|
||||
|
||||
// Sort achievements by threshold to find the highest
|
||||
const sortedAchievements = Object.entries(ACHIEVEMENTS)
|
||||
.filter(([id]) => unlocked.includes(id))
|
||||
.sort(([, a], [, b]) => b.threshold - a.threshold);
|
||||
|
||||
const highest = sortedAchievements[0][1];
|
||||
|
||||
// Apply theme to body
|
||||
const body = document.body;
|
||||
// Remove existing theme classes
|
||||
['bg-', 'text-', 'border-', 'hover:'].forEach(prefix => {
|
||||
const classes = Array.from(body.classList).filter(cls => !cls.startsWith(prefix));
|
||||
body.className = classes.join(' ');
|
||||
});
|
||||
|
||||
// Add new theme classes
|
||||
Object.entries(highest.theme).forEach(([key, value]) => {
|
||||
body.classList.add(value);
|
||||
});
|
||||
}
|
||||
|
||||
// Hide achievements display
|
||||
@@ -257,14 +261,11 @@ function initializeAchievements() {
|
||||
`;
|
||||
document.body.appendChild(achievementToast);
|
||||
|
||||
|
||||
// Enable achievements
|
||||
toggleAchievements();
|
||||
achievementsEnabled = true;
|
||||
localStorage.setItem('achievementsEnabled', true);
|
||||
|
||||
// Reset cheat code
|
||||
cheatCodeIndex = 0;
|
||||
|
||||
// Remove achievement toast after 3 seconds
|
||||
// Remove toast after 3 seconds
|
||||
setTimeout(() => {
|
||||
achievementToast.remove();
|
||||
}, 3000);
|
||||
@@ -275,7 +276,7 @@ function initializeAchievements() {
|
||||
}
|
||||
});
|
||||
|
||||
// Check achievements when enabled
|
||||
// Initialize achievements if enabled
|
||||
if (achievementsEnabled) {
|
||||
checkAchievements();
|
||||
showAchievements();
|
||||
|
||||
@@ -22,12 +22,81 @@ import (
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
// Czech day names
|
||||
var czechDayNames = []string{
|
||||
"Pondělí",
|
||||
"Úterý",
|
||||
"Středa",
|
||||
"Čtvrtek",
|
||||
"Pátek",
|
||||
"Sobota",
|
||||
"Neděle",
|
||||
}
|
||||
|
||||
// Format time in Czech style (DD.MM.YYYY HH:mm)
|
||||
func formatCzechTime(t time.Time) string {
|
||||
return t.Format("02.01.2006 15:04")
|
||||
}
|
||||
|
||||
// Format time with Czech day name (DD.MM.YYYY HH:mm - DayName)
|
||||
func formatCzechTimeWithDay(t time.Time) string {
|
||||
day := czechDayNames[t.Weekday()]
|
||||
return fmt.Sprintf("%s - %s", formatCzechTime(t), day)
|
||||
}
|
||||
|
||||
// Detect device type from User-Agent
|
||||
func detectDevice(userAgent string) string {
|
||||
userAgent = strings.ToLower(userAgent)
|
||||
|
||||
// Mobile devices
|
||||
if strings.Contains(userAgent, "iphone") || strings.Contains(userAgent, "ipod") {
|
||||
return "iPhone"
|
||||
} else if strings.Contains(userAgent, "ipad") {
|
||||
return "iPad"
|
||||
} else if strings.Contains(userAgent, "android") {
|
||||
if strings.Contains(userAgent, "mobile") {
|
||||
return "Android Phone"
|
||||
}
|
||||
return "Android Tablet"
|
||||
} else if strings.Contains(userAgent, "windows phone") {
|
||||
return "Windows Phone"
|
||||
}
|
||||
|
||||
// Desktop devices
|
||||
if strings.Contains(userAgent, "windows") {
|
||||
return "Windows PC"
|
||||
} else if strings.Contains(userAgent, "macintosh") || strings.Contains(userAgent, "mac os x") {
|
||||
return "Mac"
|
||||
} else if strings.Contains(userAgent, "linux") {
|
||||
return "Linux PC"
|
||||
}
|
||||
|
||||
// Other devices
|
||||
if strings.Contains(userAgent, "playstation") {
|
||||
return "PlayStation"
|
||||
} else if strings.Contains(userAgent, "xbox") {
|
||||
return "Xbox"
|
||||
} else if strings.Contains(userAgent, "nintendo") {
|
||||
return "Nintendo"
|
||||
}
|
||||
|
||||
// Bots and crawlers
|
||||
if strings.Contains(userAgent, "bot") || strings.Contains(userAgent, "crawler") {
|
||||
return "Bot/Crawler"
|
||||
}
|
||||
|
||||
return "Unknown Device"
|
||||
}
|
||||
|
||||
type Visitor struct {
|
||||
FirstVisit time.Time `json:"first_visit"`
|
||||
LastVisit time.Time `json:"last_visit"`
|
||||
Visits int `json:"visits"`
|
||||
IP string `json:"ip"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
Device string `json:"device"`
|
||||
Browser string `json:"browser"`
|
||||
OS string `json:"os"`
|
||||
}
|
||||
|
||||
type VisitorStats struct {
|
||||
@@ -51,6 +120,60 @@ type VisitorStats struct {
|
||||
ReferrerStats map[string]int `json:"referrer_stats"`
|
||||
}
|
||||
|
||||
// Format VisitorStats with Czech dates
|
||||
func (v *VisitorStats) FormatForDisplay() map[string]interface{} {
|
||||
displayStats := map[string]interface{}{
|
||||
"total_visits": v.TotalVisits,
|
||||
"today_visits": v.TodayVisits,
|
||||
"last_visit": formatCzechTimeWithDay(v.LastVisit),
|
||||
"monthly_visits": v.MonthlyVisits,
|
||||
"weekly_visits": v.WeeklyVisits,
|
||||
"last_updated": formatCzechTimeWithDay(v.LastUpdated),
|
||||
"unique_visitors": make(map[string]interface{}),
|
||||
"most_active_hours": make([]map[string]interface{}, len(v.MostActiveHours)),
|
||||
"most_active_days": make([]map[string]interface{}, len(v.MostActiveDays)),
|
||||
"browser_stats": v.BrowserStats,
|
||||
"os_stats": v.OSStats,
|
||||
"referrer_stats": v.ReferrerStats,
|
||||
}
|
||||
|
||||
// Format unique visitors
|
||||
for id, visitor := range v.UniqueVisitors {
|
||||
displayStats["unique_visitors"].(map[string]interface{})[id] = map[string]interface{}{
|
||||
"first_visit": formatCzechTimeWithDay(visitor.FirstVisit),
|
||||
"last_visit": formatCzechTimeWithDay(visitor.LastVisit),
|
||||
"visits": visitor.Visits,
|
||||
"ip": visitor.IP,
|
||||
"user_agent": visitor.UserAgent,
|
||||
}
|
||||
}
|
||||
|
||||
// Format most active hours
|
||||
for i, hour := range v.MostActiveHours {
|
||||
displayStats["most_active_hours"].([]map[string]interface{})[i] = map[string]interface{}{
|
||||
"hour": hour.Hour,
|
||||
"count": hour.Count,
|
||||
}
|
||||
}
|
||||
|
||||
// Format most active days with Czech names
|
||||
for i, day := range v.MostActiveDays {
|
||||
weekday := time.Weekday(0) // Start from Monday
|
||||
for _, czechDay := range czechDayNames {
|
||||
if day.Day == weekday.String() {
|
||||
displayStats["most_active_days"].([]map[string]interface{})[i] = map[string]interface{}{
|
||||
"day": czechDay,
|
||||
"count": day.Count,
|
||||
}
|
||||
break
|
||||
}
|
||||
weekday++
|
||||
}
|
||||
}
|
||||
|
||||
return displayStats
|
||||
}
|
||||
|
||||
// Initialize VisitorStats with proper struct types
|
||||
func (v *VisitorStats) init() {
|
||||
if v.TotalVisits == 0 {
|
||||
@@ -163,6 +286,11 @@ func trackVisit(w http.ResponseWriter, r *http.Request) {
|
||||
ip := r.RemoteAddr
|
||||
userAgent := r.UserAgent()
|
||||
|
||||
// Detect device information
|
||||
device := detectDevice(userAgent)
|
||||
browser := detectBrowser(userAgent)
|
||||
os := detectOS(userAgent)
|
||||
|
||||
// Update visit counts
|
||||
stats.TotalVisits++
|
||||
stats.TodayVisits++
|
||||
@@ -191,6 +319,9 @@ func trackVisit(w http.ResponseWriter, r *http.Request) {
|
||||
Visits: 1,
|
||||
IP: ip,
|
||||
UserAgent: userAgent,
|
||||
Device: device,
|
||||
Browser: browser,
|
||||
OS: os,
|
||||
}
|
||||
} else {
|
||||
visitor := stats.UniqueVisitors[visitorId]
|
||||
@@ -199,20 +330,24 @@ func trackVisit(w http.ResponseWriter, r *http.Request) {
|
||||
stats.UniqueVisitors[visitorId] = visitor
|
||||
}
|
||||
|
||||
// Update browser stats
|
||||
browser := detectBrowser(userAgent)
|
||||
// Update device stats
|
||||
if stats.BrowserStats == nil {
|
||||
stats.BrowserStats = make(map[string]int)
|
||||
}
|
||||
stats.BrowserStats[browser]++
|
||||
|
||||
// Update OS stats
|
||||
os := detectOS(userAgent)
|
||||
if stats.OSStats == nil {
|
||||
stats.OSStats = make(map[string]int)
|
||||
}
|
||||
stats.OSStats[os]++
|
||||
|
||||
// Update device stats
|
||||
if stats.ReferrerStats == nil {
|
||||
stats.ReferrerStats = make(map[string]int)
|
||||
}
|
||||
stats.ReferrerStats[device]++
|
||||
|
||||
// Update referrer stats
|
||||
referrer := r.Referer()
|
||||
if referrer != "" {
|
||||
|
||||
Reference in New Issue
Block a user