Memory update: Feed Hunter live, email setup, control panel building
This commit is contained in:
Binary file not shown.
@ -0,0 +1,50 @@
|
||||
----------------------------------------
|
||||
Exception occurred during processing of request from ('127.0.0.1', 45572)
|
||||
Traceback (most recent call last):
|
||||
File "/usr/lib/python3.12/socketserver.py", line 318, in _handle_request_noblock
|
||||
self.process_request(request, client_address)
|
||||
File "/usr/lib/python3.12/socketserver.py", line 349, in process_request
|
||||
self.finish_request(request, client_address)
|
||||
File "/usr/lib/python3.12/socketserver.py", line 362, in finish_request
|
||||
self.RequestHandlerClass(request, client_address, self)
|
||||
File "/usr/lib/python3.12/socketserver.py", line 761, in __init__
|
||||
self.handle()
|
||||
File "/usr/lib/python3.12/http/server.py", line 436, in handle
|
||||
self.handle_one_request()
|
||||
File "/usr/lib/python3.12/http/server.py", line 424, in handle_one_request
|
||||
method()
|
||||
File "/home/wdjones/.openclaw/workspace/projects/feed-hunter/portal/server.py", line 37, in do_GET
|
||||
self.serve_simulations()
|
||||
File "/home/wdjones/.openclaw/workspace/projects/feed-hunter/portal/server.py", line 243, in serve_simulations
|
||||
{self.render_trade_history(sims.get('history', []))}
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/wdjones/.openclaw/workspace/projects/feed-hunter/portal/server.py", line 748, in render_trade_history
|
||||
for trade in history[-10:]: # Last 10 trades
|
||||
~~~~~~~^^^^^^
|
||||
KeyError: slice(-10, None, None)
|
||||
----------------------------------------
|
||||
----------------------------------------
|
||||
Exception occurred during processing of request from ('127.0.0.1', 48354)
|
||||
Traceback (most recent call last):
|
||||
File "/usr/lib/python3.12/socketserver.py", line 318, in _handle_request_noblock
|
||||
self.process_request(request, client_address)
|
||||
File "/usr/lib/python3.12/socketserver.py", line 349, in process_request
|
||||
self.finish_request(request, client_address)
|
||||
File "/usr/lib/python3.12/socketserver.py", line 362, in finish_request
|
||||
self.RequestHandlerClass(request, client_address, self)
|
||||
File "/usr/lib/python3.12/socketserver.py", line 761, in __init__
|
||||
self.handle()
|
||||
File "/usr/lib/python3.12/http/server.py", line 436, in handle
|
||||
self.handle_one_request()
|
||||
File "/usr/lib/python3.12/http/server.py", line 424, in handle_one_request
|
||||
method()
|
||||
File "/home/wdjones/.openclaw/workspace/projects/feed-hunter/portal/server.py", line 37, in do_GET
|
||||
self.serve_simulations()
|
||||
File "/home/wdjones/.openclaw/workspace/projects/feed-hunter/portal/server.py", line 243, in serve_simulations
|
||||
{self.render_trade_history(sims.get('history', []))}
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/home/wdjones/.openclaw/workspace/projects/feed-hunter/portal/server.py", line 748, in render_trade_history
|
||||
for trade in history[-10:]: # Last 10 trades
|
||||
~~~~~~~^^^^^^
|
||||
KeyError: slice(-10, None, None)
|
||||
----------------------------------------
|
||||
|
||||
@ -9,37 +9,54 @@ import os
|
||||
import glob
|
||||
from datetime import datetime, timezone
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from socketserver import ThreadingMixIn
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
|
||||
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
||||
daemon_threads = True
|
||||
import re
|
||||
|
||||
# Configuration
|
||||
PORT = 8888
|
||||
DATA_DIR = "../data"
|
||||
SKILLS_DIR = "../../skills/deep-scraper/scripts"
|
||||
_PORTAL_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
_PROJECT_DIR = os.path.dirname(_PORTAL_DIR)
|
||||
DATA_DIR = os.path.join(_PROJECT_DIR, "data")
|
||||
SKILLS_DIR = os.path.join(os.path.dirname(_PROJECT_DIR), "skills", "deep-scraper", "scripts")
|
||||
X_FEED_DIR = os.path.join(os.path.dirname(_PROJECT_DIR), "..", "data", "x-feed")
|
||||
|
||||
class FeedHunterHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
parsed_path = urlparse(self.path)
|
||||
path = parsed_path.path
|
||||
query = parse_qs(parsed_path.query)
|
||||
|
||||
if path == '/' or path == '/dashboard':
|
||||
self.serve_dashboard()
|
||||
elif path == '/feed':
|
||||
self.serve_feed_view()
|
||||
elif path == '/investigations':
|
||||
self.serve_investigations()
|
||||
elif path == '/simulations':
|
||||
self.serve_simulations()
|
||||
elif path == '/status':
|
||||
self.serve_status()
|
||||
elif path == '/api/data':
|
||||
self.serve_api_data(query.get('type', [''])[0])
|
||||
elif path.startswith('/static/'):
|
||||
self.serve_static(path)
|
||||
else:
|
||||
self.send_error(404)
|
||||
try:
|
||||
parsed_path = urlparse(self.path)
|
||||
path = parsed_path.path
|
||||
query = parse_qs(parsed_path.query)
|
||||
|
||||
if path == '/' or path == '/dashboard':
|
||||
self.serve_dashboard()
|
||||
elif path == '/feed':
|
||||
self.serve_feed_view()
|
||||
elif path == '/investigations':
|
||||
self.serve_investigations()
|
||||
elif path == '/simulations':
|
||||
self.serve_simulations()
|
||||
elif path == '/status':
|
||||
self.serve_status()
|
||||
elif path == '/api/data':
|
||||
self.serve_api_data(query.get('type', [''])[0])
|
||||
elif path.startswith('/static/'):
|
||||
self.serve_static(path)
|
||||
else:
|
||||
self.send_error(404)
|
||||
except Exception as e:
|
||||
try:
|
||||
self.send_response(500)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(f"<h1>500 Error</h1><pre>{e}</pre>".encode())
|
||||
except:
|
||||
pass
|
||||
|
||||
def serve_dashboard(self):
|
||||
"""Main dashboard overview"""
|
||||
@ -237,7 +254,7 @@ class FeedHunterHandler(BaseHTTPRequestHandler):
|
||||
<div class="card">
|
||||
<h3>Trade History</h3>
|
||||
<div class="trade-history">
|
||||
{self.render_trade_history(sims.get('history', []))}
|
||||
{self.render_trade_history(sims.get('history', {}).get('closed', []) if isinstance(sims.get('history'), dict) else sims.get('history', []))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -425,7 +442,7 @@ class FeedHunterHandler(BaseHTTPRequestHandler):
|
||||
posts = []
|
||||
try:
|
||||
# Find latest x-feed directory
|
||||
x_feed_pattern = os.path.join("../../data/x-feed", "20*")
|
||||
x_feed_pattern = os.path.join(X_FEED_DIR, "20*")
|
||||
x_feed_dirs = sorted(glob.glob(x_feed_pattern))
|
||||
|
||||
if x_feed_dirs:
|
||||
@ -526,7 +543,7 @@ class FeedHunterHandler(BaseHTTPRequestHandler):
|
||||
}
|
||||
|
||||
# Check for recent pipeline runs
|
||||
x_feed_pattern = os.path.join("../../data/x-feed", "20*")
|
||||
x_feed_pattern = os.path.join(X_FEED_DIR, "20*")
|
||||
x_feed_dirs = sorted(glob.glob(x_feed_pattern))
|
||||
if x_feed_dirs:
|
||||
latest = os.path.basename(x_feed_dirs[-1])
|
||||
@ -592,7 +609,7 @@ class FeedHunterHandler(BaseHTTPRequestHandler):
|
||||
return html
|
||||
|
||||
def render_investigations(self, investigations):
|
||||
"""Render investigation reports"""
|
||||
"""Render investigation reports with rich links"""
|
||||
if not investigations:
|
||||
return '<div class="empty-state">No investigations found</div>'
|
||||
|
||||
@ -601,21 +618,81 @@ class FeedHunterHandler(BaseHTTPRequestHandler):
|
||||
investigation = inv.get('investigation', {})
|
||||
verdict = investigation.get('verdict', 'Unknown')
|
||||
risk_score = investigation.get('risk_assessment', {}).get('score', 0)
|
||||
risk_notes = investigation.get('risk_assessment', {}).get('notes', [])
|
||||
source = inv.get('source_post', {})
|
||||
verified = investigation.get('verified_data', {})
|
||||
claim_vs = investigation.get('claim_vs_reality', {})
|
||||
profile_url = investigation.get('profile_url', '')
|
||||
strategy_notes = investigation.get('strategy_notes', '')
|
||||
suggested = inv.get('suggested_simulation', {})
|
||||
|
||||
verdict_class = 'verified' if 'VERIFIED' in verdict else 'failed'
|
||||
|
||||
# Build links section
|
||||
links_html = '<div class="investigation-links">'
|
||||
if source.get('url'):
|
||||
links_html += f'<a href="{source["url"]}" target="_blank" class="inv-link">📝 Original Post</a>'
|
||||
if source.get('author'):
|
||||
author = source["author"].replace("@", "")
|
||||
links_html += f'<a href="https://x.com/{author}" target="_blank" class="inv-link">🐦 {source["author"]} on X</a>'
|
||||
if profile_url:
|
||||
links_html += f'<a href="{profile_url}" target="_blank" class="inv-link">👤 Polymarket Profile</a>'
|
||||
# Extract wallet if present in the investigation data
|
||||
wallet = inv.get('investigation', {}).get('wallet_address', '')
|
||||
if not wallet:
|
||||
# Try to find it in verified data or elsewhere
|
||||
for key, val in verified.items():
|
||||
if isinstance(val, str) and val.startswith('0x'):
|
||||
wallet = val
|
||||
break
|
||||
if wallet:
|
||||
links_html += f'<a href="https://polygonscan.com/address/{wallet}" target="_blank" class="inv-link">🔗 Wallet on Polygonscan</a>'
|
||||
links_html += '</div>'
|
||||
|
||||
# Build verified data section
|
||||
verified_html = ''
|
||||
if verified:
|
||||
verified_html = '<div class="investigation-verified"><h4>Verified Data</h4><div class="verified-grid">'
|
||||
for key, val in verified.items():
|
||||
label = key.replace('_', ' ').title()
|
||||
verified_html += f'<div class="verified-item"><span class="verified-label">{label}</span><span class="verified-value">{val}</span></div>'
|
||||
verified_html += '</div></div>'
|
||||
|
||||
# Build claim vs reality section
|
||||
claim_html = ''
|
||||
if claim_vs:
|
||||
claim_html = '<div class="investigation-claims"><h4>Claim vs Reality</h4>'
|
||||
for key, val in claim_vs.items():
|
||||
label = key.replace('_', ' ').title()
|
||||
claim_html += f'<div class="claim-row"><span class="claim-label">{label}</span><span class="claim-value">{val}</span></div>'
|
||||
claim_html += '</div>'
|
||||
|
||||
# Risk notes
|
||||
risk_html = ''
|
||||
if risk_notes:
|
||||
risk_html = '<div class="investigation-risk"><h4>Risk Assessment</h4><ul>'
|
||||
for note in risk_notes:
|
||||
risk_html += f'<li>{note}</li>'
|
||||
risk_html += '</ul></div>'
|
||||
|
||||
# Strategy notes
|
||||
strategy_html = ''
|
||||
if strategy_notes:
|
||||
strategy_html = f'<div class="investigation-strategy"><h4>Strategy Notes</h4><p>{strategy_notes}</p></div>'
|
||||
|
||||
html += f"""
|
||||
<div class="investigation-item">
|
||||
<div class="investigation-header">
|
||||
<div class="investigation-author">{source.get('author', 'Unknown')}</div>
|
||||
<div class="investigation-verdict {verdict_class}">{verdict}</div>
|
||||
</div>
|
||||
<div class="investigation-claim">{source.get('claim', 'No claim')}</div>
|
||||
<div class="investigation-score">Risk Score: {risk_score}/10</div>
|
||||
<div class="investigation-actions">
|
||||
<button onclick="showInvestigationDetail('{inv.get('id', '')}')">View Details</button>
|
||||
</div>
|
||||
<div class="investigation-claim">"{source.get('claim', 'No claim')}"</div>
|
||||
{links_html}
|
||||
{verified_html}
|
||||
{claim_html}
|
||||
<div class="investigation-score">Risk Score: <strong>{risk_score}/10</strong></div>
|
||||
{risk_html}
|
||||
{strategy_html}
|
||||
</div>
|
||||
"""
|
||||
|
||||
@ -980,6 +1057,110 @@ body {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.investigation-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin: 0.75rem 0;
|
||||
}
|
||||
|
||||
.inv-link {
|
||||
display: inline-block;
|
||||
padding: 0.35rem 0.75rem;
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--accent-blue);
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
border: 1px solid var(--border-color);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.inv-link:hover {
|
||||
background: var(--border-color);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.investigation-verified, .investigation-claims, .investigation-risk, .investigation-strategy {
|
||||
margin: 1rem 0;
|
||||
padding: 1rem;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.investigation-verified h4, .investigation-claims h4, .investigation-risk h4, .investigation-strategy h4 {
|
||||
color: var(--accent-blue);
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.verified-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.verified-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0.5rem;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.verified-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.verified-value {
|
||||
color: var(--text-primary);
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.claim-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.4rem 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.claim-row:last-child { border-bottom: none; }
|
||||
|
||||
.claim-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.claim-value {
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9rem;
|
||||
text-align: right;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
.investigation-risk ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.investigation-risk li {
|
||||
padding: 0.3rem 0;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.investigation-risk li::before {
|
||||
content: "⚠️ ";
|
||||
}
|
||||
|
||||
.investigation-strategy p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Positions */
|
||||
.position-item {
|
||||
background: var(--bg-tertiary);
|
||||
@ -1252,7 +1433,7 @@ def main():
|
||||
print("")
|
||||
|
||||
try:
|
||||
server = HTTPServer(('localhost', PORT), FeedHunterHandler)
|
||||
server = ThreadedHTTPServer(('0.0.0.0', PORT), FeedHunterHandler)
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
print("\n🛑 Portal stopped")
|
||||
|
||||
Reference in New Issue
Block a user