export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const method = request.method;
const SECRET_KEY = env.SECRET_KEY;
const BOT_TOKEN = env.TELEGRAM_BOT_TOKEN;
// Helper: Telegram Messaging
async function sendTelegram(chatId, text, replyMarkup = null) {
const payload = { chat_id: chatId, text: text, parse_mode: "Markdown" };
if (replyMarkup) payload.reply_markup = replyMarkup;
await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
});
}
// Helper: Save Signal to KV & Log History
async function dispatchSignal(action, symbol, volume, source = "Telegram") {
// Check Kill-Switch State
const isPaused = await env.SIGNAL_STORE.get("bridge_paused");
if (isPaused === "true") return { success: false, reason: "Bridge is PAUSED" };
const tradePayload = {
action,
symbol,
volume: parseFloat(volume) || 0.01,
timestamp: new Date().toISOString(),
source
};
// Set pending trade for MT5
await env.SIGNAL_STORE.put("pending_trade", JSON.stringify(tradePayload), { expirationTtl: 60 });
// Add to History (Keep last 10)
let history = await env.SIGNAL_STORE.get("history", { type: "json" }) || [];
history.unshift(tradePayload);
if (history.length > 10) history.pop();
await env.SIGNAL_STORE.put("history", JSON.stringify(history));
return { success: true, payload: tradePayload };
}
// -------------------------------------------------------------
// 1. WEB APP DASHBOARD (GET / or GET /dashboard)
// -------------------------------------------------------------
if (method === "GET" && (url.pathname === "/" || url.pathname === "/dashboard")) {
const isPaused = (await env.SIGNAL_STORE.get("bridge_paused")) === "true";
const history = (await env.SIGNAL_STORE.get("history", { type: "json" })) || [];
const html = `
Goated Bridge | Command Center
⚡ Goated Bridge Control Panel
Cloudflare Execution Engine & MT5 Gateway
${isPaused ? 'BRIDGE PAUSED' : 'SYSTEM LIVE'}
Recent Signal Logs
| Time |
Source |
Action |
Symbol |
Volume |
${history.length === 0 ? '| No signals logged yet. |
' : ''}
${history.map(item => `
| ${new Date(item.timestamp).toLocaleTimeString()} |
${item.source} |
${item.action} |
${item.symbol} |
${item.volume} |
`).join('')}
`;
return new Response(html, { headers: { "Content-Type": "text/html" } });
}
// -------------------------------------------------------------
// 2. WEB APP API ENDPOINTS
// -------------------------------------------------------------
if (method === "POST" && url.pathname === "/api/toggle-pause") {
const current = await env.SIGNAL_STORE.get("bridge_paused");
const newState = current === "true" ? "false" : "true";
await env.SIGNAL_STORE.put("bridge_paused", newState);
return new Response(JSON.stringify({ paused: newState === "true" }), { status: 200 });
}
if (method === "POST" && url.pathname === "/api/manual-signal") {
const { action, symbol, volume } = await request.json();
const res = await dispatchSignal(action, symbol, volume, "Web App");
return res.success ? new Response("OK", { status: 200 }) : new Response(res.reason, { status: 400 });
}
// -------------------------------------------------------------
// 3. TELEGRAM WEBHOOK ENDPOINT
// -------------------------------------------------------------
if (method === "POST" && url.pathname === "/telegram-webhook") {
try {
const update = await request.json();
if (update.message && update.message.text === "/start") {
const chatId = update.message.chat.id;
const keyboard = {
inline_keyboard: [
[
{ text: "🟢 BUY XAUUSD (0.1)", callback_data: "BUY_XAUUSD_0.1" },
{ text: "🔴 SELL XAUUSD (0.1)", callback_data: "SELL_XAUUSD_0.1" }
],
[
{ text: "⚠️ CLOSE ALL XAUUSD", callback_data: "CLOSE_XAUUSD_ALL" }
]
]
};
await sendTelegram(chatId, "⚡ **Goated Analyst Command Center**\nSelect an instant action:", keyboard);
return new Response("OK", { status: 200 });
}
if (update.callback_query) {
const query = update.callback_query;
const chatId = query.message.chat.id;
const parts = query.data.split('_');
const action = parts[0];
const symbol = parts[1];
const volume = parts[2] ? (parts[2] === "ALL" ? 0.0 : parseFloat(parts[2])) : 0.0;
const result = await dispatchSignal(action, symbol, volume, "Telegram");
if (result.success) {
await sendTelegram(chatId, `✅ **Signal Executed via Bridge!**\nAction: ${action}\nSymbol: ${symbol}\nVolume: ${parts[2] || "ALL"}`);
} else {
await sendTelegram(chatId, `❌ **Execution Blocked:** ${result.reason}`);
}
return new Response("OK", { status: 200 });
}
return new Response("OK", { status: 200 });
} catch (err) {
return new Response("Error", { status: 500 });
}
}
// -------------------------------------------------------------
// 4. MT5 POLLING ENDPOINT
// -------------------------------------------------------------
if (method === "GET" && url.pathname === "/signal") {
const mt5Secret = request.headers.get("X-Bridge-Secret") || url.searchParams.get("secret");
if (mt5Secret !== SECRET_KEY) {
return new Response("Unauthorized", { status: 401 });
}
const pendingTrade = await env.SIGNAL_STORE.get("pending_trade");
if (pendingTrade) {
await env.SIGNAL_STORE.delete("pending_trade");
return new Response(pendingTrade, {
status: 200,
headers: { "Content-Type": "application/json" }
});
}
return new Response(JSON.stringify({ action: "NONE" }), {
status: 200,
headers: { "Content-Type": "application/json" }
});
}
return new Response("Not Found", { status: 404 });
}
};