262 lines
8.7 KiB
Python
262 lines
8.7 KiB
Python
"""
|
|
EU-Utility - Auction Tracker Plugin
|
|
|
|
Track auction prices and market trends.
|
|
"""
|
|
|
|
import json
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
|
|
from PyQt6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
QPushButton, QTableWidget, QTableWidgetItem,
|
|
QLineEdit, QComboBox, QFrame
|
|
)
|
|
from PyQt6.QtCore import Qt
|
|
|
|
from plugins.base_plugin import BasePlugin
|
|
from core.icon_manager import get_icon_manager
|
|
from PyQt6.QtGui import QIcon
|
|
|
|
|
|
class AuctionTrackerPlugin(BasePlugin):
|
|
"""Track auction prices and analyze market trends."""
|
|
|
|
name = "Auction Tracker"
|
|
version = "1.0.0"
|
|
author = "ImpulsiveFPS"
|
|
description = "Track prices, markups, and market trends"
|
|
hotkey = "ctrl+shift+a"
|
|
|
|
def initialize(self):
|
|
"""Setup auction tracker."""
|
|
self.data_file = Path("data/auction_tracker.json")
|
|
self.data_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
self.price_history = defaultdict(list)
|
|
self.watchlist = []
|
|
|
|
self._load_data()
|
|
|
|
def _load_data(self):
|
|
"""Load auction data."""
|
|
if self.data_file.exists():
|
|
try:
|
|
with open(self.data_file, 'r') as f:
|
|
data = json.load(f)
|
|
self.price_history = defaultdict(list, data.get('prices', {}))
|
|
self.watchlist = data.get('watchlist', [])
|
|
except:
|
|
pass
|
|
|
|
def _save_data(self):
|
|
"""Save auction data."""
|
|
with open(self.data_file, 'w') as f:
|
|
json.dump({
|
|
'prices': dict(self.price_history),
|
|
'watchlist': self.watchlist
|
|
}, f, indent=2)
|
|
|
|
def get_ui(self):
|
|
"""Create auction tracker UI."""
|
|
widget = QWidget()
|
|
widget.setStyleSheet("background: transparent;")
|
|
layout = QVBoxLayout(widget)
|
|
layout.setSpacing(15)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
# Get icon manager
|
|
icon_mgr = get_icon_manager()
|
|
|
|
# Title with icon
|
|
title_layout = QHBoxLayout()
|
|
|
|
title_icon = QLabel()
|
|
icon_pixmap = icon_mgr.get_pixmap('trending-up', size=20)
|
|
title_icon.setPixmap(icon_pixmap)
|
|
title_icon.setFixedSize(20, 20)
|
|
title_layout.addWidget(title_icon)
|
|
|
|
title = QLabel("Auction Tracker")
|
|
title.setStyleSheet("color: white; font-size: 16px; font-weight: bold;")
|
|
title_layout.addWidget(title)
|
|
title_layout.addStretch()
|
|
|
|
layout.addLayout(title_layout)
|
|
|
|
# Search
|
|
search_layout = QHBoxLayout()
|
|
|
|
self.search_input = QLineEdit()
|
|
self.search_input.setPlaceholderText("Search item...")
|
|
self.search_input.setStyleSheet("""
|
|
QLineEdit {
|
|
background-color: rgba(30, 35, 45, 200);
|
|
color: white;
|
|
border: 1px solid rgba(100, 110, 130, 80);
|
|
border-radius: 4px;
|
|
padding: 8px;
|
|
}
|
|
""")
|
|
search_layout.addWidget(self.search_input)
|
|
|
|
search_btn = QPushButton()
|
|
search_pixmap = icon_mgr.get_pixmap('search', size=16)
|
|
search_btn.setIcon(QIcon(search_pixmap))
|
|
search_btn.setIconSize(Qt.QSize(16, 16))
|
|
search_btn.setFixedSize(32, 32)
|
|
search_btn.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #ff8c42;
|
|
border: none;
|
|
border-radius: 4px;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #ffa060;
|
|
}
|
|
""")
|
|
search_btn.clicked.connect(self._search_item)
|
|
search_layout.addWidget(search_btn)
|
|
|
|
layout.addLayout(search_layout)
|
|
|
|
# Price table
|
|
self.price_table = QTableWidget()
|
|
self.price_table.setColumnCount(6)
|
|
self.price_table.setHorizontalHeaderLabels(["Item", "Bid", "Buyout", "Markup", "Trend", "Time"])
|
|
self.price_table.setStyleSheet("""
|
|
QTableWidget {
|
|
background-color: rgba(30, 35, 45, 200);
|
|
color: white;
|
|
border: 1px solid rgba(100, 110, 130, 80);
|
|
border-radius: 6px;
|
|
}
|
|
QHeaderView::section {
|
|
background-color: rgba(35, 40, 55, 200);
|
|
color: rgba(255,255,255,180);
|
|
padding: 8px;
|
|
font-weight: bold;
|
|
font-size: 11px;
|
|
}
|
|
""")
|
|
self.price_table.horizontalHeader().setStretchLastSection(True)
|
|
layout.addWidget(self.price_table)
|
|
|
|
# Quick scan
|
|
scan_btn = QPushButton("Scan Auction Window")
|
|
scan_btn.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #ffc107;
|
|
color: black;
|
|
padding: 12px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
font-weight: bold;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #ffd54f;
|
|
}
|
|
""")
|
|
scan_btn.clicked.connect(self._scan_auction)
|
|
layout.addWidget(scan_btn)
|
|
|
|
# Sample data
|
|
self._add_sample_data()
|
|
|
|
layout.addStretch()
|
|
return widget
|
|
|
|
def _add_sample_data(self):
|
|
"""Add sample auction data."""
|
|
sample_items = [
|
|
{"name": "Nanocube", "bid": 304.00, "buyout": 304.00, "markup": 101.33},
|
|
{"name": "Aakas Plating", "bid": 154.00, "buyout": 159.00, "markup": 102.67},
|
|
{"name": "Wenrex Ingot", "bid": 111.00, "buyout": 118.00, "markup": 108.82},
|
|
]
|
|
|
|
self.price_table.setRowCount(len(sample_items))
|
|
for i, item in enumerate(sample_items):
|
|
self.price_table.setItem(i, 0, QTableWidgetItem(item['name']))
|
|
self.price_table.setItem(i, 1, QTableWidgetItem(f"{item['bid']:.2f}"))
|
|
self.price_table.setItem(i, 2, QTableWidgetItem(f"{item['buyout']:.2f}"))
|
|
|
|
markup_item = QTableWidgetItem(f"{item['markup']:.2f}%")
|
|
if item['markup'] > 105:
|
|
markup_item.setForeground(Qt.GlobalColor.red)
|
|
elif item['markup'] < 102:
|
|
markup_item.setForeground(Qt.GlobalColor.green)
|
|
self.price_table.setItem(i, 3, markup_item)
|
|
|
|
self.price_table.setItem(i, 4, QTableWidgetItem("→"))
|
|
self.price_table.setItem(i, 5, QTableWidgetItem("2m ago"))
|
|
|
|
def _search_item(self):
|
|
"""Search for item price history."""
|
|
query = self.search_input.text().lower()
|
|
# TODO: Implement search
|
|
|
|
def _scan_auction(self):
|
|
"""Scan auction window with OCR."""
|
|
# TODO: Implement OCR scanning
|
|
pass
|
|
|
|
def record_price(self, item_name, bid, buyout, tt_value=None):
|
|
"""Record a price observation."""
|
|
entry = {
|
|
'time': datetime.now().isoformat(),
|
|
'bid': bid,
|
|
'buyout': buyout,
|
|
'tt': tt_value,
|
|
'markup': (buyout / tt_value * 100) if tt_value else None
|
|
}
|
|
|
|
self.price_history[item_name].append(entry)
|
|
|
|
# Keep last 100 entries per item
|
|
if len(self.price_history[item_name]) > 100:
|
|
self.price_history[item_name] = self.price_history[item_name][-100:]
|
|
|
|
self._save_data()
|
|
|
|
def get_price_trend(self, item_name, days=7):
|
|
"""Get price trend for an item."""
|
|
history = self.price_history.get(item_name, [])
|
|
if not history:
|
|
return None
|
|
|
|
cutoff = (datetime.now() - timedelta(days=days)).isoformat()
|
|
recent = [h for h in history if h['time'] > cutoff]
|
|
|
|
if len(recent) < 2:
|
|
return None
|
|
|
|
prices = [h['buyout'] for h in recent]
|
|
return {
|
|
'current': prices[-1],
|
|
'average': sum(prices) / len(prices),
|
|
'min': min(prices),
|
|
'max': max(prices),
|
|
'trend': 'up' if prices[-1] > prices[0] else 'down' if prices[-1] < prices[0] else 'stable'
|
|
}
|
|
|
|
def get_deals(self, max_markup=102.0):
|
|
"""Find items below market price."""
|
|
deals = []
|
|
for item_name, history in self.price_history.items():
|
|
if not history:
|
|
continue
|
|
|
|
latest = history[-1]
|
|
markup = latest.get('markup')
|
|
|
|
if markup and markup <= max_markup:
|
|
deals.append({
|
|
'item': item_name,
|
|
'price': latest['buyout'],
|
|
'markup': markup
|
|
})
|
|
|
|
return sorted(deals, key=lambda x: x['markup'])
|