pawsling

Pawsling

A slingshot game for Telegram (Mini App): cats and raccoons against an uprising of household robots and gadgets.

webapp/index.html   a tiny loader that fetches the game past Telegram's cache
webapp/game.js      the whole game (canvas + synthesised sounds, no assets)
webapp/game.css     page styles for the game
bot/                a small bot that opens the game: /start and the "Грати" menu button
worker/             the leaderboard: a Cloudflare Worker with a D1 database

Gameplay

Local testing

python -m http.server 5391 --directory webapp

Open http://localhost:5391. The #dev hash (http://localhost:5391/#dev) runs the loop on a timer and exposes window.__pawsling for automated checks.

Launching in Telegram

The game runs without a permanently running server: the page is on GitHub Pages (https://vitaliivepsha.github.io/pawsling/webapp/), and everything else is configured in Telegram.

  1. Hosting. GitHub Pages from the main branch, / (root) folder. Every push to main updates the game within a minute or two. Telegram’s WebView caches pages hard, so index.html only loads game.js and game.css with a fresh ?v= on every launch: players get the new version as soon as Pages has it. Keep game changes in those two files; if index.html itself ever changes, bump the ?v= in the game address (step 2 and @BotFather).
  2. Menu button “Грати”. bot/bot.py is a one-time script that sets it via setChatMenuButton and exits; Telegram remembers the button. Rerun only if the game address changes. The bot’s messages (/start, payments) are handled by the worker’s webhook, so never run a polling bot for this token: polling removes the webhook. The current address is https://vitaliivepsha.github.io/pawsling/webapp/?v=2.
    cd bot
    cp .env.example .env      # fill in BOT_TOKEN and WEBAPP_URL
    pip install -r requirements.txt
    python bot.py             # wait for "Menu button set", then Ctrl+C
    
  3. @BotFather (one time):
    • Bot Settings → Configure Mini App → Enable Mini App, with the same URL. The “Open App” button then appears in the bot’s profile, and the game opens via t.me/<bot>?startapp.
    • Edit Bot → Edit Description: the greeting shown before pressing Start.
    • Edit Bot → Edit About: a short description in the profile.
    • /setuserpic: the bot’s profile picture, assets/bot-avatar.png (1024×1024) or webapp/icon-512.png: a paw in flight with speed lines (paw + sling) on a warm gradient, chosen to stay readable as a tiny circle in the chat list on dark and light themes.

Server: leaderboard, payments, bot replies

Content

Depth

Daily and social

News channels

The worker posts to a Ukrainian and an English Telegram channel (CHANNEL_UK, CHANNEL_EN in wrangler.toml, the bot an admin there): the week’s results and the new weekly challenge on Monday, a tip or a room spotlight from CH_FEED on Tuesday, Thursday and Saturday, the challenge leader on Friday, and seasonal events: the announcement on the first day, and three days before the end (2 Nov, 7 Jan, 15:00) a “3 days left” post with how many players already have the hat (from event_runs, left out while under 10). A channel stays quiet until the owner’s /chan intro. Play buttons carry startapp=ch_<lang>_<kind>; every new player’s first start_param goes to sources, and /chan shows arrivals by source. Setup and the audience plan: promo/CHANNELS.md.

The community group (COMMUNITY_GROUP, a forum; the bot an admin with “Manage topics”) gets its topics from the owner’s /chan group: 🇺🇦 UA, 🇬🇧 EN, 🛠 Technical Support and 📣 Events, the last closed to members. The week’s results and the event posts go to Events in both languages (startapp=grp_…), and /start links the player’s language topic next to the news channel. Once a channel reaches CH_GROUP_AT (100) subscribers it gets one post inviting to the group, with buttons into its language topic and into Technical Support (/chan post group posts it by hand).

Leaderboard

worker/ is a Cloudflare Worker (free plan) with a D1 database. The game posts Telegram’s initData with every request; the worker checks its signature with the bot token, so a result is always saved for the real Telegram account. Scores are capped (500 waves, 400 stars); a determined player could still send a fake result from their own account, which is the usual limit of a client-side game.

Setup (Node 18+; with nvm: nvm use 22):

cd worker
npx wrangler login                                   # opens the browser, once
npx wrangler d1 create pawsling                      # put the printed database_id into wrangler.toml
npx wrangler d1 execute pawsling --remote --file=schema.sql
npx wrangler deploy                                  # prints https://pawsling-leaderboard.<account>.workers.dev
npx wrangler secret put BOT_TOKEN                    # paste the bot token when asked
npx wrangler secret put ADMIN_ID                     # optional: your Telegram id, for sales messages

With ADMIN_ID set, the bot messages the owner about every purchase and refund (a refund also takes back what the purchase gave), and answers the owner’s /sales with sales for the last day, week, month and all time, the best sellers and the latest purchases. Refunded purchases are not counted. Other players get no answer to /sales.

Then set BOARD_URL at the top of the leaderboard section in webapp/game.js to that address and push.