Add fullscreen_message skill

This commit is contained in:
aeroreyna
2026-02-18 10:17:09 -05:00
parent 5d8623a6e0
commit 7865dd48e8
16 changed files with 58 additions and 384 deletions
+21
View File
@@ -0,0 +1,21 @@
# fullscreen_message Skill
This skill lets you display a fullscreen message popup on the local machine, triggered from the command line (or by other agents/automations). Great for visual notifications, alerts, or simple messages—works outside of chat.
## Usage
```
python3 fullscreen_message_skill.py --message "Your custom message!" --timeout 5
```
- `--message` (optional): The text to display (default: "Hello, Adolfo!")
- `--timeout` (optional): Auto-dismiss after N seconds (default: wait for key or click)
You can connect this to a heartbeat or automation to make sure you never miss an alert!
---
### To Expand
- Color, font size, dynamic sizing
- Interactive elements (buttons, input)
- Multi-message queue
- Two-way local interaction
@@ -0,0 +1,37 @@
import argparse
import pygame
import sys
import time
def main():
parser = argparse.ArgumentParser(description="Display a fullscreen message.")
parser.add_argument('--message', type=str, default="Hello, Adolfo!", help='Message to display')
parser.add_argument('--timeout', type=int, default=0, help='Seconds before auto-dismiss (0 = wait for key)')
args = parser.parse_args()
pygame.init()
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
pygame.display.set_caption("Fullscreen Message")
font = pygame.font.Font(None, 100)
text = font.render(args.message, True, (255, 255, 255))
text_rect = text.get_rect(center=(screen.get_width() // 2, screen.get_height() // 2))
clock = pygame.time.Clock()
start_time = time.time()
running = True
while running:
for event in pygame.event.get():
if event.type in (pygame.QUIT, pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN):
running = False
if args.timeout and (time.time() - start_time) > args.timeout:
running = False
screen.fill((0, 0, 0))
screen.blit(text, text_rect)
pygame.display.flip()
clock.tick(30)
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
-8
View File
@@ -1,8 +0,0 @@
# macOS Calendar
Description: Manage calendar events via the macOS Calendar app using AppleScript.
## Tools
### list
Description: List calendar events for the next N days (default 7).
Command: osascript skills/macos-calendar/fetch_events.scpt {{days}}
-57
View File
@@ -1,57 +0,0 @@
#!/usr/bin/osascript
on run argv
set daysToFetch to 7
if (count of argv) > 0 then
set daysToFetch to (item 1 of argv) as integer
end if
set currentDate to current date
set endDate to currentDate + (daysToFetch * days)
tell application "Calendar"
set output to ""
set allCalendars to calendars
repeat with aCal in allCalendars
set calName to name of aCal
-- Filter out holidays or subscription calendars if they are too noisy, but for now keep all.
set calEvents to (every event of aCal whose start date is greater than or equal to currentDate and start date is less than or equal to endDate)
repeat with anEvent in calEvents
try
set evtSummary to summary of anEvent
on error
set evtSummary to "No Title"
end try
try
set evtStart to start date of anEvent
on error
set evtStart to "Unknown"
end try
try
set evtEnd to end date of anEvent
on error
set evtEnd to "Unknown"
end try
try
set evtLoc to location of anEvent
if evtLoc is missing value then set evtLoc to ""
on error
set evtLoc to ""
end try
set output to output & "Calendar: " & calName & "\nEvent: " & evtSummary & "\nStart: " & evtStart & "\nEnd: " & evtEnd & "\nLocation: " & evtLoc & "\n---\n"
end repeat
end repeat
if output is "" then
return "No events found for the next " & daysToFetch & " days."
else
return output
end if
end tell
end run
-12
View File
@@ -1,12 +0,0 @@
# macOS Mail
Description: Manage emails via the macOS Mail app using AppleScript. Can fetch recent emails and send new ones.
## Tools
### fetch
Description: Get the latest N emails from the inbox.
Command: osascript skills/macos-mail/fetch_emails.scpt {{limit}}
### send
Description: Send an email using the default account in macOS Mail.
Command: osascript skills/macos-mail/send_email.scpt "{{to}}" "{{subject}}" "{{body}}"
-42
View File
@@ -1,42 +0,0 @@
#!/usr/bin/osascript
on run argv
set messageLimit to 10
if (count of argv) > 0 then
set messageLimit to (item 1 of argv) as integer
end if
tell application "Mail"
set output to ""
set inboxMessages to (messages of inbox whose read status is false)
-- Fallback to all messages if no unread, or just take top N
if (count of inboxMessages) is 0 then
set inboxMessages to messages of inbox
end if
-- Sort logic is hard in pure AppleScript efficiently, usually they come in order.
-- We'll just grab the first N items which are usually newest.
set msgCount to count of inboxMessages
if msgCount > messageLimit then
set msgCount to messageLimit
end if
repeat with i from 1 to msgCount
set msg to item i of inboxMessages
set msgSubject to subject of msg
set msgSender to sender of msg
set msgDate to date received of msg
set msgContent to content of msg
-- Truncate content if too long
if (length of msgContent) > 500 then
set msgContent to (text 1 thru 500 of msgContent) & "..."
end if
set output to output & "From: " & msgSender & "\nSubject: " & msgSubject & "\nDate: " & msgDate & "\nBody: " & msgContent & "\n---\n"
end repeat
return output
end tell
end run
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/osascript
on run argv
if (count of argv) < 2 then
return "Usage: send_email.scpt <recipient> <subject> <body>"
end if
set recipientAddress to item 1 of argv
set msgSubject to item 2 of argv
set msgContent to item 3 of argv
tell application "Mail"
set newMessage to make new outgoing message with properties {subject:msgSubject, content:msgContent, visible:false}
tell newMessage
make new to recipient at end of to recipients with properties {address:recipientAddress}
end tell
send newMessage
end tell
return "Email sent to " & recipientAddress
end run
-12
View File
@@ -1,12 +0,0 @@
# macOS Notes
Description: Manage Apple Notes via AppleScript.
## Tools
### list
Description: List recent notes or search by title.
Command: osascript skills/macos-notes/list_notes.scpt "{{query}}"
### add
Description: Create a new note or append to an existing one.
Command: osascript skills/macos-notes/add_note.scpt "{{title}}" "{{body}}"
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/osascript
on run argv
if (count of argv) < 2 then
return "Usage: add_note.scpt <title> <body>"
end if
set noteTitle to item 1 of argv
set noteBody to item 2 of argv
-- Apple Notes expects HTML for the body.
-- We'll wrap the body in minimal HTML.
set htmlBody to "<div>" & noteBody & "</div>"
-- Replace newlines with <br>
set htmlBody to do shell script "echo " & quoted form of htmlBody & " | sed 's/$/<br>/'"
tell application "Notes"
tell default account
-- Check if note exists to append, or create new
set existingNotes to (notes whose name is noteTitle)
if (count of existingNotes) > 0 then
set currentNote to item 1 of existingNotes
set currentBody to body of currentNote
-- Append to existing body
set body of currentNote to currentBody & "<br>" & htmlBody
return "Appended to note: " & noteTitle
else
make new note with properties {name:noteTitle, body:htmlBody}
return "Created new note: " & noteTitle
end if
end tell
end tell
end run
-32
View File
@@ -1,32 +0,0 @@
#!/usr/bin/osascript
on run argv
set searchObj to ""
if (count of argv) > 0 then
set searchObj to item 1 of argv
end if
tell application "Notes"
if searchObj is "" then
set foundNotes to notes of default account
-- limiting to top 10 recent
set noteCount to count of foundNotes
if noteCount > 10 then set noteCount to 10
set subNotes to items 1 thru noteCount of foundNotes
else
set subNotes to (notes of default account whose name contains searchObj)
end if
set output to ""
repeat with aNote in subNotes
set nName to name of aNote
set nBody to body of aNote -- This is HTML
set nModDate to modification date of aNote
-- Simple HTML cleanup for readability (very basic)
set nBodyClean to do shell script "echo " & quoted form of nBody & " | sed -e 's/<[^>]*>//g'"
set output to output & "Note: " & nName & "\nModified: " & nModDate & "\nBody: " & nBodyClean & "\n---\n"
end repeat
return output
end tell
end run
-28
View File
@@ -1,28 +0,0 @@
---
name: pi-ssh-access
description: "Remembers and documents how to SSH into the Raspberry Pi (192.168.68.126) as adolforeyna with the current ed25519 key. Use when the user asks for the connection details so Bro can log in from another machine or confirm passwordless access setup."
---
# Raspberry Pi SSH Access
## Key facts Codex should keep handy
- **Host:** `192.168.68.126` (Pi on the local LAN)
- **Username:** `adolforeyna`
- **Default port:** `22` (standard SSH)
- **Public key path (local):** `~/.ssh/id_ed25519.pub`
- **Private key path (local):** `~/.ssh/id_ed25519`
- **SSH key comment:** `adolforeyna@Mac-mini-M4.local`
- **Known hosts entry:** Added via `ssh-keyscan -H 192.168.68.126`
## Steps for Bro to reconnect
1. Ensure the Pi is online and reachable at `192.168.68.126`.
2. Use the generated key automatically: `ssh adolforeyna@192.168.68.126` (no password required).
3. If a host key error resurfaces, rerun `ssh-keyscan -H 192.168.68.126 >> ~/.ssh/known_hosts` and reconnect.
4. When the user requests, copy the public key again with `ssh-copy-id -i ~/.ssh/id_ed25519.pub adolforeyna@192.168.68.126`.
## Reminder for maintenance
- If the key ever changes or is rotated, update this skill and regenerate both `id_ed25519` and `id_ed25519.pub` as needed.
- Keep this skill synced with `tools/ssh` notes if you ever keep a separate reference file.
-8
View File
@@ -1,8 +0,0 @@
# Repo Sync
Description: Sync the current workspace to the configured git repository. Use this after making significant changes to files.
## Tools
### sync
Description: Commit and push all current changes to the remote repository.
Command: ./skills/repo-sync/sync.sh
-12
View File
@@ -1,12 +0,0 @@
#!/bin/bash
cd /Users/adolforeyna/.openclaw/workspace
# Check if there are changes
if [[ -n $(git status -s) ]]; then
git add .
git commit -m "Auto-sync: $(date '+%Y-%m-%d %H:%M:%S')"
git push origin main
echo "Synced changes to repo."
else
echo "No changes to sync."
fi
-23
View File
@@ -1,23 +0,0 @@
# SKILL.md - Say Out Loud
## Purpose
Announce messages out loud through the Mac minis built-in speakers. Plays a chime, sets volume to 80%, and uses the macOS default system voice. Triggers on phrases like “talk to us” or “say out loud.”
## Triggers
- “talk to us”
- “say out loud”
- “announce”
- or by explicit command in the interface
## How it works
- Sets output to built-in speakers (if available)
- Sets volume to 80%
- Plays the macOS "Glass" chime
- Speaks the full answer using the system's default voice (can be changed to Alex, David, etc. on request)
## Usage
Just say “talk to us” or any of the trigger phrases and Bro will say the next message out loud for everyone in the room.
---
Script: `say_out_loud.sh` (see alongside this file)
-20
View File
@@ -1,20 +0,0 @@
#!/bin/bash
# say_out_loud.sh
# Usage: ./say_out_loud.sh "message to say"
MESSAGE="$1"
# Set output to built-in speakers — comment this line if not needed
default_output="$(system_profiler SPAudioDataType | grep -B 5 'Built-in Output' | grep 'Device ID' | awk -F ": " '{print $2}' | head -n1)"
if [[ -n "$default_output" ]]; then
SwitchAudioSource -s "$default_output" 2>/dev/null
fi
# Set system volume to 80%
osascript -e 'set volume output volume 80'
# Play chime
afplay /System/Library/Sounds/Glass.aiff
# Speak the message (default voice)
say "$MESSAGE"
-78
View File
@@ -1,78 +0,0 @@
---
name: say-voice-replies
description: Deliver short, hands-free responses by turning text into macOS `say` voice notes saved in the system temp directory and sending them as Telegram voice messages. Use whenever Adolfo asks for audio answers (e.g., while driving) or you decide speech will land better than text.
---
# Say Voice Replies
## Overview
Use this skill to quickly convert any response into an audio note using the built-in macOS `say` command and deliver it through Telegram as a voice message. The key constraints we uncovered on 2026-02-14 are that the `message` tool refuses files from the workspace and only accepts media written inside the macOS temporary directory, so every workflow below is designed around that.
## Quick Start
1. Draft the spoken script (aim for 1025 seconds unless the user asked for longer).
2. Use the macOS default `say` voice and rate unless Adolfo specifically requests a different vibe.
3. Generate the audio in `$TMPDIR` using `say` → send it with the `message` tool using `asVoice: true`.
4. (Optional) Delete the temp file once the voice note is confirmed delivered.
## Workflow
### 1. Prepare the narration
- Keep phrasing conversational and note any emoji you want to read out loud.
- If the reply needs structure, say the headings (e.g., “Update one… Update two…”).
- Sum up the audio in text when sending the message (“Audio reply: ETA + blockers”) so the user knows what the note contains before tapping it.
### 2. Generate a temp file that the `message` tool accepts
MacOS exposes an agent-specific temp dir via `$TMPDIR`, which resolves to `/var/folders/...` — the only location Telegram uploads accepted during testing.
```bash
TMPFILE=$(mktemp "${TMPDIR:-/tmp}/brovoice_XXXXXX")
OUTFILE="${TMPFILE}.m4a"
```
Use `say` to create the audio (AAC keeps the file small and Telegram-friendly):
```bash
say --data-format=aac \
"Yo bro, leaving the studio now. Should hit the church in fifteen." \
-o "$OUTFILE"
```
Notes:
- The stock system voice/rate keeps everything consistent; only add `-v <VoiceName>` or `-r <speed>` if Adolfo requests a different vibe.
- If you need emphasis, split into multiple `say` calls and concatenate with `afconvert`, but most replies can be cut in one take.
- `say` writes synchronously; no extra waits are needed before sending.
### 3. Send the voice note via Telegram
The `message` tool needs three things:
```json
{
"action": "send",
"channel": "telegram",
"media": "/var/folders/.../brovoice_a1b2c3.m4a",
"asVoice": true,
"message": "Audio reply: key takeaways + ETA"
}
```
Guidelines:
- Always set `asVoice: true` so Telegram renders the clip as a push-to-play voice message instead of a regular file.
- Keep a short text caption that previews the content (helps when the user cant tap immediately).
- If the user explicitly wants only audio, you can keep the caption minimal (e.g., “Audio-only reply”).
### 4. Clean up
Once Telegram confirms `{ "ok": true }`, remove the temp file:
```bash
rm -f "$OUTFILE"
```
This avoids leaving dozens of `.m4a` files inside `/var/folders/...`.
## Tips & Variations
- **Faster follow-ups:** Capture the temp path and reuse it if you plan to cut a second take immediately; otherwise regenerate with `mktemp`.
- **Speaking rate tweaks (optional):** If Adolfo asks for faster/slower delivery, `say -r 160` ≈ relaxed, `-r 190` ≈ energetic; stay under `-r 210` for intelligibility while driving.
- **Edge cases:** If `say` errors (rare on headless sessions), fall back to the built-in `tts` tool—it already writes to the same temp tree and produces a `MEDIA:/path/to/file.mp3` string you can feed directly into `message`.
- **Captioning:** When summarizing long instructions, include bullet cues in the text reply while the audio carries the nuance.
Stick to this flow whenever Adolfo asks for a voice response (especially while driving) or when tone-of-voice will land better than plain text.