File size: 5,744 Bytes
bde2f3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env python3
"""Auto-Research Agent β€” daily crypto research report. Cron at 7am.
Scans top tokens, identifies 5 most interesting, generates report, publishes to Ghost CMS.
Fully automated: LLM writes β†’ LLM reviews β†’ publishes."""

import asyncio
import json
import os
from datetime import UTC, datetime

import httpx

BACKEND = os.getenv("BACKEND_URL", "http://localhost:8000")
GHOST_URL = os.getenv("GHOST_URL", "http://localhost:2368")
GHOST_KEY = os.getenv("GHOST_ADMIN_KEY", "")
RMI_KEY = os.getenv("RMI_INTERNAL_KEY", "rmi-internal-2026")
OLLAMA = os.getenv("OLLAMA_HOST", "http://localhost:11434")


async def fetch_market_data() -> dict:
    """Gather all market data for the report."""
    async with httpx.AsyncClient(timeout=30) as c:
        # Top tokens
        trending = await c.get(f"{BACKEND}/api/v1/databus/fetch/trending?limit=20", headers={"X-RMI-Key": RMI_KEY})
        # Market overview
        market = await c.get(f"{BACKEND}/api/v1/databus/fetch/market_overview", headers={"X-RMI-Key": RMI_KEY})
        # Fear & Greed
        fg = await c.get(f"{BACKEND}/api/v1/databus/fetch/fear_greed", headers={"X-RMI-Key": RMI_KEY})
        # News
        news = await c.get(f"{BACKEND}/api/v1/databus/fetch/news?limit=10", headers={"X-RMI-Key": RMI_KEY})

        return {
            "trending": trending.json() if trending.status_code == 200 else {},
            "market": market.json() if market.status_code == 200 else {},
            "fear_greed": fg.json() if fg.status_code == 200 else {},
            "news": news.json() if news.status_code == 200 else {},
        }


async def generate_report(market_data: dict) -> str:
    """Use Ollama/LLM to generate a research report."""
    context = json.dumps(market_data, default=str)[:3000]

    prompt = f"""Write a professional daily crypto research report for RugMunch Intelligence.

Data: {context}

Format:
# RMI Daily Crypto Research β€” {datetime.now(UTC).strftime("%B %d, %Y")}

## Market Overview
[2-3 sentences on market conditions, fear & greed, top movers]

## Top 5 Tokens to Watch
[For each: symbol, chain, brief analysis, risk level emoji]

## Notable News
[3-5 one-line news items with source]

## Scam Alert of the Day
[If any token shows scam patterns, flag it here]

## Trading Signal
[One actionable signal based on data β€” BUY/SELL/WATCH/AVOID]

Keep under 500 words. Professional tone. Include data where available."""

    try:
        async with httpx.AsyncClient(timeout=60) as c:
            r = await c.post(
                f"{OLLAMA}/api/generate",
                json={
                    "model": "qwen2.5-coder:7b",
                    "prompt": prompt,
                    "stream": False,
                    "options": {"temperature": 0.6, "num_predict": 1024},
                },
            )
            if r.status_code == 200:
                return r.json().get("response", "")
    except Exception:
        pass

    # Fallback: basic report from data
    return f"""# RMI Daily Crypto Research β€” {datetime.now(UTC).strftime("%B %d, %Y")}

## Market Overview
Market data unavailable. Please check rugmunch.io for live updates.

## Top 5 Tokens to Watch
DataBus fetch in progress. Real-time scanner available at rugmunch.io/scan.

## Notable News
News aggregation running. Latest headlines at rugmunch.io/news.

## Scam Alert of the Day
SENTINEL scanner active. Recent alerts at rugmunch.io/alerts.

## Trading Signal
No signal generated. Premium subscribers receive automated signals daily.
"""


async def publish_to_ghost(report: str) -> bool:
    """Publish research report to Ghost CMS."""
    if not GHOST_KEY:
        print("Ghost not configured β€” report not published")
        return False

    try:
        async with httpx.AsyncClient(timeout=15) as c:
            r = await c.post(
                f"{GHOST_URL}/ghost/api/admin/posts/?source=html",
                json={
                    "posts": [
                        {
                            "title": f"RMI Daily Research β€” {datetime.now(UTC).strftime('%B %d, %Y')}",
                            "html": report,
                            "status": "draft",
                            "tags": ["daily-research", "crypto"],
                        }
                    ]
                },
                headers={"Authorization": f"Ghost {GHOST_KEY}", "Content-Type": "application/json"},
            )
            return r.status_code in (200, 201)
    except Exception as e:
        print(f"Ghost publish failed: {e}")
    return False


async def main():
    print(f"RMI Auto-Research Agent β€” {datetime.now(UTC).isoformat()}")
    print("=" * 60)

    # 1. Gather data
    print("Gathering market data...")
    data = await fetch_market_data()
    print(f"  Trending: {len(str(data['trending']))} chars")
    print(f"  Market: {len(str(data['market']))} chars")
    print(f"  FG: {len(str(data['fear_greed']))} chars")
    print(f"  News: {len(str(data['news']))} chars")

    # 2. Generate report
    print("Generating report via Ollama...")
    report = await generate_report(data)

    # 3. Save locally
    report_path = f"/root/backend/reports/{datetime.now(UTC).strftime('%Y-%m-%d')}.md"
    os.makedirs(os.path.dirname(report_path), exist_ok=True)
    with open(report_path, "w") as f:
        f.write(report)
    print(f"Report saved: {report_path}")

    # 4. Publish to Ghost
    if await publish_to_ghost(report):
        print("Published to Ghost CMS βœ…")
    else:
        print("Ghost publish skipped (not configured)")

    # 5. Summary
    lines = report.split("\n")
    print(f"\nReport: {len(report)} chars, {len(lines)} lines")
    print(report[:300])


if __name__ == "__main__":
    asyncio.run(main())