Skip to content
xnbep-langPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

Magic — a Telegram mini-app

Pick magic signs from an Astronomy sign (moon phase, planet, constellation, element, color), cast a spell, and get back a real astronomy reading: science interpretation, tonight's sky events, and an observation challenge. A second sign, Ocean, ships too, mostly to prove the architecture holds up.

Everything lives in magic.html — one file, no build step, no dependencies to install. That's a deliberate choice for a Telegram mini-app: Telegram just needs a URL that serves HTML, so one file is the easiest possible thing to host and keep working.

Preview it right now

Open magic.html directly in a browser. It runs standalone outside Telegram — the Telegram SDK script is loaded defensively (window.Telegram?.WebApp), so it just no-ops if it's not present.

For real use, serve it over HTTPS (GitHub Pages, Vercel, Netlify, Cloudflare Pages, or any static host all work) rather than opening the file directly — Telegram requires HTTPS, and geolocation prompts generally need a real origin too.

Making it an actual Telegram mini-app

  1. Host magic.html somewhere public over HTTPS.
  2. Message @BotFather on Telegram → /newapp (or /newbot first if you don't have a bot yet, then /newapp on it).
  3. Give it a name and icon, and paste your hosted URL when asked.
  4. BotFather gives you a t.me/YourBot/YourApp link — that's the mini-app.

Why real astronomy, and how accurate it is

The moon phase, planet positions, sunrise/sunset, and rise/set times are computed from actual orbital mechanics in the browser — no API key, no backend, works offline. Specifically:

  • Moon phase — from the real angular separation between the Sun and Moon (not a fixed 29.5-day cycle guess), cross-checked against live almanac data to within ~1–3% illumination.
  • Planets — NASA/JPL's published low-precision Keplerian elements (valid 1800–2050), solved for position, then converted to altitude/azimuth for your location. Validated two ways: physical limits (Mercury/Venus elongation never exceeds their real ~28°/47° maximums across a 400-day scan) and a live cross-check against real-world "what's visible tonight" reporting — it correctly reproduced which planets were up, in the right part of the sky, at the right time of night.
  • Meteor showers — a fixed calendar of the 11 major annual showers, cross-checked against the current AMS/IMO calendar.
  • Sunrise/sunset — the standard NOAA solar-position formula.
  • Tides (Ocean sign) — real spring/neap tide regime, derived from the actual Sun–Moon angle. It won't give you a precise tide time for a specific beach — that needs local harmonic tide-station data this app doesn't have — so it intentionally doesn't pretend to.

This is naked-eye, "which way do I look" precision, not arcsecond ephemeris — that's the right level for a stargazing app, and it's honest about the difference (see the footer disclaimer in the app itself).

Architecture: how a "Magic sign" plugs in

The file has three layers, in this order:

  1. Core (TelegramBridge, MagicRegistry, SpellGenerator, el()) — knows nothing about astronomy, oceans, or anything else. It just renders whatever categories a registered domain declares, and generates spell names from whatever sigil fragments a symbol supplies.
  2. A domain's data + engine — e.g. AstronomyData / AstronomyEngine. Pure calculation, no UI, no magic framing.
  3. A domain's registration — e.g. registerAstronomyDomain(). This is the only layer that knows both the real-world subject and the "magic" framing. It defines the symbols and two functions, then calls MagicRegistry.register(...).

The Ocean domain in the file is a working example of step 3, built to prove the contract — it even reuses AstronomyEngine.getMoonPhase() for genuine spring/neap tide logic, without touching a single line of the Astronomy domain's own code.

Adding a new sign (e.g. Weather)

You only ever need to write one thing: an object with categories and two functions, registered once. Nothing in Core or in other domains changes.

MagicRegistry.register({
  id: 'weather',
  name: 'Weather',
  icon: '⛈',
  categories: [
    {
      id: 'sky', name: 'Sky', maxSelect: 1,
      symbols: [
        { id: 'clear', name: 'Clear', glyph: '☀',
          sigil: { epithet: 'Open', root: 'Vault', power: 'Clarity' } },
        { id: 'storm', name: 'Storm', glyph: '⛈',
          sigil: { epithet: 'Roaring', root: 'Tempest', power: 'Upheaval' } }
        // ...
      ]
    }
    // more categories: wind, precipitation, whatever fits
  ],

  // Required: return { scienceText, skyEvents: [{glyph, label, value}, ...] }
  computeInsight(selection, ctx) {
    // ctx = { date, lat, lon, hemisphere }
    // call a real weather API here, or your own calculation
    return { scienceText: '...', skyEvents: [...] };
  },

  // Required: return one actionable string
  generateChallenge(selection, insight, ctx) {
    return 'Step outside and ...';
  }
});

Every symbol needs a sigil: { epithet, root, power } — three words the generic SpellGenerator mixes into a name. That's the entire integration surface. The category picker, the sigil-circle drawing, the domain switcher tab, and the result cards all pick this up automatically because they're written against the registry, not against "astronomy" by name.

File map (all inside magic.html)

Section What it is
<style> Design tokens + component CSS
TelegramBridge Telegram WebApp SDK wrapper (haptics, theming)
MagicRegistry The domain plug-in system
SpellGenerator Domain-agnostic spell name generator
AstronomyData / AstronomyEngine Orbital mechanics, validated
registerAstronomyDomain() Astronomy's symbols + content
registerOceanDomain() Ocean's symbols + content (the demo sign)
App DOM rendering + event wiring, generic over any domain

If this grows past one file, the natural next step is to split each of those into its own module (core/, domains/astronomy/, domains/ocean/) — the boundaries above are already exactly where the <script> tags would become import/export module files.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages