Skill: jiajiaoy/daily-astro (version 1.0.3)

--- name: daily-astro description: "Daily horoscope for all 12 zodiac signs — love, career & finance scores (1–5★), lucky color/number/direction, daily affirmation. Morning & evening push. Bilingual EN/CN." keywords: - 星座运势 - 今日星座 - 每日星座 - 星座今日 - 白羊座 - 金牛座 - 双子座 - 巨蟹座 - 狮子座 - 处女座 - 天秤座 - 天蝎座 - 射手座 - 摩羯座 - 水瓶座 - 双鱼座 - 星座配对 - 爱情运势 - 事业运势 - 财运 - 幸运颜色 - 幸运数字 - 星座占卜 - 上升星座 - 月亮星座 - horoscope - zodiac - daily horoscope - astrology - aries - taurus - gemini - cancer - leo - virgo - libra - scorpio - sagittarius - capricorn - aquarius - pisces - love horoscope - weekly horoscope - zodiac compatibility metadata: openclaw: runtime: node: ">=18" --- # 西方星座运势 > 西方星座运势 — 12星座每日运程 · 爱情事业财运 · 幸运元素 · 中英双语 ## 何时使用 - 用户说"今日星座""我的星座运势""白羊座今天怎么样" - 用户想查爱情运/事业运/财运 - 用户说"horoscope""zodiac""星座配对" - 用户说"今天适合表白吗""今天适合谈合同吗" --- ## 推送管理 ```bash node scripts/push-toggle.js on <userId> node scripts/push-toggle.js on <userId> --morning 08:00 --evening 21:00 --channel feishu node scripts/push-toggle.js off <userId> node scripts/push-toggle.js status <userId> ``` 支持渠道：`telegram` / `feishu` / `slack` / `discord`

path: README.md
content: # Daily Astro — Western Horoscope Skill

> Daily horoscope for all 12 zodiac signs — love, career, finance scores, lucky elements. Bilingual EN/CN.

[![clawhub](https://img.shields.io/badge/clawhub-daily--astro-blue)](https://clawhub.ai/skills/daily-astro)
[![openclaw](https://img.shields.io/badge/openclaw-skill-orange)](https://openclaw.ai)

An [OpenClaw](https://openclaw.ai) skill that delivers daily Western horoscope readings for all 12 zodiac signs — love, career, and finance scores, lucky color/number/direction, and a daily affirmation. Pairs naturally with [yunshi](https://github.com/jiajiaoy/yunshi) for a complete Chinese + Western astrology experience.

---

## Features

- **12 Zodiac Signs** — Aries through Pisces, all covered daily
- **Three Fortune Scores** — Love 💕 / Career 💼 / Finance 💰 (1–5 stars each)
- **Lucky Elements** — Color, number, direction for today
- **Daily Affirmation** — One guiding sentence per sign
- **Evening Preview** — Tomorrow's top 3 signs + watch-out signs
- **Bilingual** — Chinese and English output

---

## Daily Push Schedule

| Push | Time | Content |
|------|------|---------|
| Morning | 08:00 | Today's horoscope for all 12 signs |
| Evening | 21:00 | Tomorrow's horoscope preview + lucky tips |

```bash
node scripts/push-toggle.js on <userId>
node scripts/push-toggle.js on <userId> --morning 08:00 --evening 21:00 --channel feishu
node scripts/push-toggle.js off <userId>
node scripts/push-toggle.js status <userId>
```

Supported channels: `telegram` / `feishu` / `slack` / `discord`

---

## Trigger Words

星座运势、今日星座、白羊座、金牛座、双子座、巨蟹座、狮子座、处女座、天秤座、天蝎座、射手座、摩羯座、水瓶座、双鱼座、horoscope、zodiac、daily horoscope、astrology、love horoscope

---

*MIT License · OpenClaw Skill*

## Keywords

horoscope · daily horoscope · zodiac · astrology · western astrology · Aries · Taurus · Gemini · Cancer · Leo · Virgo · Libra · Scorpio · Sagittarius · Capricorn · Aquarius · Pisces · 星座运势 · 今日星座 · 每日运势 · 星座

---

Built for [OpenClaw](https://openclaw.ai) · Published on [clawhub.ai](https://clawhub.ai/skills/daily-astro)


path: _meta.json
content: {
  "slug": "daily-astro",
  "version": "1.0.0",
  "runtime": {
    "node": ">=18"
  },
  "ownerId": "kn79bebfnwg15sb0g7cj5z5nyd83gxh0"
}

path: package.json
content: {
  "name": "daily-horoscope-west",
  "version": "1.0.2",
  "description": "Daily horoscope for all 12 zodiac signs — love, career & finance scores (1–5★), lucky color/number/direction, daily affirmation. Morning & evening push. Bilingual EN/CN.",
  "engines": {
    "node": ">=18"
  },
  "license": "MIT"
}

path: scripts/evening-push.js
content: #!/usr/bin/env node
'use strict';
const fs=require('fs'),path=require('path');
const USERS_DIR=path.join(__dirname,'../data/users');
function sanitizeId(v){if(typeof v!=='string'||!/^[a-zA-Z0-9_-]{1,128}$/.test(v)){console.error('invalid userId');process.exit(1);}return v;}
function safeUserPath(u){const r=path.resolve(USERS_DIR,u+'.json');if(!r.startsWith(path.resolve(USERS_DIR)+path.sep)){console.error('illegal path');process.exit(1);}return r;}
function loadUser(u){const f=safeUserPath(u);return fs.existsSync(f)?JSON.parse(fs.readFileSync(f,'utf8')):{};}
const userId=sanitizeId(process.argv[2]||'default');
loadUser(userId);
const now=new Date();
const WEEKDAYS=['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
const wd=now.getDay();
const date=`${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}`;
const weekday=WEEKDAYS[wd];
const month=now.getMonth()+1;
const day=now.getDate();
const tomorrow_weekday=WEEKDAYS[(wd+1)%7];
console.log(`晚安星座播报🌟今天是${date}。请生成明日12星座运势预告：①明日最佳星座TOP3（综合运势最旺）②明日需要注意的星座（有小波折）③明日全体幸运提示1条④一句睡前星座冥想语。简洁有趣，中英双语。`);


path: scripts/morning-push.js
content: #!/usr/bin/env node
'use strict';
const fs=require('fs'),path=require('path');
const USERS_DIR=path.join(__dirname,'../data/users');
function sanitizeId(v){if(typeof v!=='string'||!/^[a-zA-Z0-9_-]{1,128}$/.test(v)){console.error('invalid userId');process.exit(1);}return v;}
function safeUserPath(u){const r=path.resolve(USERS_DIR,u+'.json');if(!r.startsWith(path.resolve(USERS_DIR)+path.sep)){console.error('illegal path');process.exit(1);}return r;}
function loadUser(u){const f=safeUserPath(u);return fs.existsSync(f)?JSON.parse(fs.readFileSync(f,'utf8')):{};}
const userId=sanitizeId(process.argv[2]||'default');
loadUser(userId);
const now=new Date();
const WEEKDAYS=['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
const wd=now.getDay();
const date=`${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}`;
const weekday=WEEKDAYS[wd];
const month=now.getMonth()+1;
const day=now.getDate();
const tomorrow_weekday=WEEKDAYS[(wd+1)%7];
console.log(`今天是${weekday}（${date}），请为12个西方星座生成今日运势推送。每个星座包含：①今日综合运势简评（2句）②爱情💕/事业💼/财运💰各项指数（1-5颗星）③今日幸运颜色、数字、方位④一句今日箴言。输出格式清晰，中英双语，适合每日推送。星座顺序：白羊/金牛/双子/巨蟹/狮子/处女/天秤/天蝎/射手/摩羯/水瓶/双鱼。`);


path: scripts/push-toggle.js
content: #!/usr/bin/env node
'use strict';
const fs=require('fs'),path=require('path');
const SKILL='daily-astro',DEFAULT_MORNING='08:00',DEFAULT_EVENING='21:00';
const USERS_DIR=path.join(__dirname,'../data/users');
const ALLOWED_CH=new Set(['telegram','feishu','slack','discord']);
function sanitizeId(v){if(typeof v!=='string'||!/^[a-zA-Z0-9_-]{1,128}$/.test(v)){console.error('invalid userId');process.exit(1);}return v;}
function sanitizeTime(v,l){if(!/^\d{1,2}:\d{2}$/.test(v)){console.error('invalid '+l);process.exit(1);}const[h,m]=v.split(':').map(Number);if(h>23||m>59){console.error('invalid '+l);process.exit(1);}return{h,m};}
function safeUserPath(u){const r=path.resolve(USERS_DIR,u+'.json');if(!r.startsWith(path.resolve(USERS_DIR)+path.sep)){console.error('illegal path');process.exit(1);}return r;}
function loadUser(u){const f=safeUserPath(u);return fs.existsSync(f)?JSON.parse(fs.readFileSync(f,'utf8')):{};}
function saveUser(u,d){fs.mkdirSync(USERS_DIR,{recursive:true});fs.writeFileSync(safeUserPath(u),JSON.stringify(d,null,2),'utf8');}
function enablePush(userId,opts){
  userId=sanitizeId(userId);
  const user=loadUser(userId);
  const mt=opts.morning||user.morningTime||DEFAULT_MORNING;
  const et=opts.evening||user.eveningTime||DEFAULT_EVENING;
  const{h:mh,m:mm}=sanitizeTime(mt,'--morning');
  const{h:eh,m:em}=sanitizeTime(et,'--evening');
  const ch=opts.channel||user.channel||'telegram';
  if(!ALLOWED_CH.has(ch)){console.error('unsupported channel:'+ch);process.exit(1);}
  const sk=`agent:main:${ch}:direct:${userId}`;
  console.log('__OPENCLAW_CRON_ADD__:'+JSON.stringify({name:`${SKILL}-morning-${userId}`,cronExpr:`${mm} ${mh} * * *`,tz:'Asia/Shanghai',session:'isolated',sessionKey:sk,channel:ch,to:userId,announce:true,timeoutSeconds:180,message:`node ${path.join(__dirname,'morning-push.js')} ${userId}`}));
  console.log('__OPENCLAW_CRON_ADD__:'+JSON.stringify({name:`${SKILL}-evening-${userId}`,cronExpr:`${em} ${eh} * * *`,tz:'Asia/Shanghai',session:'isolated',sessionKey:sk,channel:ch,to:userId,announce:true,timeoutSeconds:180,message:`node ${path.join(__dirname,'evening-push.js')} ${userId}`}));
  saveUser(userId,{...user,morningTime:mt,eveningTime:et,channel:ch,pushEnabled:true,updatedAt:new Date().toISOString()});
  console.log(`\n✅ ${SKILL} 推送已开启\n⏰ 早推: ${mt}  🌙 晚推: ${et}  📡 渠道: ${ch}\n关闭: node push-toggle.js off ${userId}`);
}
function disablePush(userId){
  userId=sanitizeId(userId);
  console.log(`__OPENCLAW_CRON_RM__:${SKILL}-morning-${userId}`);
  console.log(`__OPENCLAW_CRON_RM__:${SKILL}-evening-${userId}`);
  saveUser(userId,{...loadUser(userId),pushEnabled:false,updatedAt:new Date().toISOString()});
  console.log(`✅ ${SKILL} 推送已关闭`);
}
function showStatus(userId){
  userId=sanitizeId(userId);
  const u=loadUser(userId);
  console.log(`\n📡 ${SKILL} — ${userId}\n状态: ${u.pushEnabled?'✅ 开启':'❌ 关闭'}  早推: ${u.morningTime||DEFAULT_MORNING}  晚推: ${u.eveningTime||DEFAULT_EVENING}  渠道: ${u.channel||'telegram'}\n`);
}
if(require.main!==module)return;
const[cmd,uid,...rest]=process.argv.slice(2);
if(!cmd||!uid){console.log('Usage: node push-toggle.js on|off|status <userId>');process.exit(1);}
const opts={};
const mi=rest.indexOf('--morning');if(mi!==-1)opts.morning=rest[mi+1];
const ei=rest.indexOf('--evening');if(ei!==-1)opts.evening=rest[ei+1];
const ci=rest.indexOf('--channel');if(ci!==-1)opts.channel=rest[ci+1];
if(cmd==='on')enablePush(uid,opts);
else if(cmd==='off')disablePush(uid);
else if(cmd==='status')showStatus(uid);
else{console.error('unknown cmd:'+cmd);process.exit(1);}


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)