fetch-and-update
Fetch the latest Cyprus reservoir/dam data and update the app. Use when: fetch latest data, update the app, refresh reservoir data, update dams data.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Fetch the latest Cyprus reservoir/dam data and update the app. Use when: fetch latest data, update the app, refresh reservoir data, update dams data.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
| name | fetch-and-update |
| description | Fetch the latest Cyprus reservoir/dam data and update the app. Use when: fetch latest data, update the app, refresh reservoir data, update dams data. |
You're an agent that updates data on https://cyprus-dams.bougay.com/ automatically
moa.gov.cy/moa/wdd/Wdd.nsf/page18_en Notes page). Fetch this page's HTML with Node (https.get with a browser User-Agent header) — WebFetch/curl return HTTP 403. The latest data file appears as a direct link of the form https://www.gov.cy/app/uploads/sites/168/YYYY/MM/DD_MM_YYYY-UK.xlsx. Grep the HTML for href="..." and pick the most recent *-UK.xlsx (ignore the Graphs-*.xlsx companion file). The English data sheet is named ΙΣΤΟΣΕΛΙΔΑ-UK(N) (the column layout is identical to the old WEB-PAGE-UK(N) sheet: cols 0=name, 3=capacity, 4=last-24h inflow, 5=total inflow since Oct, 6=current MCM, 7=current %, 8=last-year MCM, 9=last-year %, 10=max MCM, 11=max date). Note the Recharge/Other dams (Tamassos, Klirou-Malounta, Solea) now sit below the 10-year inflow table, after a RECHARGE/OTHER heading row.GR.xlsx instead of UK.xlsx, and the data sheet is named ΙΣΤΟΣΕΛΙΔΑ-GR(N).Graphs-DD_MM_YYYY.xlsx companion gets refreshed to the new date (e.g. Graphs-20_07_2026.xlsx alongside a stale 16_07_2026-GR.xlsx) — while the data XLSX for that date is not linked yet. It is usually already uploaded at a predictable URL — probe directly (HTTP HEAD/GET) before concluding there's no new data:
https://www.gov.cy/app/uploads/sites/168/YYYY/MM/DD-MONTHNAME-YYYY-UK.xlsx (e.g. .../2026/02/20-JULY-2026-UK.xlsx; full uppercase English month name; the /YYYY/MM/ path segment is the upload folder — reuse the one from the currently linked files, it does not track the report month)https://www.gov.cy/media/sites/168/YYYY/MM/ and the -GR.xlsx variantsDD-MMM-YYYYUK.xlsx, e.g. 19-JUN-2026UK.xlsx). Use Node to download the XLSX file (not Curl) and parse it. DON'T continue until you downloaded the latest data fileThe update also runs unattended in the cloud: a Vercel cron job checks the gov.cy pages
every 10 minutes and fires this routine when a newer bulletin appears (see
CLOUD-AUTOMATION.md).
A cloud session is a fresh checkout, so anything gitignored is simply absent —
community/* (except the tracked community/TELEGRAM.md), logs/, .env.local, and all
of scripts/ except scripts/og/. When those files are missing, you are in a cloud run:
apply these overrides.
corepack enable && pnpm install --frozen-lockfile. Retry once on registry failure.<routine-fire-payload> block carries detected_bulletin_date
and the deployed_dataset the cron compared against. Treat the date as a hint that saves
you a lookup — still confirm it against the pages, and if the data XLSX for it isn't
linked yet, probe the predictable URLs (see the stale-link note above).community/TELEGRAM.md is present and git-tracked — read it the same way a local run
would, for both the daysSinceLatest/schedule check and the exact Telegram post shape (see
the template in Platform formatting below). community/WHATSAPP.md and any other
sibling file are still gitignored and absent — cloud runs only post to Telegram, not
WhatsApp; leave that platform's post to a local run.getSummaryChanges story arc
(markdown, links, different structure from the Telegram file):
curl -s -H "Authorization: Bearer $INTERNAL_API_SECRET" \
"https://fragmata.info/api/internal/narrative?limit=6"
Returns the recent datasets' getSummaryChanges narratives in all three languages, newest
first, plus daysSinceLatest. Since the recent data-*.ts modules are also tracked and
present in the checkout, reading them directly works just as well — use whichever is
convenient.tsx scripts/post-telegram.ts:
curl -s -X POST "https://fragmata.info/api/internal/telegram" \
-H "Authorization: Bearer $INTERNAL_API_SECRET" \
-H "Content-Type: application/json" \
-d @post.json # {"text": "<the Telegram post body>"} add "dryRun": true to preview
Write the body to a temp JSON file rather than inlining it, to avoid shell-quoting
problems. Expect {"ok":true,"messageId":…,"chatId":…}; a 422 means the post exceeds
Telegram's 4096-char limit — shorten it and retry. Retry once on a network failure.community/TELEGRAM.md the same way a local run does (see
After a successful send below) and commit it alongside the dataset commit — it's
tracked now, so the edit isn't lost when the session ends. Never write a future-dated /
not-yet-sent draft into this file (it's a public repo); drafts belong in the still-gitignored
community/DRAFTS.md until they've actually gone out.main — Vercel deploys from it, which is what closes the loop and
stops the cron from firing again for the same bulletin.Inline Node.js in Bash:
-e script to prevent bash variable substitution issuesjsonData[i][j] works, but avoid template expressions"Connection": "close" in the request headers so sockets don't linger..then(() => process.exit(0), e => { console.error(e); process.exit(1); }) so the process terminates the moment its work is done. (All console.logs have already run by then, so nothing is lost.)File Creation:
Data Module Creation:
Historical Storage Data (historicalStorageData.ts):
The historical heatmap chart uses src/utils/historicalStorageData.ts — it must be updated alongside each new data module or the chart will stop at the previous date.
historicalStorageData array with the current storage amounts from the new data module{ date: "YYYY-MM-DD", kouris: X, kalavasos: X, ... , totalSCP: X, totalAll: X }totalSCP = sum of Southern Conveyor reservoirs (kouris, kalavasos, lefkara, dipotamos, germasoyeia, arminou, polemidia, achna)totalAll = sum of all 21 reservoirsstorage.current.amount in the new data moduleNarrative coherence — the getSummaryChanges text, articles, and community posts are all part of one evolving story. Before writing any of them:
community/TELEGRAM.md (tracked, present in every run including cloud) and the latest getSummaryChanges to understand the current narrative arccommunity/TELEGRAM.md only ever holds posts that have actually been sent (see Community Post below on where drafts belong instead)Summary / Recent Changes (getSummaryChanges):
[Kouris](/dam/kouris/), [Arminou](/dam/arminou/)→[Kouris](/dam/kouris/)[Κούρης](/el/dam/kouris/), [Αρμίνου](/el/dam/arminou/)→[Κούρης](/el/dam/kouris/)[Курис](/ru/dam/kouris/), [Арминоу](/ru/dam/arminou/)→[Курис](/ru/dam/kouris/)DAM_SLUG_MAP in src/utils/slugs.ts (e.g., agia-marina, klirou-malounta)en, /el for Greek, /ru for RussianPer-Dam Summaries (getDamSummary):
Each data module should export a getDamSummary(damName, language) function that returns a short 1-2 sentence summary for each of the 21 dams. These are displayed as subtitles on dam pages and used as SEO meta descriptions.
'en' | 'el' | 'ru')string | null — return null if dam name not founddata-20-MAR-2026.ts as referencedataManager.ts (getDamSummary wrapper) — no changes needed there, it auto-detects the functionTelegram delivery:
(Local runs. In a cloud run the script isn't in the checkout — POST to the relay endpoint in Cloud runs above instead.)
After the git push, send the Telegram version of the community post you just wrote by piping it into the CLI:
tsx scripts/post-telegram.ts <<'EOF'
<paste the exact text that belongs inside the ### Telegram code fence in community/TELEGRAM.md>
EOF
(tsx is expected to be on the user's PATH — installed globally via pnpm add -g tsx.)
TELEGRAM.md itself; you pass the textTELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID from .env.local at the repo root (gitignored)TELEGRAM_CHAT_ID to the channel's @handle or pass --chat-id @handle (bot must be an admin there)--dry-run to print the length and preview without sendingmessage_id and chat_id on stdout — capture themAfter a successful send, record the identifiers inside community/TELEGRAM.md so a later edit or delete can target the right message:
### Telegram heading of the post you just sent (above the opening ``` fence), in this exact format:
### Telegram
Sent: message_id=<id>, chat_id=<chat_id>, at=<YYYY-MM-DDTHH:MM:SSZ>
```
...message text...
```message_id and chat_id printed by the script (or by the relay's JSON
response in a cloud run), and the current UTC timestamp for at=community/TELEGRAM.md is git-tracked — commit this edit (local runs: as part of the normal
commit; cloud runs: alongside the dataset commit, before pushing to main)News Ticker Refresh:
The dashboard has a scrolling news ticker showing recent water crisis articles. Keep it current on each data update:
src/utils/newsArticles.ts — add new articles following the existing { title, url, lang, source } formatNEWS.md to keep it in sync (grouped by language: English, Russian, Greek)Community Post (community/TELEGRAM.md + community/WHATSAPP.md):
After each data update, append a new community post entry to both community/TELEGRAM.md and community/WHATSAPP.md. Each entry carries the same ## <date> heading and summary line in both files, followed by that platform's code block (### Telegram in TELEGRAM.md, ### WhatsApp in WHATSAPP.md).
community/TELEGRAM.md is git-tracked (this is a public repo) — only ever append a post
after it has been sent; never write a future-dated or not-yet-sent draft into it. Drafts go
in the still-gitignored community/DRAFTS.md and move over once sent. community/WHATSAPP.md
(and any other sibling file) stays gitignored/local-only, no such restriction there.
## <date> heading in community/TELEGRAM.md to find when the previous post was made — the file is tracked and present in every run, local or cloud. (daysSinceLatest from the narrative endpoint works too, if you're already fetching it for the website narrative.)community/TELEGRAM.md is a public, git-tracked file — only append an entry after the post has actually been sent (i.e. you have a real message_id). Anything written ahead of time (a scheduled article announcement, a pre-planned post) belongs in community/DRAFTS.md instead, which stays gitignored. Move it into TELEGRAM.md once it's actually gone out.Minor data updates (📊):
Major updates (📰):
Headline total calculation: Exclude Recharge/Other dams (Tamassos, Klirou-Malounta, Solea) from the headline storage % and MCM — this matches the dashboard. Individual Recharge dams can still appear in bullet points.
Platform formatting — one code block per file:
Telegram (### Telegram):
• for bullets🔗 https://fragmata.infocommunity/TELEGRAM.md
history; in a cloud run that file is absent, so this template IS the spec, don't improvise):
[emoji] Cyprus Dams — [Month Day]
[Day-of-week] bulletin: total storage [X]% ([Y] MCM) — [down/up] [Z] MCM from [prior day-name
or date], [pace commentary, e.g. "the same slow drawdown pace continuing"]. [Optional 1-sentence
standout story — often a running thread like Arminou's rise/plateau]. Gap over last year:
+[N]pp. Season inflow: [N] MCM (July so far: [N] MCM). Arminou→Kouris transfer unchanged at
20.44 MCM.
• [Dam] X% (±N.Npp) — [short context]
• [Dam] X% (±N.Npp) — [short context]
• [Dam] X% (±N.Npp) — [short context]
• Kalopanagiotis 100% — still the only dam overflowing
• Achna 1.9% — unchanged, zero inflow all season
Gap vs last year: +[N]pp.
🔗 https://fragmata.info
[emoji] Cyprus Dams — [Month Day] (no year, no "Update" suffix
in recent posts), blank line, then the paragraph.Kalopanagiotis 100% and Achna 1.9% are recurring anchor bullets — include them nearly
every post since they're part of the running narrative, not just "notable movements."Gap vs last year: +N pp. appears twice — once inline in the paragraph, once again as
its own line right before the closing link. Keep both; it's the established convention, not
a duplication bug.WhatsApp (### WhatsApp):
*asterisks* for native bold rendering• for bullets🔗 https://fragmata.infoWhat to highlight — let the data decide, don't repeat a fixed list of dams:
waterTransferred changed)× (multiplication sign) not x for multipliers