├── README.md └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # StalcraftMarketAnalysis -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | from datetime import datetime 4 | from statistics import mean 5 | from typing import Dict, List 6 | from collections import defaultdict 7 | from colorama import init, Fore, Style 8 | from tqdm import tqdm 9 | import time 10 | import sys 11 | 12 | # Initialize colorama for Windows compatibility 13 | init() 14 | 15 | def loading_animation(text): 16 | chars = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" 17 | for _ in range(10): 18 | for char in chars: 19 | sys.stdout.write(f'\r{Fore.CYAN}{char} {text}...{Style.RESET_ALL}') 20 | sys.stdout.flush() 21 | time.sleep(0.1) 22 | 23 | def fetch_auction_data() -> dict: 24 | print(f"\n{Fore.CYAN}📊 STALCRAFT Market Analyzer{Style.RESET_ALL}") 25 | print("=" * 50) 26 | 27 | loading_animation("Fetching market data") 28 | 29 | url = "http://xppai.io/stalcraft/a.json" 30 | headers = { 31 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' 32 | } 33 | response = requests.get(url, headers=headers) 34 | response.raise_for_status() 35 | print(f"\r{Fore.GREEN}✓ Data fetched successfully!{Style.RESET_ALL}") 36 | return response.json() 37 | 38 | def format_price(price): 39 | return f"{price:,.0f}" 40 | 41 | def get_quality_name(qlt): 42 | quality_names = { 43 | 0: "Common", 44 | 1: "Uncommon", 45 | 2: "Special", 46 | 3: "Rare", 47 | 4: "Legendary", 48 | 5: "Exclusive" 49 | } 50 | return quality_names.get(qlt, "Unknown") 51 | 52 | def analyze_and_display_prices(data: dict) -> dict: 53 | print(f"\n{Fore.YELLOW}Processing {len(data['prices'])} market entries...{Style.RESET_ALL}") 54 | 55 | # Group prices by quality 56 | quality_prices = defaultdict(list) 57 | skipped_count = 0 58 | 59 | for item in tqdm(data['prices'], desc="Analyzing prices", ncols=75): 60 | additional = item.get('additional', {}) 61 | 62 | if 'bonus_properties' in additional: 63 | skipped_count += 1 64 | continue 65 | 66 | qlt = additional.get('qlt') 67 | if qlt is not None: 68 | quality_prices[qlt].append(item['price']) 69 | 70 | # Prepare analysis results 71 | analysis_results = { 72 | "quality_analysis": {}, 73 | "market_summary": { 74 | "total_items": len(data['prices']), 75 | "items_with_bonus": skipped_count 76 | }, 77 | "recent_activity": [] 78 | } 79 | 80 | # Display and store analysis by quality 81 | print(f"\n{Fore.CYAN}📈 Shard Price Analysis by Quality Level{Style.RESET_ALL}") 82 | print("=" * 50) 83 | 84 | quality_colors = { 85 | 0: Fore.WHITE, # Common 86 | 1: Fore.GREEN, # Uncommon 87 | 2: Fore.BLUE, # Special 88 | 3: Fore.MAGENTA, # Rare 89 | 4: Fore.YELLOW, # Legendary 90 | 5: Fore.RED # Exclusive 91 | } 92 | 93 | for qlt in sorted(quality_prices.keys()): 94 | prices = quality_prices[qlt] 95 | if not prices: 96 | continue 97 | 98 | color = quality_colors.get(qlt, Fore.WHITE) 99 | avg_price = mean(prices) 100 | min_price = min(prices) 101 | max_price = max(prices) 102 | 103 | # Display in console 104 | print(f"\n{color}🏷️ {get_quality_name(qlt)} (Quality {qlt}):{Style.RESET_ALL}") 105 | print(f"{' └ Average Price:':<20} {Fore.GREEN}{format_price(avg_price)}₽{Style.RESET_ALL}") 106 | print(f"{' └ Minimum Price:':<20} {Fore.BLUE}{format_price(min_price)}₽{Style.RESET_ALL}") 107 | print(f"{' └ Maximum Price:':<20} {Fore.RED}{format_price(max_price)}₽{Style.RESET_ALL}") 108 | print(f"{' └ Number of items:':<20} {len(prices)}") 109 | print(f"\n{' 💡 Buy Recommendations:'}") 110 | print(f"{' └ Standard:':<20} {Fore.YELLOW}{format_price(avg_price * 0.9)}₽{Style.RESET_ALL} (10% below avg)") 111 | print(f"{' └ Bargain:':<20} {Fore.GREEN}{format_price(min_price * 1.1)}₽{Style.RESET_ALL} (10% above min)") 112 | 113 | # Store in results 114 | analysis_results["quality_analysis"][get_quality_name(qlt)] = { 115 | "quality_level": qlt, 116 | "average_price": round(avg_price), 117 | "minimum_price": round(min_price), 118 | "maximum_price": round(max_price), 119 | "item_count": len(prices), 120 | "buy_recommendations": { 121 | "standard": round(avg_price * 0.9), 122 | "bargain": round(min_price * 1.1) 123 | } 124 | } 125 | 126 | # Display and store market summary 127 | print(f"\n{Fore.CYAN}📊 Market Summary{Style.RESET_ALL}") 128 | print("=" * 50) 129 | print(f"{'Total items analyzed:':<20} {len(data['prices'])}") 130 | print(f"{'Items with bonus:':<20} {skipped_count}") 131 | 132 | # Display and store recent activity 133 | print(f"\n{Fore.CYAN}🕒 Recent Market Activity{Style.RESET_ALL}") 134 | print("=" * 50) 135 | recent_items = sorted(data['prices'], key=lambda x: x['time'], reverse=True)[:5] 136 | 137 | for item in recent_items: 138 | additional = item.get('additional', {}) 139 | time = datetime.fromisoformat(item['time'].replace('Z', '+00:00')) 140 | qlt = additional.get('qlt') 141 | color = quality_colors.get(qlt, Fore.WHITE) if qlt else Fore.WHITE 142 | 143 | quality_name = get_quality_name(qlt) if qlt is not None else "" 144 | quality_text = f"({quality_name})" if quality_name else "" 145 | bonus_info = f"{Fore.YELLOW}[Has Bonus]{Style.RESET_ALL}" if 'bonus_properties' in additional else "" 146 | 147 | # Display in console 148 | print(f"{Fore.BLUE}{time.strftime('%Y-%m-%d %H:%M:%S')}{Style.RESET_ALL} - " 149 | f"{color}{format_price(item['price'])}₽{Style.RESET_ALL} {quality_text} {bonus_info}") 150 | 151 | # Store in results 152 | analysis_results["recent_activity"].append({ 153 | "time": item['time'], 154 | "price": item['price'], 155 | "quality": get_quality_name(qlt) if qlt is not None else None, 156 | "has_bonus": 'bonus_properties' in additional 157 | }) 158 | 159 | return analysis_results 160 | 161 | def export_to_json(analysis_results: dict, filename: str = "market_analysis.json") -> None: 162 | with open(filename, 'w') as f: 163 | json.dump(analysis_results, f, separators=(',', ':')) 164 | print(f"\n{Fore.GREEN}✓ Analysis exported to {filename}{Style.RESET_ALL}") 165 | 166 | def main(): 167 | try: 168 | data = fetch_auction_data() 169 | analysis_results = analyze_and_display_prices(data) 170 | export_to_json(analysis_results) 171 | print(f"\n{Fore.GREEN}✓ Analysis complete! XppaiCyber{Style.RESET_ALL}") 172 | except requests.RequestException as e: 173 | print(f"\n{Fore.RED}✗ Error fetching data: {e}{Style.RESET_ALL}") 174 | except Exception as e: 175 | print(f"\n{Fore.RED}✗ Error analyzing data: {e}{Style.RESET_ALL}") 176 | import traceback 177 | print(traceback.format_exc()) 178 | 179 | if __name__ == "__main__": 180 | main() --------------------------------------------------------------------------------