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
|
#!/usr/bin/env python3
"""discord.py sidecar bot with HTTP API for OMP."""
import asyncio, logging, os, subprocess
import discord
from discord import app_commands
from discord.ext import commands
from aiohttp import web
log = logging.getLogger("discord-omp")
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix="!", intents=intents)
NOTIFY_CH = int(os.environ.get("DISCORD_NOTIFY_CHANNEL", "0"))
HTTP_PORT = int(os.environ.get("DISCORD_HTTP_PORT", "8901"))
# ββ HTTP endpoints (called by OMP extension) ββ
async def handle_notify(req: web.Request) -> web.Response:
data = await req.json()
ch = bot.get_channel(NOTIFY_CH)
if not ch:
return web.json_response({"error": "channel not found"}, status=404)
embed_data = data.get("embed")
if embed_data:
embed = discord.Embed(
title=embed_data.get("title", ""),
description=embed_data.get("description", ""),
color=embed_data.get("color", 0x5865F2),
)
for f in embed_data.get("fields", []):
embed.add_field(
name=f["name"], value=f["value"], inline=f.get("inline", False)
)
await ch.send(content=data.get("content"), embed=embed)
else:
await ch.send(data.get("content", "π¨ (empty)"))
return web.json_response({"ok": True})
async def handle_status(_: web.Request) -> web.Response:
return web.json_response({
"bot": str(bot.user), "guilds": len(bot.guilds),
"latency_ms": round(bot.latency * 1000, 1), "ready": bot.is_ready(),
})
async def handle_channels(_: web.Request) -> web.Response:
return web.json_response([
{"id": str(c.id), "name": c.name, "guild": g.name}
for g in bot.guilds for c in g.text_channels
])
async def start_http():
app = web.Application()
app.router.add_post("/notify", handle_notify)
app.router.add_get("/status", handle_status)
app.router.add_get("/channels", handle_channels)
runner = web.AppRunner(app)
await runner.setup()
await web.TCPSite(runner, "127.0.0.1", HTTP_PORT).start()
log.info(f"HTTP API β 127.0.0.1:{HTTP_PORT}")
# ββ Discord commands ββ
@bot.event
async def on_ready():
await start_http()
await bot.tree.sync()
log.info(f"Ready as {bot.user}")
@bot.hybrid_command(name="ask-omp", description="Ask the OMP coding agent")
@app_commands.describe(prompt="Your question")
async def ask_omp(ctx: commands.Context, *, prompt: str):
await ctx.defer()
try:
r = subprocess.run(
["omp", "-p", prompt, "--no-input"],
capture_output=True, text=True, timeout=120, cwd="/home/dev",
)
out = r.stdout.strip()[:1900] or "(no output)"
await ctx.send(f"```\n{out}\n```")
except subprocess.TimeoutExpired:
await ctx.send("β±οΈ Timed out (120s)")
except Exception as e:
await ctx.send(f"β {e}")
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
asyncio.run(bot.start(os.environ["DISCORD_TOKEN"]))
|