文件内容
scripts/myquran_sholat.py
#!/usr/bin/env python3
"""Helper untuk API jadwal sholat Indonesia (api.myquran.com).
Tidak butuh dependency eksternal (stdlib only).
Commands:
cari <keyword>
hari-ini <keyword>
tanggal <keyword> <YYYY-MM-DD>
bulan <keyword> <YYYY-MM>
id-hari-ini <id>
id-tanggal <id> <YYYY-MM-DD>
id-bulan <id> <YYYY-MM>
Output default: teks ringkas.
"""
from __future__ import annotations
import argparse
import json
import sys
import urllib.parse
import urllib.request
from typing import Any, Dict, List, Optional, Tuple
API_BASE = "https://api.myquran.com/v3"
DEFAULT_TZ = "Asia/Jakarta"
def _get_json(url: str, timeout: int = 20) -> Dict[str, Any]:
req = urllib.request.Request(url, headers={"User-Agent": "openclaw-jadwal-sholat/1.0"})
with urllib.request.urlopen(req, timeout=timeout) as resp:
raw = resp.read().decode("utf-8", errors="replace")
return json.loads(raw)
def search_locations(keyword: str) -> List[Dict[str, Any]]:
kw = urllib.parse.quote(keyword.strip())
url = f"{API_BASE}/sholat/kabkota/cari/{kw}"
j = _get_json(url)
if not j.get("status"):
return []
data = j.get("data")
if isinstance(data, list):
return data
return []
def pick_location(keyword: str, items: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
if not items:
return None
k = keyword.strip().lower()
# Exact match against lokasi field
for it in items:
lokasi = str(it.get("lokasi", "")).strip().lower()
if lokasi == k:
return it
# Contains match
for it in items:
lokasi = str(it.get("lokasi", "")).strip().lower()
if k and k in lokasi:
return it
return items[0]
def fetch_jadwal_today(id_: str, tz: str = DEFAULT_TZ) -> Dict[str, Any]:
q = urllib.parse.urlencode({"tz": tz})
url = f"{API_BASE}/sholat/jadwal/{urllib.parse.quote(id_)}/today?{q}"
return _get_json(url)
def fetch_jadwal_period(id_: str, period: str, tz: str = DEFAULT_TZ) -> Dict[str, Any]:
q = urllib.parse.urlencode({"tz": tz})
url = f"{API_BASE}/sholat/jadwal/{urllib.parse.quote(id_)}/{urllib.parse.quote(period)}?{q}"
return _get_json(url)
def _extract_single_day(j: Dict[str, Any]) -> Tuple[str, Dict[str, Any], Dict[str, Any]]:
"""Return (label, meta, day) where day is an entry with keys imsak/subuh/..."""
data = j.get("data") or {}
meta = {
"id": data.get("id"),
"kabko": data.get("kabko"),
"prov": data.get("prov"),
}
jadwal = (data.get("jadwal") or {})
if not isinstance(jadwal, dict) or not jadwal:
return ("", meta, {})
# pick first key
key = sorted(jadwal.keys())[0]
day = jadwal.get(key) or {}
label = str(day.get("tanggal") or key)
return (label, meta, day)
def format_day(label: str, meta: Dict[str, Any], day: Dict[str, Any]) -> str:
kabko = meta.get("kabko") or "(lokasi)"
prov = meta.get("prov") or ""
loc = f"{kabko}{', ' + prov if prov else ''}".strip()
fields = [
("Imsak", day.get("imsak")),
("Subuh", day.get("subuh")),
("Terbit", day.get("terbit")),
("Dhuha", day.get("dhuha")),
("Dzuhur", day.get("dzuhur")),
("Ashar", day.get("ashar")),
("Maghrib", day.get("maghrib")),
("Isya", day.get("isya")),
]
lines = [f"{loc}", f"Tanggal: {label}"]
for k, v in fields:
if v:
lines.append(f"- {k}: {v}")
return "\n".join(lines)
def cmd_cari(args: argparse.Namespace) -> int:
items = search_locations(args.keyword)
if not items:
print("Tidak ditemukan lokasi.")
return 2
# print as list
for it in items[:30]:
print(f"{it.get('id','')}\t{it.get('lokasi','')}")
if len(items) > 30:
print(f"... ({len(items)-30} lagi)")
return 0
def cmd_hari_ini(args: argparse.Namespace) -> int:
items = search_locations(args.keyword)
picked = pick_location(args.keyword, items)
if not picked:
print("Tidak ditemukan lokasi.")
return 2
j = fetch_jadwal_today(str(picked.get("id")), tz=args.tz)
if not j.get("status"):
print(j.get("message") or "Gagal mengambil jadwal.")
return 3
label, meta, day = _extract_single_day(j)
print(format_day(label, meta, day))
return 0
def cmd_tanggal(args: argparse.Namespace) -> int:
items = search_locations(args.keyword)
picked = pick_location(args.keyword, items)
if not picked:
print("Tidak ditemukan lokasi.")
return 2
j = fetch_jadwal_period(str(picked.get("id")), args.date, tz=args.tz)
if not j.get("status"):
print(j.get("message") or "Gagal mengambil jadwal.")
return 3
label, meta, day = _extract_single_day(j)
print(format_day(label, meta, day))
return 0
def cmd_bulan(args: argparse.Namespace) -> int:
items = search_locations(args.keyword)
picked = pick_location(args.keyword, items)
if not picked:
print("Tidak ditemukan lokasi.")
return 2
j = fetch_jadwal_period(str(picked.get("id")), args.month, tz=args.tz)
if not j.get("status"):
print(j.get("message") or "Gagal mengambil jadwal.")
return 3
data = j.get("data") or {}
jadwal = data.get("jadwal") or {}
if not isinstance(jadwal, dict) or not jadwal:
print("Jadwal kosong.")
return 4
# Print first 7 days as preview
keys = sorted(jadwal.keys())
meta = {"kabko": data.get("kabko"), "prov": data.get("prov")}
print(f"{meta.get('kabko','(lokasi)')}, {meta.get('prov','')}``.\nBulan: {args.month} (menampilkan 7 hari pertama)".replace('`', ''))
for k in keys[:7]:
day = jadwal.get(k) or {}
label = str(day.get("tanggal") or k)
print(f"\n{label}")
for name in ["imsak","subuh","dzuhur","ashar","maghrib","isya"]:
val = day.get(name)
if val:
print(f"- {name}: {val}")
return 0
def cmd_id_hari_ini(args: argparse.Namespace) -> int:
j = fetch_jadwal_today(args.id, tz=args.tz)
if not j.get("status"):
print(j.get("message") or "Gagal mengambil jadwal.")
return 3
label, meta, day = _extract_single_day(j)
print(format_day(label, meta, day))
return 0
def cmd_id_period(args: argparse.Namespace) -> int:
j = fetch_jadwal_period(args.id, args.period, tz=args.tz)
if not j.get("status"):
print(j.get("message") or "Gagal mengambil jadwal.")
return 3
label, meta, day = _extract_single_day(j)
print(format_day(label, meta, day))
return 0
def main(argv: List[str]) -> int:
ap = argparse.ArgumentParser(prog="myquran_sholat")
ap.add_argument("--tz", default=DEFAULT_TZ, help="Timezone (default Asia/Jakarta)")
sub = ap.add_subparsers(dest="cmd", required=True)
p = sub.add_parser("cari", help="Cari kab/kota")
p.add_argument("keyword")
p.set_defaults(func=cmd_cari)
p = sub.add_parser("hari-ini", help="Jadwal sholat hari ini berdasarkan keyword lokasi")
p.add_argument("keyword")
p.set_defaults(func=cmd_hari_ini)
p = sub.add_parser("tanggal", help="Jadwal sholat tanggal tertentu (YYYY-MM-DD) berdasarkan keyword lokasi")
p.add_argument("keyword")
p.add_argument("date")
p.set_defaults(func=cmd_tanggal)
p = sub.add_parser("bulan", help="Jadwal sholat bulan tertentu (YYYY-MM) berdasarkan keyword lokasi (preview 7 hari)")
p.add_argument("keyword")
p.add_argument("month")
p.set_defaults(func=cmd_bulan)
p = sub.add_parser("id-hari-ini", help="Jadwal sholat hari ini berdasarkan id lokasi")
p.add_argument("id")
p.set_defaults(func=cmd_id_hari_ini)
p = sub.add_parser("id-period", help="Jadwal sholat period (YYYY-MM / YYYY-MM-DD) berdasarkan id lokasi")
p.add_argument("id")
p.add_argument("period")
p.set_defaults(func=cmd_id_period)
args = ap.parse_args(argv)
return int(args.func(args))
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))