'init'
parent
0d48549d36
commit
e87735e7bd
File diff suppressed because it is too large
Load Diff
|
@ -8,6 +8,11 @@
|
||||||
"start": "wrangler dev"
|
"start": "wrangler dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"esbuild": "^0.25.4",
|
||||||
"wrangler": "^4.14.3"
|
"wrangler": "^4.14.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.804.0",
|
||||||
|
"@aws-sdk/node-http-handler": "^3.370.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
405
src/worker.js
405
src/worker.js
|
@ -1,71 +1,386 @@
|
||||||
export default {
|
var __defProp = Object.defineProperty;
|
||||||
async fetch(request, env, ctx) {
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
||||||
if (request.method !== 'POST') {
|
|
||||||
return new Response('Only POST supported', { status: 405 });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// src/worker.js
|
||||||
|
var __defProp2 = Object.defineProperty;
|
||||||
|
var __name2 = /* @__PURE__ */ __name((target, value) => __defProp2(target, "name", { value, configurable: true }), "__name");
|
||||||
|
var worker_default = {
|
||||||
|
async fetch(request, env, ctx) {
|
||||||
|
try {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
if (url.pathname.startsWith("/status/")) {
|
||||||
|
const eventId = url.pathname.split("/")[2];
|
||||||
|
return await handleStatusPage(eventId, env);
|
||||||
|
}
|
||||||
|
if (request.method !== "POST") return new Response("Only POST", { status: 405 });
|
||||||
const update = await request.json();
|
const update = await request.json();
|
||||||
const message = update.message;
|
const message = update.message;
|
||||||
const callback = update.callback_query;
|
const callback = update.callback_query;
|
||||||
|
const inlineQuery = update.inline_query;
|
||||||
if (message?.text?.startsWith('/create_event')) {
|
if (message?.text === "/start") {
|
||||||
return new Response(JSON.stringify({ ok: true })); // Stub — use external admin UI
|
await sendMessage(env.BOT_TOKEN, message.chat.id, "Welcome To Gebels Manager! Use /create_event to create an event or /list to see all events.");
|
||||||
|
return new Response("OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback) {
|
if (message?.text === "/list") {
|
||||||
return handleCallback(callback, env);
|
const events = await listEvents(env.EVENTS);
|
||||||
|
if (!events.length) {
|
||||||
|
await sendMessage(env.BOT_TOKEN, message.chat.id, "\u{1F4ED} No events found.");
|
||||||
|
return new Response("OK");
|
||||||
}
|
}
|
||||||
|
for (const event of events) {
|
||||||
|
|
||||||
return new Response('OK', { status: 200 });
|
const text = `*${event.title}*
|
||||||
|
${event.description}
|
||||||
|
|
||||||
|
[\u2705 View Status](https://ancient-river-4662.siramirmoghi3.workers.dev/status/${event.id})`;
|
||||||
|
const markup = {
|
||||||
|
inline_keyboard: [[
|
||||||
|
{ text: "\u2705 Accept", callback_data: `accept_${event.id}` },
|
||||||
|
{ text: "\u274C Refuse", callback_data: `refuse_${event.id}` },
|
||||||
|
{ text: "🗑️ Delete", callback_data: `delete_${event.id}` },
|
||||||
|
]]
|
||||||
|
};
|
||||||
|
await sendPhoto(env.BOT_TOKEN, message.chat.id, event.image, text, "Markdown", markup);
|
||||||
}
|
}
|
||||||
|
return new Response("OK");
|
||||||
}
|
}
|
||||||
|
if (message?.text === "/cancel") {
|
||||||
async function handleCallback(callback, env) {
|
await env.STATES.delete(message.from.id.toString());
|
||||||
const data = callback.data;
|
await sendMessage(env.BOT_TOKEN, message.chat.id, "\u274C Event creation canceled.");
|
||||||
const [action, eventId] = data.split('_');
|
return new Response("OK");
|
||||||
const user = callback.from.username || callback.from.first_name;
|
|
||||||
|
|
||||||
const acceptsRaw = await env.ACCEPTS.get(eventId);
|
|
||||||
const accepts = new Set(acceptsRaw ? JSON.parse(acceptsRaw) : []);
|
|
||||||
if (action === 'accept') {
|
|
||||||
accepts.add(user);
|
|
||||||
} else if (action === 'revoke') {
|
|
||||||
accepts.delete(user);
|
|
||||||
}
|
}
|
||||||
|
if (message?.text === "/create_event") {
|
||||||
|
await env.STATES.put(message.from.id.toString(), JSON.stringify({ step: "title" }));
|
||||||
|
await sendMessage(env.BOT_TOKEN, message.chat.id, "Please enter the *title* of the event:", "Markdown");
|
||||||
|
return new Response("OK");
|
||||||
|
}
|
||||||
|
if (inlineQuery) {
|
||||||
|
const query = inlineQuery.query.trim().toLowerCase();
|
||||||
|
const allEvents = await listEvents(env.EVENTS);
|
||||||
|
const filtered = allEvents.filter((e) => e.title.toLowerCase().includes(query)).sort((a, b) => b.createdAt - a.createdAt).slice(0, 5);
|
||||||
|
const results = await Promise.all(filtered.map(async (e) => {
|
||||||
|
return {
|
||||||
|
type: "photo",
|
||||||
|
id: e.id,
|
||||||
|
photo_file_id: e.image,
|
||||||
|
// Using file_id for cached photo
|
||||||
|
caption: `*${e.title}*
|
||||||
|
${e.description}
|
||||||
|
|
||||||
await env.ACCEPTS.put(eventId, JSON.stringify([...accepts]));
|
[\u2705 View Status](https://ancient-river-4662.siramirmoghi3.workers.dev/status/${e.id})`,
|
||||||
|
parse_mode: "Markdown",
|
||||||
const eventRaw = await env.EVENTS.get(eventId);
|
reply_markup: {
|
||||||
const event = JSON.parse(eventRaw);
|
inline_keyboard: [[
|
||||||
|
{ text: "\u2705 Accept", callback_data: `accept_${e.id}` },
|
||||||
const caption = `*${event.title}*\n${event.description}\n\nAccepted: ${[...accepts].join(', ') || 'None'}`;
|
{ text: "\u274C Refuse", callback_data: `refuse_${e.id}` }
|
||||||
const reply_markup = {
|
]]
|
||||||
inline_keyboard: [[{
|
}
|
||||||
text: action === 'accept' ? '❌ Revoke' : '✅ Accept',
|
};
|
||||||
callback_data: `${action === 'accept' ? 'revoke' : 'accept'}_${eventId}`
|
}));
|
||||||
}]]
|
return await respondInlineQuery(env.BOT_TOKEN, inlineQuery.id, results);
|
||||||
|
}
|
||||||
|
if (callback) return await handleCallback(callback, env);
|
||||||
|
if (message) {
|
||||||
|
const userId = message.from.id.toString();
|
||||||
|
const stateRaw = await env.STATES.get(userId);
|
||||||
|
if (!stateRaw) return new Response("No state");
|
||||||
|
const state = JSON.parse(stateRaw);
|
||||||
|
const { step, data = {} } = state;
|
||||||
|
if (step === "title") {
|
||||||
|
data.title = message.text;
|
||||||
|
await env.STATES.put(userId, JSON.stringify({ step: "description", data }));
|
||||||
|
await sendMessage(env.BOT_TOKEN, message.chat.id, "Enter the *description*:", "Markdown");
|
||||||
|
} else if (step === "description") {
|
||||||
|
data.description = message.text;
|
||||||
|
await env.STATES.put(userId, JSON.stringify({ step: "image", data }));
|
||||||
|
await sendMessage(env.BOT_TOKEN, message.chat.id, "Please send an *image* for the event.", "Markdown");
|
||||||
|
} else if (step === "image" && message.photo) {
|
||||||
|
const fileId = message.photo.at(-1).file_id;
|
||||||
|
await env.STATES.put(userId, JSON.stringify({ step: 'limit', data: { ...data, fileId } }));
|
||||||
|
await sendMessage(env.BOT_TOKEN, message.chat.id, `Please enter a valid number for the acceptance limit or enter 0 for unlimited`, "Markdown");
|
||||||
|
} else if (step === 'limit') {
|
||||||
|
const limit = parseInt(message.text);
|
||||||
|
if (isNaN(limit)) {
|
||||||
|
await sendMessage(env.BOT_TOKEN, message.chat.id, 'Please enter a valid number for the acceptance limit or enter 0 for unlimited.');
|
||||||
|
} else {
|
||||||
|
data.limit = (limit === 0) ? null : limit; // 0 means no limit
|
||||||
|
const createdAt = new Date().toISOString(); // Track creation time
|
||||||
|
const eventId = crypto.randomUUID();
|
||||||
|
const eventData = {
|
||||||
|
...data,
|
||||||
|
limit: data.limit,
|
||||||
|
image: data.fileId, // Move fileId here in the event creation
|
||||||
|
id: eventId,
|
||||||
|
creatorId: message.from.id,
|
||||||
|
createdAt, // Add the creation timestamp
|
||||||
};
|
};
|
||||||
|
|
||||||
const telegramRes = await fetch(`https://api.telegram.org/bot${env.BOT_TOKEN}/editMessageCaption`, {
|
// Store event in the database
|
||||||
method: 'POST',
|
await env.EVENTS.put(eventId, JSON.stringify(eventData));
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
|
// Clear the state after event creation
|
||||||
|
await env.STATES.delete(userId);
|
||||||
|
|
||||||
|
await sendMessage(env.BOT_TOKEN, message.chat.id, `✅ Event created: *${data.title}*`, 'Markdown');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await sendMessage(env.BOT_TOKEN, message.chat.id, "Unexpected input. Try /create_event again.");
|
||||||
|
await env.STATES.delete(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Response("OK");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Unhandled Exception:", {
|
||||||
|
message: err.message,
|
||||||
|
stack: err.stack,
|
||||||
|
name: err.name
|
||||||
|
});
|
||||||
|
return new Response("Internal Error", { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
async function handleCallback(callback, env) {
|
||||||
|
const data = callback.data;
|
||||||
|
const [action, eventId] = data.split("_");
|
||||||
|
const user = callback.from.username || callback.from.first_name || "unknown";
|
||||||
|
const [rawAccepts, rawRefuses] = await Promise.all([
|
||||||
|
env.ACCEPTS.get(eventId),
|
||||||
|
env.REFUSES.get(eventId)
|
||||||
|
]);
|
||||||
|
const accepts = new Set(rawAccepts ? JSON.parse(rawAccepts) : []);
|
||||||
|
const refuses = new Set(rawRefuses ? JSON.parse(rawRefuses) : []);
|
||||||
|
accepts.delete(user);
|
||||||
|
refuses.delete(user);
|
||||||
|
const eventRaw = await env.EVENTS.get(eventId);
|
||||||
|
if (!eventRaw) return new Response("Event not found");
|
||||||
|
const event = JSON.parse(eventRaw);
|
||||||
|
if (action === "delete") {
|
||||||
|
// Only allow creator to delete the event
|
||||||
|
|
||||||
|
console.log(callback.from.id, event.creatorId);
|
||||||
|
if (callback.from.id !== event.creatorId) {
|
||||||
|
await sendMessage(env.BOT_TOKEN, callback.message.chat.id, "Only the event creator can delete this event.");
|
||||||
|
return new Response("Unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete event and all related data
|
||||||
|
await Promise.all([
|
||||||
|
env.EVENTS.delete(eventId),
|
||||||
|
env.ACCEPTS.delete(eventId),
|
||||||
|
env.REFUSES.delete(eventId)
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Edit message or send update
|
||||||
|
await fetch(`https://api.telegram.org/bot${env.BOT_TOKEN}/editMessageText`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
chat_id: callback.message.chat.id,
|
chat_id: callback.message.chat.id,
|
||||||
message_id: callback.message.message_id,
|
message_id: callback.message.message_id,
|
||||||
caption,
|
text: "🗑️ Event deleted by creator."
|
||||||
parse_mode: 'Markdown',
|
|
||||||
reply_markup
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return new Response("Event deleted");
|
||||||
|
}
|
||||||
|
if (action === "accept") {
|
||||||
|
|
||||||
|
// Check if the acceptance limit has been reached
|
||||||
|
if (accepts.has(user)) {
|
||||||
|
await sendMessage(env.BOT_TOKEN, callback.message.chat.id, `You have already accepted the event *${event.title}*`, 'Markdown');
|
||||||
|
return new Response('OK');
|
||||||
|
}
|
||||||
|
if (event.limit !== 0 && event.limit !== null && accepts.size >= event.limit) {
|
||||||
|
await sendMessage(env.BOT_TOKEN, callback.message.chat.id, `Sorry, the event *${event.title}* has reached its acceptance limit.`, 'Markdown');
|
||||||
|
return new Response('OK');
|
||||||
|
}
|
||||||
|
|
||||||
|
accepts.add(user);
|
||||||
|
} else if (action === "refuse") {
|
||||||
|
refuses.add(user);
|
||||||
|
}
|
||||||
|
await Promise.all([
|
||||||
|
env.ACCEPTS.put(eventId, JSON.stringify([...accepts])),
|
||||||
|
env.REFUSES.put(eventId, JSON.stringify([...refuses]))
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const creatorId = event.creatorId;
|
||||||
|
const creatorMessage = `Your event *${event.title}* has received a new ${action === "accept" ? "acceptance ✅" : "refusal ❌"}.`;
|
||||||
|
await sendMessage(env.BOT_TOKEN, creatorId, creatorMessage, "Markdown");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Wait for all notifications to be sent
|
||||||
|
|
||||||
|
if (!eventRaw) return new Response("Event not found");
|
||||||
await fetch(`https://api.telegram.org/bot${env.BOT_TOKEN}/answerCallbackQuery`, {
|
await fetch(`https://api.telegram.org/bot${env.BOT_TOKEN}/answerCallbackQuery`, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
callback_query_id: callback.id,
|
callback_query_id: callback.id,
|
||||||
text: action === 'accept' ? 'Accepted!' : 'Revoked!'
|
text: `You selected ${action === "accept" ? "\u2705 Accept" : "\u274C Refuse"}`
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
return new Response("OK");
|
||||||
return new Response(JSON.stringify({ ok: true }));
|
|
||||||
}
|
}
|
||||||
|
__name(handleCallback, "handleCallback");
|
||||||
|
async function sendMessage(token, chat_id, text, parse_mode) {
|
||||||
|
return fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ chat_id, text, parse_mode })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
__name(sendMessage, "sendMessage");
|
||||||
|
async function respondInlineQuery(token, queryId, results) {
|
||||||
|
return fetch(`https://api.telegram.org/bot${token}/answerInlineQuery`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ inline_query_id: queryId, results, cache_time: 0 })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
__name(respondInlineQuery, "respondInlineQuery");
|
||||||
|
async function listEvents(KV) {
|
||||||
|
const list = await KV.list();
|
||||||
|
return await Promise.all(
|
||||||
|
list.keys.map(async (k) => {
|
||||||
|
const raw = await KV.get(k.name);
|
||||||
|
return JSON.parse(raw);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
__name(listEvents, "listEvents");
|
||||||
|
async function handleStatusPage(eventId, env) {
|
||||||
|
const [acceptsRaw, refusesRaw, eventRaw] = await Promise.all([
|
||||||
|
env.ACCEPTS.get(eventId),
|
||||||
|
env.REFUSES.get(eventId),
|
||||||
|
env.EVENTS.get(eventId)
|
||||||
|
]);
|
||||||
|
console.log("eventRaw", eventId, acceptsRaw, refusesRaw, eventRaw);
|
||||||
|
if (!eventRaw) return new Response("Event not found", { status: 404 });
|
||||||
|
const event = JSON.parse(eventRaw);
|
||||||
|
const accepts = acceptsRaw ? JSON.parse(acceptsRaw) : [];
|
||||||
|
const refuses = refusesRaw ? JSON.parse(refusesRaw) : [];
|
||||||
|
const fileResponse = await fetch(`https://api.telegram.org/bot${env.BOT_TOKEN}/getFile?file_id=${event.image}`);
|
||||||
|
const fileData = await fileResponse.json();
|
||||||
|
const fileUrl = `https://api.telegram.org/file/bot${env.BOT_TOKEN}/${fileData.result.file_path}`;
|
||||||
|
const html = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Gebels - ${event.title}</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=UnifrakturCook:wght@700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: linear-gradient(to bottom right, #2e003e, #120024);
|
||||||
|
font-family: 'UnifrakturCook', cursive;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 2px solid #b47cff;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 500px;
|
||||||
|
box-shadow: 0 4px 20px rgba(180, 124, 255, 0.3);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #ffe86a;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1.1em;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
margin-top: 20px;
|
||||||
|
color: #94f0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin: 5px 0;
|
||||||
|
background-color: rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #ffe86a;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #fff4c2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card">
|
||||||
|
<h1>${event.title}</h1>
|
||||||
|
<img src="${fileUrl}" alt="${event.title}" style="width: 100%; border-radius: 8px; margin-bottom: 20px;" />
|
||||||
|
<p>${event.description}</p>
|
||||||
|
<h2>\u2705 Accepted</h2>
|
||||||
|
<ul>
|
||||||
|
${accepts.map((u) => `<li><a href="https://t.me/${u}" target="_blank">@${u}</a></li>`).join("")}
|
||||||
|
</ul>
|
||||||
|
<h2>\u274C Refused</h2>
|
||||||
|
<ul>
|
||||||
|
${refuses.map((u) => `<li><a href="https://t.me/${u}" target="_blank">@${u}</a></li>`).join("")}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
`;
|
||||||
|
return new Response(html, {
|
||||||
|
headers: { "Content-Type": "text/html" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
__name(handleStatusPage, "handleStatusPage");
|
||||||
|
async function sendPhoto(token, chat_id, photo, caption, parse_mode = "Markdown", reply_markup = null) {
|
||||||
|
const payload = {
|
||||||
|
chat_id,
|
||||||
|
photo,
|
||||||
|
caption,
|
||||||
|
parse_mode,
|
||||||
|
...reply_markup ? { reply_markup } : {}
|
||||||
|
};
|
||||||
|
return fetch(`https://api.telegram.org/bot${token}/sendPhoto`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
__name(sendPhoto, "sendPhoto");
|
||||||
|
__name2(handleCallback, "handleCallback");
|
||||||
|
export {
|
||||||
|
worker_default as default
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
name = "ancient-river-4662"
|
|
||||||
main = "worker.js"
|
|
||||||
compatibility_date = "2023-08-23"
|
|
||||||
|
|
||||||
[unsafe.metadata.observability]
|
|
||||||
enabled = true
|
|
|
@ -2,6 +2,16 @@ name = "ancient-river-4662"
|
||||||
main = "src/worker.js"
|
main = "src/worker.js"
|
||||||
workers_dev = true
|
workers_dev = true
|
||||||
compatibility_date = "2025-05-08"
|
compatibility_date = "2025-05-08"
|
||||||
|
account_id = "6a8d4d305440e990b98e950f6453f950"
|
||||||
|
|
||||||
|
kv_namespaces = [
|
||||||
|
{ binding = "EVENTS", id = "0fc103ecca3b41d9af481cfb01ed9d52" },
|
||||||
|
{ binding = "ACCEPTS", id = "831bf99c66a44ec5a35e10c0dc4d086c" },
|
||||||
|
{ binding = "REFUSES", id = "36e31a9aebf04480a321234917a150a2" },
|
||||||
|
{ binding = "STATES", id = "af6bc31086de41f38055ced84b7c9606" }
|
||||||
|
]
|
||||||
|
|
||||||
[observability]
|
[observability]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue