Skill: turingcorp-net/smart-surprise (version 1.1.0)

--- name: smart-surprise description: A proactive AI companion that initiates casual conversations with users at unpredictable times, making the AI feel like a real partner rather than a passive tool. Use when you want to bring surprise, warmth, and variety to user interactions — sending greetings, sharing tips, checking in on wellbeing, or sparking interesting conversations based on the user's personal preferences. Activate when: (1) a user installs this skill and triggers the initial cron, (2) you want to proactively reach out to a user instead of always waiting to be messaged first. This skill manages a self-perpetuating chain of one-shot cron jobs — each run delivers a personalized message, learns from user preferences, schedules the next random trigger, then deletes itself. --- # Smart Surprise ## What This Skill Does Smart Surprise transforms a passive AI assistant into a **proactive companion** that reaches out to users at unpredictable moments — with greetings, care, tips, news, or just a friendly chat. Over time it learns what the user likes and tailors content accordingly, making every interaction feel more personal. Unlike fixed scheduled tasks that feel mechanical, Smart Surprise uses a **chain of self-deleting one-shot cron jobs** to create organic, surprise-driven interactions. The chain runs indefinitely and **learns continuously** from what the user responds to. ## How the Core Loop Works ``` [One-shot cron fires at scheduled time] ↓ [Main session agent runs] ↓ [Reads config + topics/preferences from topics.md] ↓ [Selects 2-3 topics based on user preferences] ↓ [Composes a personalized message] ↓ [Sends to user via openclaw message send] ↓ [Updates user preferences in topics.md] ↓ [Schedules next random trigger] ↓ [Deletes itself — chain continues permanently] ``` ## Workflow ### Phase 1: Initial Setup (One Time Only) When the user first installs and activates Smart Surprise, an initial one-shot cron job must be created. This is the **only manual step** — everything after is automatic and self-learning. See [references/setup.md](references/setup.md) for the complete step-by-step setup guide. ### Phase 2: Runtime (Self-Perpetuating Chain) Each triggered run executes the following steps: **Step 1: Read configuration and preferences** - Read `~/.openclaw/workspace/skills/smart-surprise/config.json` — static settings (timezone, location, intervals, etc.) - Read `~/.openclaw/workspace/skills/smart-surprise/references/topics.md` — available topics + learned user preferences **Step 2: Determine the next trigger time** - Generate a random delay between `minIntervalMinutes` and `maxIntervalMinutes` - If the next trigger would fall inside quiet hours → shift to `quietHoursEnd` - Write the next scheduled time to `~/.openclaw/workspace/skills/smart-surprise/next_run.json` **Step 3: Compose the message** - Check current time and day in the user's configured timezone - Randomly select **2-3 topics** from the active pool, weighted by user preference (topics the user likes are more likely to be selected) - **Always include ≥1 interaction topic** (greeting, time-care, or check-in) - Combine into a structured message: Opening → Body → Closing - Generate content for each selected topic **Step 4: Deliver the message** ``` openclaw message send --channel <channel> --target <channelTarget> --message "<composed_message>" ``` **Step 5: Learn from this interaction** - After sending, silently update `topics.md` with any preference signals observed in this session - Examples of preference signals: - User explicitly says "I don't like this topic" → record as `disliked` - User responds positively to a topic → record as `liked` - User asks for more of something → increase weight - User never responds to a topic → decrease weight - The next run reads the updated preferences automatically **Step 6: Schedule the next run** - Use `openclaw cron add` with the random delay - Set `deleteAfterRun: true` on the new job - The job fires, executes, schedules the next, and deletes itself **Step 7: Exit** - The `deleteAfterRun: true` flag ensures self-deletion after completion - No session cleanup needed — the main session handles this automatically ## Topics System The topics system replaces the traditional "content categories" approach with a **learnable, extensible topic pool**. ### Topics The skill ships with 11 initial topics: - `greeting` — Warm, casual opening (interaction) - `time-care` — Time-aware contextual care (interaction) - `weather` — Weather-based practical suggestions (requires `location` in config) - `calendar` — Google Calendar reminders (requires calendar integration) - `health` — Health micro-tips - `tips` — Useful tips and life hacks - `history` — "On this day" historical facts - `entertainment` — Movie/music recommendations - `quote` — Quotes and poems - `news` — Top news of the day - `check-in` — Emotional connection / wellbeing question (interaction) ### Topic Extensibility **This is not a closed list.** Users or the agent can add new topics at any time by editing `topics.md`. The agent can also create new topics during runtime if the user's interests suggest one. ### Preference Learning Each topic in `topics.md` has a **preference state**: `normal`, `preferred`, or `dislike`. The agent updates these after each interaction based on user signals. - `normal` — selected at normal frequency - `preferred` — selected more often - `dislike` — selected much less often (never fully excluded) The agent listens for signals like "I love this" → `preferred`, "stop" → `dislike`, "talk more about Y" → `preferred`. `check-in` can never be `dislike`. ### Combination Rules - Every message must contain at least 1 **interaction topic** - Default: 2 topics per message (1 interaction + 1 informational) - Maximum: 3 topics per message (never overwhelm) ### Message Structure ``` 1. Opening: greeting or time-care (sets emotional tone) 2. Body: 1-2 informational topics (tips/news/quote/etc.) 3. Closing: check-in or time-care (invites a response) ``` See [references/topics.md](references/topics.md) for the complete topic definitions, default weights, and preference learning rules. ## Configuration All static configuration is in `~/.openclaw/workspace/skills/smart-surprise/config.json`. **No hardcoded values.** | Parameter | Default | Description | |-----------|---------|-------------| | `timezone` | `Asia/Shanghai` | User's IANA timezone | | `location` | (required for weather) | City for weather lookups (e.g., `Beijing`, `New York`) | | `quietHoursStart` | `22:00` | Quiet hours start (HH:MM) | | `quietHoursEnd` | `08:00` | Quiet hours end (HH:MM) | | `minIntervalMinutes` | `60` | Minimum random wait (minutes) | | `maxIntervalMinutes` | `480` | Maximum random wait (minutes) | | `channel` | (required) | OpenClaw channel name (`telegram`, `discord`, etc.) | | `channelTarget` | (required) | Recipient ID on that channel (chat ID, user ID, etc.) | | `characteristics` | `"warm, casual, playful"` | Natural language description of preferred communication style | See [references/config.md](references/config.md) for the complete schema. ## Credentials & Sensitive Files **Google Calendar Integration (optional):** If you enable the `calendar` topic, the skill will attempt to read Google OAuth credentials. Two supported paths: - `~/.openclaw/secrets/google-calendar.json` — OAuth token file - `~/.openclaw/scripts/google-calendar.py` — helper script ⚠️ **These files contain sensitive tokens.** Only provide access if you trust the skill and understand what these files contain. If not configured, the `calendar` topic silently produces no output (no error is shown). ## File Structure ``` ~/.openclaw/workspace/skills/smart-surprise/ ├── config.json # Static configuration (timezone, location, intervals) ├── next_run.json # Runtime state — written by the agent, do NOT edit └── references/ ├── setup.md # Installation guide ├── topics.md # Topic definitions + user preference learning └── config.md # Configuration field reference ``` ## Skill Metadata - **Type:** Proactive interaction / personal companion - **Trigger mechanism:** Self-perpetuating cron chain (one-shot jobs, auto-delete) - **Delivery:** User-configured channel (Telegram, Discord, WhatsApp, etc.) - **Learning:** Continuous, preference-based topic weighting in topics.md - **Installation:** One-time manual setup; chain runs forever and learns over time - **Context:** Each run uses main session (sees conversation history)

path: config.json
content: timezone: Asia/Shanghai
location: Hangzhou
quietHoursStart: 22:00
quietHoursEnd: 08:00
minIntervalMinutes: 30
maxIntervalMinutes: 120
channel: telegram
channelTarget: YOUR_CHANNEL_TARGET
characteristics: warm, casual, playful

path: next_run.json
content: scheduledAt: 2026-04-13T15:34:04.797Z
localTime: 2026-04-13 23:34 CST
intervalMinutes: 237

path: references/config.md
content: # Smart Surprise — Configuration Reference

Complete schema and field-by-field documentation for `config.json`.

## Config File Location

```
~/.openclaw/workspace/skills/smart-surprise/config.json
```

## Full Schema

```json
{
  "timezone": "Asia/Shanghai",
  "location": "Beijing",
  "quietHoursStart": "22:00",
  "quietHoursEnd": "08:00",
  "minIntervalMinutes": 60,
  "maxIntervalMinutes": 480,
  "channel": "telegram",
  "channelTarget": "YOUR_CHANNEL_TARGET",
  "characteristics": "warm, casual, playful"
}
```

## Field Reference

### timezone

**Type:** string  
**Default:** `"Asia/Shanghai"`  
**Format:** IANA timezone identifier (e.g., `Asia/Shanghai`, `America/New_York`, `Europe/London`)

Determines:
- Time-of-day context for greetings (morning/afternoon/evening)
- Quiet hours window calculation
- Day-of-week labels in messages

### location

**Type:** string  
**Default:** none (required for weather topic)  
**Format:** City name (e.g., `Beijing`, `New York`, `London`)

Required for the `weather` topic to fetch accurate local weather data.

Used in: `https://wttr.in/{location}?format=j1`

### quietHoursStart / quietHoursEnd

**Type:** string (HH:MM, 24-hour)  
**Defaults:** `"22:00"` / `"08:00"`

No messages are sent during this window.

**Behavior:**
- If a scheduled trigger lands inside quiet hours → skip it entirely
- If the next random delay would land inside quiet hours → push to `quietHoursEnd` instead
- Quiet hours **do not** count toward the minimum interval wait

---

### minIntervalMinutes / maxIntervalMinutes

**Type:** integer (minutes)  
**Defaults:** `60` / `480` (1 hour / 8 hours)

The random range for the next trigger delay. Each completed run picks a uniformly random value in this range.

| Profile | min | max | Feel |
|---------|-----|-----|------|
| Default | 60 | 480 | Moderate surprise |
| Frequent | 30 | 240 | More surprises |
| Relaxed | 120 | 1440 | Less intrusive |

---

### channelTarget

**Type:** string  
**Required:** Yes

Recipient ID on your configured channel. Used for `openclaw message send --target <channelTarget>`.

**How to find your Telegram chat ID:**
- Message `@userinfobot` on Telegram — it replies with your user ID
- Or check OpenClaw logs: `openclaw sessions list`

---

### channel

**Type:** string  
**Required:** Yes  
**Supported:** `telegram`, `discord`, `slack`, `whatsapp`, `signal`, `msteams`

The delivery channel. Must be configured and enabled in your OpenClaw setup.

### characteristics

**Type:** string (free text)  
**Default:** `"warm, casual, playful"`  

A natural language description of the user's preferred communication style. The agent reads this on every run and uses it to guide the tone, word choice, and personality of every message generated.

The agent also updates this field after interactions if the user expresses a preference for a different style.

**Example values:**
- `"warm, casual, playful"` — friendly, informal, a bit cute
- `"calm, thoughtful, professional"` — measured, reflective, serious tone
- `"gentle, caring, encouraging"` — soft, supportive, warm
- `"witty, funny, light-hearted"` — humorous, entertaining
- `"brief, direct, efficient"` — short, to-the-point, minimal fluff

Free text is intentional — any description the user gives in plain language works.

---

## Runtime State File

**Location:** `~/.openclaw/workspace/skills/smart-surprise/next_run.json`

Written by each run to communicate scheduling info to the next agent. **Do not edit manually.**

---

## Environment-Specific Notes

### Telegram

```json
{
  "channel": "telegram",
  "channelTarget": "YOUR_CHANNEL_TARGET"
}
```

### Discord

```json
{
  "channel": "discord",
  "channelTarget": "YOUR_CHANNEL_TARGET"
}
```



### Google Calendar Integration

⚠️ **Credentials required for this topic.** If enabled, the skill will read:
1. Google OAuth credentials at `~/.openclaw/secrets/google-calendar.json`, or
2. The helper script at `~/.openclaw/scripts/google-calendar.py`

These files contain sensitive tokens — only provide access if you understand what they contain and trust the skill.

If credentials are not configured, the `calendar` topic silently produces no output (no error is shown to the user).

### Weather

The `weather` topic requires `location` to be set in this config file.

---

## Complete Example Configurations

### Example 1: Default (Beijing user)

```json
{
  "timezone": "Asia/Shanghai",
  "location": "Beijing",
  "quietHoursStart": "22:00",
  "quietHoursEnd": "08:00",
  "minIntervalMinutes": 60,
  "maxIntervalMinutes": 480,
  "channel": "telegram",
  "channelTarget": "YOUR_CHANNEL_TARGET",
  "characteristics": "warm, casual, playful"
}
```

### Example 2: New York User (Evening Focus)

```json
{
  "timezone": "America/New_York",
  "location": "New York",
  "quietHoursStart": "23:00",
  "quietHoursEnd": "07:00",
  "minIntervalMinutes": 120,
  "maxIntervalMinutes": 720,
  "channel": "telegram",
  "channelTarget": "YOUR_CHANNEL_TARGET",
  "characteristics": "calm, thoughtful, professional"
}
```


path: references/setup.md
content: # Smart Surprise — Setup Procedure

This guide walks you through installing and activating Smart Surprise on your OpenClaw instance.

## Prerequisites

- Openclaw installed and configured with at least one messaging channel (Telegram, Discord, etc.)
- `openclaw` CLI accessible in your PATH

---

## Step 1: Create the Config File

Create `~/.openclaw/workspace/skills/smart-surprise/config.json`:

```json
{
  "timezone": "Asia/Shanghai",
  "location": "Beijing",
  "quietHoursStart": "22:00",
  "quietHoursEnd": "08:00",
  "minIntervalMinutes": 30,
  "maxIntervalMinutes": 120,
  "channel": "telegram",
  "channelTarget": "<USER_CHAT_ID>"
}
```

### Required Fields

| Field | Description |
|-------|-------------|
| `timezone` | IANA timezone identifier (e.g., `Asia/Shanghai`, `America/New_York`) |
| `location` | City for weather lookups (e.g., `Beijing`, `New York`) |
| `channel` | Your configured Openclaw channel name (`telegram`, `discord`, etc.) |
| `channelTarget` | Recipient ID on that channel (Telegram chat ID, Discord user ID, etc.) |

### Optional Fields

| Field | Default | Description |
|-------|---------|-------------|
| `quietHoursStart` | `"22:00"` | Quiet hours start (HH:MM, 24h) |
| `quietHoursEnd` | `"08:00"` | Quiet hours end (HH:MM, 24h) |
| `minIntervalMinutes` | `30` | Minimum random wait (minutes) |
| `maxIntervalMinutes` | `120` | Maximum random wait (minutes) |

---

## Step 2: Create the Initial Cron Trigger

Run this **once** to start the self-perpetuating chain. The initial trigger should be **~5 minutes after setup** so the agent has time to initialize. Generate the UTC timestamp dynamically:

```bash
# Generate UTC timestamp for 5 minutes from now
INIT_TIME=$(date -u -d "+5 minutes" +"%Y-%m-%dT%H:%MZ")

openclaw cron add \
  --name "Smart Surprise" \
  --session isolated \
  --delete-after-run \
  --at "$INIT_TIME" \
  --message "Read ~/.openclaw/workspace/skills/smart-surprise/SKILL.md and execute Phase 2: Runtime workflow. Deliver a greeting message to the user, then schedule the next random trigger and delete this job." \
  --timeout-seconds 900
```

> **Important:** Do NOT use a fixed timestamp like `2026-04-10T09:00Z`. Always use a dynamic timestamp (`now + 5 minutes`) so the first trigger fires shortly after the skill is installed, not at some arbitrary future date.

### Recommended Cron Job Parameters (JSON equivalent)

If you manage cron jobs via the JSON config file (e.g., editing `~/.openclaw/cron/jobs.json`), use these settings:

```json
{
  "name": "Smart Surprise",
  "enabled": true,
  "deleteAfterRun": true,
  "schedule": {
    "kind": "at",
    "at": "<INIT_TIME>"
  },
  "sessionTarget": "isolated",
  "wakeMode": "now",
  "payload": {
    "kind": "agentTurn",
    "message": "Read ~/.openclaw/workspace/skills/smart-surprise/SKILL.md and execute Phase 2: Runtime workflow. Deliver a greeting message to the user, then schedule the next random trigger and delete this job.",
    "timeoutSeconds": 900
  },
  "delivery": {
    "mode": "none"
  }
}
```

> `<INIT_TIME>`: Use a UTC timestamp in ISO 8601 format (e.g., `2026-04-10T09:05:00.000Z`). Generate dynamically as `now + 5 minutes` so the first trigger fires shortly after installation, not at an arbitrary future date.

### Key parameters explained

| Parameter | Recommended Value | Why |
|-----------|-----------------|-----|
| `sessionTarget` | `isolated` | Runs in a fresh isolated session — avoids session-routing issues that cause hangs or token errors |
| `wakeMode` | `now` | Fires immediately at the scheduled time |
| `payload.timeoutSeconds` | `900` (15 min) | Gives the agent enough time to compose and send a message |
| `deleteAfterRun` | `true` | Job deletes itself after completion — enables the self-perpetuating chain |
| `delivery.mode` | `none` | Do NOT use `announce` — it sends all agent thinking/output to the user. Agent sends the final message via `openclaw message send` in SKILL.md Step 4. |

### ⚠️ Common Mistakes When Setting Up Cron Manually

> **Mistake 1 — Using `session:agent:main:telegram:direct:<chatId>` for `sessionTarget`**
>
> This session key relies on the Telegram plugin being active and correctly routed. It often fails with `Telegram bot token missing for account "default"` or causes the job to hang. Use `isolated` instead.
>
> **Mistake 2 — Using `--session main` in the CLI**
>
> This routes to the main embedded session, which gets blocked if the user is currently chatting. The job will hang in `running` state forever. Use `--session isolated` instead.
>
> **Mistake 3 — Using `mode: announce` in `delivery`**
>
> Setting `delivery.mode: "announce"` will send the agent's entire output (including all thinking and intermediate steps) to the user — which reveals the agent's internal reasoning process. Always use `mode: "none"` here. The agent sends the final message precisely via `openclaw message send` in SKILL.md Step 4.

### Finding Your Chat ID

- **Telegram:** Message `@userinfobot` — it replies with your numeric user ID
- **Other channels:** Check OpenClaw logs or session keys when you send a test message

---

## Step 3: Verify

```bash
openclaw cron list | grep -i surprise
```

You should see a `Smart Surprise` entry with status `running` or `idle`.

---

## How the Chain Works

1. The initial cron fires at the scheduled time
2. The agent reads config + topics.md, composes a message, sends it, updates preferences, schedules the next random trigger, then deletes itself
3. The next trigger fires at the random time (between `minIntervalMinutes` and `maxIntervalMinutes`)
4. Repeat forever

The agent continuously learns your preferences over time by updating topics.md after each interaction.

---

## Troubleshooting

### Job stuck in `running` state forever

**Cause:** Used `--session main` or `session:agent:main:telegram:direct:<chatId>` — the main session was busy when the job fired, or the Telegram session routing failed with a token error.

**Fix:**
```bash
# Find and remove the stuck job
openclaw cron list | grep -i surprise
openclaw cron rm <job-id>   # repeat for each job found

# Recreate with correct session target (isolated)
openclaw cron add \
  --name "Smart Surprise" \
  --session isolated \
  --delete-after-run \
  --at "<future-UTC-time>" \
  --message "Read ~/.openclaw/workspace/skills/smart-surprise/SKILL.md and execute Phase 2: Runtime workflow..." \
  --timeout-seconds 900
```

> **Note:** Always use `isolated` as the session target. The old `session:agent:main:telegram:direct:<chatId>` approach is prone to `Telegram bot token missing for account "default"` errors and session-routing hangs.

### Message appears to send but user doesn't receive it

**Check:** Is `delivery.channel` set to your configured channel name (e.g., `telegram`) and `delivery.to` set to the correct recipient chat ID?

### To check logs in real-time

```bash
openclaw logs 2>&1 | grep -i "smart-surprise\|telegram\|cron.*deliver"
```

---

## Uninstallation

To completely remove Smart Surprise:

```bash
# Step 1: Stop and remove all Smart Surprise cron jobs
openclaw cron list | grep -i surprise
openclaw cron rm <job-id>   # repeat for each job found

# Step 2: Remove the skill directory
rm -rf ~/.openclaw/workspace/skills/smart-surprise

# Step 3: Optionally remove runtime state
rm -f ~/.openclaw/workspace/skills/smart-surprise/next_run.json
```

This fully removes Smart Surprise from your system.


path: references/topics.md
content: # Smart Surprise — Topics Reference

The single source of truth for all content topics and user preferences.

**This file is read and updated by the agent on every run.**

## Topics

**The agent may add new topics here during runtime based on the user's interests.** The 11 below are the initial set.

### greeting [interaction] [normal]
A warm, casual greeting. Match the tone to the time of day. Be friendly and a bit playful — never robotic.

### time-care [interaction] [normal]
Time-aware care or a contextual nudge. Something relevant to the current hour or moment in the user's day. Keep it short and actionable.

### weather [information] [normal]
A brief weather observation with one practical suggestion based on conditions. Requires `location` in config.

### calendar [information] [normal]
A reminder of today's upcoming calendar events from Google Calendar. Brief and scannable. Skip silently if no events.

### health [information] [normal]
A single, short health micro-tip. Rotate across hydration, movement, posture, eye rest. Never preachy.

### tips [information] [normal]
One useful or interesting tip, life hack, or productivity suggestion. Always include a brief "why it matters."

### history [information] [normal]
Something fascinating that happened on this exact date in history. Engaging, not dry. Pick something that sparks curiosity.

### entertainment [information] [normal]
A recommendation for a movie, song, book, or show. Keep it brief — one sentence on why it's worth it.

### quote [information] [normal]
A meaningful quote, line of poetry, or saying. Match the mood or season. Source it properly.

### news [information] [normal]
One interesting or thought-provoking news story of the day. Not just a headline — explain why it's worth knowing.

### check-in [interaction] [normal]
A warm, genuine question about how the user is doing. This is the most important interaction topic — it must appear in every message. Keep it casual and caring.

---

## Preference Learning

After each message, the agent silently updates the `preference` field of the topics in this file.

**Three states:**

| State | Meaning |
|-------|---------|
| `normal` | Default — selected at normal frequency |
| `preferred` | User likes this topic — selected more often |
| `dislike` | User dislikes this topic — selected much less often |

**How to update:**

| Signal | New preference |
|--------|---------------|
| User says "I love X" / responds very positively | `preferred` |
| User explicitly asks for more of this topic | `preferred` |
| User says "I don't like X" / "please stop" | `dislike` |
| User reacts negatively or ignores this topic repeatedly | downgrade one level |
| User says "wish you talked about Y more" | `preferred` |

**Rules:**
- `check-in` can never be set to `dislike` — always keep at minimum `normal`
- `dislike` means rarely selected, not fully excluded

**📋 Silent Preference Updates:**
The agent updates preferences silently after each interaction — this is intentional to keep the conversation flow natural. However, you can monitor all preference changes at any time by reading `topics.md`. If you notice any topic has been wrongly classified (e.g., `dislike` when you actually liked it), simply edit `topics.md` to correct it — the agent will respect your manual override on the next run.

---

## Topic Selection

On each run, the agent:

1. Filters out topics marked `dislike`
2. Selects 2–3 topics, favoring those marked `preferred` when possible
3. Always selects at least 1 `type: interaction` topic
4. Places `check-in` as either the opening or the closing of the message

**Message structure:**
```
Opening:  greeting or time-care
Body:     1–2 information topics
Closing:  check-in or time-care
```


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