Initial commit (by create-cloudflare CLI)

Details:
  C3 = create-cloudflare@2.46.1
  project name = ancient-river-4662
  package manager = npm@11.2.0
  wrangler = wrangler@4.14.3
  git = 2.49.0.windows.1
master
Amir Hossein Moghiseh 2025-05-08 11:49:50 +03:30
commit 0d48549d36
10 changed files with 1845 additions and 0 deletions

12
.editorconfig 100644
View File

@ -0,0 +1,12 @@
# http://editorconfig.org
root = true
[*]
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.yml]
indent_style = space

172
.gitignore vendored 100644
View File

@ -0,0 +1,172 @@
# Logs
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Runtime data
pids
_.pid
_.seed
\*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
\*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
\*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
\*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*
# wrangler project
.dev.vars
.wrangler/

6
.prettierrc 100644
View File

@ -0,0 +1,6 @@
{
"printWidth": 140,
"singleQuote": true,
"semi": true,
"useTabs": true
}

1540
package-lock.json generated 100644

File diff suppressed because it is too large Load Diff

13
package.json 100644
View File

@ -0,0 +1,13 @@
{
"name": "ancient-river-4662",
"version": "0.0.0",
"private": true,
"scripts": {
"deploy": "wrangler deploy",
"dev": "wrangler dev",
"start": "wrangler dev"
},
"devDependencies": {
"wrangler": "^4.14.3"
}
}

13
src/package-lock.json generated 100644
View File

@ -0,0 +1,13 @@
{
"name": "ancient-river-4662",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ancient-river-4662",
"version": "1.0.0",
"license": "ISC"
}
}
}

5
src/package.json 100644
View File

@ -0,0 +1,5 @@
{
"name": "ancient-river-4662",
"version": "1.0.0",
"license": "ISC"
}

71
src/worker.js 100644
View File

@ -0,0 +1,71 @@
export default {
async fetch(request, env, ctx) {
if (request.method !== 'POST') {
return new Response('Only POST supported', { status: 405 });
}
const update = await request.json();
const message = update.message;
const callback = update.callback_query;
if (message?.text?.startsWith('/create_event')) {
return new Response(JSON.stringify({ ok: true })); // Stub — use external admin UI
}
if (callback) {
return handleCallback(callback, env);
}
return new Response('OK', { status: 200 });
}
}
async function handleCallback(callback, env) {
const data = callback.data;
const [action, eventId] = data.split('_');
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);
}
await env.ACCEPTS.put(eventId, JSON.stringify([...accepts]));
const eventRaw = await env.EVENTS.get(eventId);
const event = JSON.parse(eventRaw);
const caption = `*${event.title}*\n${event.description}\n\nAccepted: ${[...accepts].join(', ') || 'None'}`;
const reply_markup = {
inline_keyboard: [[{
text: action === 'accept' ? '❌ Revoke' : '✅ Accept',
callback_data: `${action === 'accept' ? 'revoke' : 'accept'}_${eventId}`
}]]
};
const telegramRes = await fetch(`https://api.telegram.org/bot${env.BOT_TOKEN}/editMessageCaption`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
chat_id: callback.message.chat.id,
message_id: callback.message.message_id,
caption,
parse_mode: 'Markdown',
reply_markup
})
});
await fetch(`https://api.telegram.org/bot${env.BOT_TOKEN}/answerCallbackQuery`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
callback_query_id: callback.id,
text: action === 'accept' ? 'Accepted!' : 'Revoked!'
})
});
return new Response(JSON.stringify({ ok: true }));
}

View File

@ -0,0 +1,6 @@
name = "ancient-river-4662"
main = "worker.js"
compatibility_date = "2023-08-23"
[unsafe.metadata.observability]
enabled = true

7
wrangler.toml 100644
View File

@ -0,0 +1,7 @@
name = "ancient-river-4662"
main = "src/worker.js"
workers_dev = true
compatibility_date = "2025-05-08"
[observability]
enabled = true