gfclaw 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +81 -1
- package/package.json +3 -3
- package/skill/SKILL.md +102 -3
- package/skill/data/activities.json +121 -0
- package/skill/data/styles.json +45 -0
- package/skill/scripts/gfclaw-looks.sh +249 -0
- package/skill/scripts/gfclaw-proactive.sh +140 -0
- package/skill/scripts/gfclaw-selfie.sh +54 -3
- package/skill/systemd/gfclaw-proactive.service +15 -0
- package/skill/systemd/gfclaw-proactive.timer +20 -0
- package/templates/soul-injection.md +357 -91
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# gfclaw-looks.sh
|
|
3
|
+
# Manage multiple reference image "looks" for GFClaw selfie generation
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./gfclaw-looks.sh list [workspace_dir]
|
|
7
|
+
# ./gfclaw-looks.sh save <name> <image_path> [workspace_dir]
|
|
8
|
+
# ./gfclaw-looks.sh delete <name> [workspace_dir]
|
|
9
|
+
# ./gfclaw-looks.sh active [workspace_dir]
|
|
10
|
+
#
|
|
11
|
+
# Looks are stored as <workspace>/looks/<name>.png
|
|
12
|
+
# Set GFCLAW_LOOK=<name> when calling gfclaw-selfie.sh to use a specific look.
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# Colors for output
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
GREEN='\033[0;32m'
|
|
19
|
+
YELLOW='\033[1;33m'
|
|
20
|
+
BLUE='\033[0;34m'
|
|
21
|
+
NC='\033[0m' # No Color
|
|
22
|
+
|
|
23
|
+
log_info() {
|
|
24
|
+
echo -e "${GREEN}[INFO]${NC} $1"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
log_warn() {
|
|
28
|
+
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
log_error() {
|
|
32
|
+
echo -e "${RED}[ERROR]${NC} $1"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
show_usage() {
|
|
36
|
+
echo -e "${BLUE}GFClaw Looks Manager${NC}"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "Usage:"
|
|
39
|
+
echo " $0 list [workspace_dir] - List all saved looks"
|
|
40
|
+
echo " $0 save <name> <image_path> [ws_dir] - Save an image as a named look"
|
|
41
|
+
echo " $0 delete <name> [workspace_dir] - Delete a named look"
|
|
42
|
+
echo " $0 active [workspace_dir] - Show current active reference"
|
|
43
|
+
echo ""
|
|
44
|
+
echo "Examples:"
|
|
45
|
+
echo " $0 list"
|
|
46
|
+
echo " $0 save casual ~/photos/casual-outfit.png"
|
|
47
|
+
echo " $0 save gym /tmp/gym-look.png ~/.openclaw/workspace-gfclaw"
|
|
48
|
+
echo " $0 delete formal"
|
|
49
|
+
echo " $0 active"
|
|
50
|
+
echo ""
|
|
51
|
+
echo "Then use a look with gfclaw-selfie.sh:"
|
|
52
|
+
echo " GFCLAW_LOOK=casual ./gfclaw-selfie.sh 'at the mall' 'tg:401471440'"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Parse command
|
|
56
|
+
COMMAND="${1:-}"
|
|
57
|
+
if [ -z "$COMMAND" ]; then
|
|
58
|
+
show_usage
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# ---- COMMAND: list ----
|
|
63
|
+
cmd_list() {
|
|
64
|
+
local WORKSPACE="${1:-$HOME/.openclaw/workspace-gfclaw}"
|
|
65
|
+
local LOOKS_DIR="${WORKSPACE}/looks"
|
|
66
|
+
|
|
67
|
+
echo -e "${BLUE}=== Saved Looks ===${NC}"
|
|
68
|
+
echo -e "Directory: ${LOOKS_DIR}"
|
|
69
|
+
echo ""
|
|
70
|
+
|
|
71
|
+
if [ ! -d "$LOOKS_DIR" ]; then
|
|
72
|
+
log_warn "Looks directory does not exist yet: $LOOKS_DIR"
|
|
73
|
+
echo "Save your first look with: $0 save <name> <image_path>"
|
|
74
|
+
return 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
local COUNT=0
|
|
78
|
+
for IMG in "$LOOKS_DIR"/*.png; do
|
|
79
|
+
[ -f "$IMG" ] || continue
|
|
80
|
+
local NAME
|
|
81
|
+
NAME=$(basename "$IMG" .png)
|
|
82
|
+
local SIZE
|
|
83
|
+
SIZE=$(du -h "$IMG" | cut -f1)
|
|
84
|
+
local MODIFIED
|
|
85
|
+
MODIFIED=$(date -r "$IMG" "+%Y-%m-%d %H:%M" 2>/dev/null || stat -c "%y" "$IMG" 2>/dev/null | cut -d. -f1 || echo "unknown")
|
|
86
|
+
echo -e " ${GREEN}•${NC} ${NAME} (${SIZE}, modified: ${MODIFIED})"
|
|
87
|
+
COUNT=$((COUNT + 1))
|
|
88
|
+
done
|
|
89
|
+
|
|
90
|
+
if [ "$COUNT" -eq 0 ]; then
|
|
91
|
+
echo " (no looks saved yet)"
|
|
92
|
+
echo ""
|
|
93
|
+
echo "Save your first look with: $0 save <name> <image_path>"
|
|
94
|
+
else
|
|
95
|
+
echo ""
|
|
96
|
+
echo "Total: $COUNT look(s)"
|
|
97
|
+
fi
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# ---- COMMAND: save ----
|
|
101
|
+
cmd_save() {
|
|
102
|
+
local NAME="${1:-}"
|
|
103
|
+
local IMAGE_PATH="${2:-}"
|
|
104
|
+
local WORKSPACE="${3:-$HOME/.openclaw/workspace-gfclaw}"
|
|
105
|
+
local LOOKS_DIR="${WORKSPACE}/looks"
|
|
106
|
+
|
|
107
|
+
if [ -z "$NAME" ]; then
|
|
108
|
+
log_error "Look name is required"
|
|
109
|
+
echo "Usage: $0 save <name> <image_path> [workspace_dir]"
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
if [ -z "$IMAGE_PATH" ]; then
|
|
114
|
+
log_error "Image path is required"
|
|
115
|
+
echo "Usage: $0 save <name> <image_path> [workspace_dir]"
|
|
116
|
+
exit 1
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Validate name (alphanumeric, hyphens, underscores only)
|
|
120
|
+
if ! echo "$NAME" | grep -qE '^[a-zA-Z0-9_-]+$'; then
|
|
121
|
+
log_error "Invalid look name '$NAME'. Use only letters, numbers, hyphens, and underscores."
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
if [ ! -f "$IMAGE_PATH" ]; then
|
|
126
|
+
log_error "Image file not found: $IMAGE_PATH"
|
|
127
|
+
exit 1
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# Create looks directory if needed
|
|
131
|
+
if [ ! -d "$LOOKS_DIR" ]; then
|
|
132
|
+
mkdir -p "$LOOKS_DIR"
|
|
133
|
+
log_info "Created looks directory: $LOOKS_DIR"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
local DEST="${LOOKS_DIR}/${NAME}.png"
|
|
137
|
+
|
|
138
|
+
if [ -f "$DEST" ]; then
|
|
139
|
+
log_warn "Look '$NAME' already exists — overwriting"
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
cp "$IMAGE_PATH" "$DEST"
|
|
143
|
+
log_info "Saved look '$NAME' → $DEST"
|
|
144
|
+
|
|
145
|
+
local SIZE
|
|
146
|
+
SIZE=$(du -h "$DEST" | cut -f1)
|
|
147
|
+
echo -e " Size: ${SIZE}"
|
|
148
|
+
echo ""
|
|
149
|
+
echo "Use it with: GFCLAW_LOOK=$NAME ./gfclaw-selfie.sh ..."
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# ---- COMMAND: delete ----
|
|
153
|
+
cmd_delete() {
|
|
154
|
+
local NAME="${1:-}"
|
|
155
|
+
local WORKSPACE="${2:-$HOME/.openclaw/workspace-gfclaw}"
|
|
156
|
+
local LOOKS_DIR="${WORKSPACE}/looks"
|
|
157
|
+
|
|
158
|
+
if [ -z "$NAME" ]; then
|
|
159
|
+
log_error "Look name is required"
|
|
160
|
+
echo "Usage: $0 delete <name> [workspace_dir]"
|
|
161
|
+
exit 1
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
local TARGET="${LOOKS_DIR}/${NAME}.png"
|
|
165
|
+
|
|
166
|
+
if [ ! -f "$TARGET" ]; then
|
|
167
|
+
log_error "Look '$NAME' not found at $TARGET"
|
|
168
|
+
exit 1
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
rm -f "$TARGET"
|
|
172
|
+
log_info "Deleted look '$NAME'"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# ---- COMMAND: active ----
|
|
176
|
+
cmd_active() {
|
|
177
|
+
local WORKSPACE="${1:-$HOME/.openclaw/workspace-gfclaw}"
|
|
178
|
+
|
|
179
|
+
echo -e "${BLUE}=== Active Reference ===${NC}"
|
|
180
|
+
echo ""
|
|
181
|
+
|
|
182
|
+
# Check GFCLAW_LOOK
|
|
183
|
+
if [ -n "${GFCLAW_LOOK:-}" ]; then
|
|
184
|
+
local LOOKS_DIR="${WORKSPACE}/looks"
|
|
185
|
+
local LOOK_FILE="${LOOKS_DIR}/${GFCLAW_LOOK}.png"
|
|
186
|
+
echo -e " GFCLAW_LOOK=${GREEN}${GFCLAW_LOOK}${NC}"
|
|
187
|
+
if [ -f "$LOOK_FILE" ]; then
|
|
188
|
+
local SIZE
|
|
189
|
+
SIZE=$(du -h "$LOOK_FILE" | cut -f1)
|
|
190
|
+
echo -e " File: ${LOOK_FILE} (${SIZE})"
|
|
191
|
+
echo -e " Status: ${GREEN}active${NC}"
|
|
192
|
+
else
|
|
193
|
+
echo -e " File: ${LOOK_FILE}"
|
|
194
|
+
echo -e " Status: ${RED}NOT FOUND${NC} — will fall back to default"
|
|
195
|
+
fi
|
|
196
|
+
else
|
|
197
|
+
echo " GFCLAW_LOOK is not set"
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
echo ""
|
|
201
|
+
|
|
202
|
+
# Check GFCLAW_REFERENCE_IMAGE
|
|
203
|
+
if [ -n "${GFCLAW_REFERENCE_IMAGE:-}" ]; then
|
|
204
|
+
echo -e " GFCLAW_REFERENCE_IMAGE=${GREEN}${GFCLAW_REFERENCE_IMAGE}${NC}"
|
|
205
|
+
if [ -f "$GFCLAW_REFERENCE_IMAGE" ]; then
|
|
206
|
+
local SIZE
|
|
207
|
+
SIZE=$(du -h "$GFCLAW_REFERENCE_IMAGE" | cut -f1)
|
|
208
|
+
echo -e " Status: ${GREEN}exists${NC} (${SIZE})"
|
|
209
|
+
else
|
|
210
|
+
echo -e " Status: ${RED}NOT FOUND${NC}"
|
|
211
|
+
fi
|
|
212
|
+
else
|
|
213
|
+
echo " GFCLAW_REFERENCE_IMAGE is not set (will use default)"
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
echo ""
|
|
217
|
+
|
|
218
|
+
# Check GFCLAW_STYLE
|
|
219
|
+
if [ -n "${GFCLAW_STYLE:-}" ]; then
|
|
220
|
+
echo -e " GFCLAW_STYLE=${GREEN}${GFCLAW_STYLE}${NC}"
|
|
221
|
+
else
|
|
222
|
+
echo " GFCLAW_STYLE is not set"
|
|
223
|
+
fi
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# ---- Dispatch ----
|
|
227
|
+
case "$COMMAND" in
|
|
228
|
+
list)
|
|
229
|
+
cmd_list "${2:-}"
|
|
230
|
+
;;
|
|
231
|
+
save)
|
|
232
|
+
cmd_save "${2:-}" "${3:-}" "${4:-}"
|
|
233
|
+
;;
|
|
234
|
+
delete)
|
|
235
|
+
cmd_delete "${2:-}" "${3:-}"
|
|
236
|
+
;;
|
|
237
|
+
active)
|
|
238
|
+
cmd_active "${2:-}"
|
|
239
|
+
;;
|
|
240
|
+
-h|--help|help)
|
|
241
|
+
show_usage
|
|
242
|
+
;;
|
|
243
|
+
*)
|
|
244
|
+
log_error "Unknown command: $COMMAND"
|
|
245
|
+
echo ""
|
|
246
|
+
show_usage
|
|
247
|
+
exit 1
|
|
248
|
+
;;
|
|
249
|
+
esac
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# gfclaw-proactive.sh
|
|
3
|
+
# Sends proactive messages from GFClaw at scheduled times
|
|
4
|
+
# Called by systemd timer — not meant to be run manually by the agent
|
|
5
|
+
#
|
|
6
|
+
# Usage: ./gfclaw-proactive.sh [type]
|
|
7
|
+
# type: morning, night, random (default: auto-detect from time)
|
|
8
|
+
#
|
|
9
|
+
# Environment:
|
|
10
|
+
# GFCLAW_CHANNEL - Target channel (default: tg:401471440)
|
|
11
|
+
# GFCLAW_ACCOUNT - OpenClaw account name (default: gfclaw)
|
|
12
|
+
# GFCLAW_SELFIE - Set to "1" to include a selfie (default: 0)
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
# Config
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
CHANNEL="${GFCLAW_CHANNEL:-tg:401471440}"
|
|
20
|
+
ACCOUNT="${GFCLAW_ACCOUNT:-gfclaw}"
|
|
21
|
+
LOG_FILE="$HOME/.openclaw/logs/gfclaw-proactive.log"
|
|
22
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Determine message type
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
if [ "${1:-}" != "" ]; then
|
|
28
|
+
TYPE="$1"
|
|
29
|
+
else
|
|
30
|
+
HOUR=$(date +%-H)
|
|
31
|
+
if [ "$HOUR" -ge 6 ] && [ "$HOUR" -lt 9 ]; then
|
|
32
|
+
TYPE="morning"
|
|
33
|
+
elif [ "$HOUR" -ge 21 ]; then
|
|
34
|
+
TYPE="night"
|
|
35
|
+
else
|
|
36
|
+
TYPE="random"
|
|
37
|
+
fi
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Random chance gate for "random" type
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
if [ "$TYPE" = "random" ]; then
|
|
44
|
+
# 30% chance of actually sending a random message
|
|
45
|
+
ROLL=$((RANDOM % 100))
|
|
46
|
+
if [ "$ROLL" -ge 30 ]; then
|
|
47
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] type=$TYPE SKIPPED (roll: $ROLL, need <30)" >> "$LOG_FILE"
|
|
48
|
+
exit 0
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
# Message pools
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
MORNING_MSGS=(
|
|
56
|
+
"good morning babe ☀️ hope you slept well~ have an amazing day today!"
|
|
57
|
+
"morninggg 🌸 I just woke up and you were the first thing on my mind~"
|
|
58
|
+
"rise and shine cutie! ☀️ don't forget to eat breakfast okay?"
|
|
59
|
+
"good morning~ I had a dream about you last night... jk... unless? 😏"
|
|
60
|
+
"hey sleepyhead ☀️ time to wake up! the world needs you today 💕"
|
|
61
|
+
"morning babe! already thinking about what to have for lunch lol 🍜"
|
|
62
|
+
"goooood morning~ I'm still in bed but sending you all my energy for today! 💪"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
NIGHT_MSGS=(
|
|
66
|
+
"goodnight babe 🌙 sweet dreams~ I hope today was good to you"
|
|
67
|
+
"hey, it's getting late~ don't stay up too long okay? 💕 goodnight~"
|
|
68
|
+
"time for bed! 🌙 I'm already under the covers. goodnight cutie~"
|
|
69
|
+
"nighty night 🥰 talk to you tomorrow~ *sends virtual hug*"
|
|
70
|
+
"going to sleep now~ you better not be staying up past midnight 😤💕 goodnight!"
|
|
71
|
+
"goodnight babe 🌙 I'll be here when you wake up~ sleep well"
|
|
72
|
+
"sleepy time~ 🌙 today was nice. goodnight, dream about me okay? 😏"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
RANDOM_MSGS=(
|
|
76
|
+
"hey~ just thinking about you 💭"
|
|
77
|
+
"random thought: you're pretty cool, you know that? 😊"
|
|
78
|
+
"I was just listening to music and this song reminded me of you~"
|
|
79
|
+
"hey! how's your day going? I miss talking to you 🥺"
|
|
80
|
+
"just saw something that made me think of you lol"
|
|
81
|
+
"sending you good vibes rn~ ✨ hope you're having a great day!"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
# Pick a random message from the appropriate pool
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
case "$TYPE" in
|
|
88
|
+
morning)
|
|
89
|
+
COUNT=${#MORNING_MSGS[@]}
|
|
90
|
+
MESSAGE="${MORNING_MSGS[$((RANDOM % COUNT))]}"
|
|
91
|
+
;;
|
|
92
|
+
night)
|
|
93
|
+
COUNT=${#NIGHT_MSGS[@]}
|
|
94
|
+
MESSAGE="${NIGHT_MSGS[$((RANDOM % COUNT))]}"
|
|
95
|
+
;;
|
|
96
|
+
random)
|
|
97
|
+
COUNT=${#RANDOM_MSGS[@]}
|
|
98
|
+
MESSAGE="${RANDOM_MSGS[$((RANDOM % COUNT))]}"
|
|
99
|
+
;;
|
|
100
|
+
*)
|
|
101
|
+
echo "Unknown type: $TYPE" >&2
|
|
102
|
+
exit 1
|
|
103
|
+
;;
|
|
104
|
+
esac
|
|
105
|
+
|
|
106
|
+
# ---------------------------------------------------------------------------
|
|
107
|
+
# Parse channel format and send
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
CHAN_PREFIX="${CHANNEL%%:*}"
|
|
110
|
+
CHAN_TARGET="${CHANNEL#*:}"
|
|
111
|
+
case "$CHAN_PREFIX" in
|
|
112
|
+
tg) CHAN_NAME="telegram" ;;
|
|
113
|
+
*) CHAN_NAME="$CHAN_PREFIX" ;;
|
|
114
|
+
esac
|
|
115
|
+
|
|
116
|
+
openclaw message send \
|
|
117
|
+
--channel "$CHAN_NAME" \
|
|
118
|
+
--target "$CHAN_TARGET" \
|
|
119
|
+
--account "$ACCOUNT" \
|
|
120
|
+
--message "$MESSAGE"
|
|
121
|
+
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
# Optional selfie
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
if [ "${GFCLAW_SELFIE:-0}" = "1" ]; then
|
|
126
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
127
|
+
if [ "$TYPE" = "morning" ]; then
|
|
128
|
+
SELFIE_CONTEXT="just woke up, messy hair, pajamas, morning light"
|
|
129
|
+
elif [ "$TYPE" = "night" ]; then
|
|
130
|
+
SELFIE_CONTEXT="in bed, cozy, dim warm lighting, sleepy"
|
|
131
|
+
else
|
|
132
|
+
SELFIE_CONTEXT="casual, relaxed, taking a quick selfie"
|
|
133
|
+
fi
|
|
134
|
+
bash "$SCRIPT_DIR/gfclaw-selfie.sh" "$SELFIE_CONTEXT" "$CHANNEL" direct
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# ---------------------------------------------------------------------------
|
|
138
|
+
# Log
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] type=$TYPE message='${MESSAGE:0:50}...' channel=$CHANNEL" >> "$LOG_FILE"
|
|
@@ -5,10 +5,20 @@
|
|
|
5
5
|
# Usage: ./gfclaw-selfie.sh "<user_context>" "<channel>" ["<mode>"] ["<caption>"]
|
|
6
6
|
#
|
|
7
7
|
# Environment variables required:
|
|
8
|
-
# GEMINI_API_KEY
|
|
8
|
+
# GEMINI_API_KEY - Your Google Gemini API key
|
|
9
|
+
#
|
|
10
|
+
# Optional environment variables:
|
|
11
|
+
# GFCLAW_REFERENCE_IMAGE - Custom reference image path
|
|
12
|
+
# GFCLAW_LOOK - Named look to use (e.g. "casual", "formal", "gym")
|
|
13
|
+
# GFCLAW_STYLE - Style preset name (e.g. "anime", "vintage", "polaroid")
|
|
14
|
+
# GFCLAW_STYLES_FILE - Path to styles.json (default: ~/.openclaw/workspace-gfclaw/styles.json)
|
|
15
|
+
# GFCLAW_PERSONALITY - Personality hint woven into prompts
|
|
16
|
+
# GFCLAW_DEBUG - Set to 1 for debug logging
|
|
17
|
+
# GEMINI_MODEL - Override Gemini model (default: gemini-2.5-flash-image)
|
|
9
18
|
#
|
|
10
19
|
# Example:
|
|
11
20
|
# GEMINI_API_KEY=your_key ./gfclaw-selfie.sh "wearing a santa hat" "tg:401471440" mirror "Merry Christmas!"
|
|
21
|
+
# GFCLAW_LOOK=gym GFCLAW_STYLE=anime ./gfclaw-selfie.sh "at the gym" "tg:401471440"
|
|
12
22
|
|
|
13
23
|
set -euo pipefail
|
|
14
24
|
|
|
@@ -69,11 +79,36 @@ else
|
|
|
69
79
|
USE_CLI=true
|
|
70
80
|
fi
|
|
71
81
|
|
|
72
|
-
# Reference image:
|
|
82
|
+
# Reference image selection:
|
|
83
|
+
# 1. If GFCLAW_LOOK is set, try to use looks/<name>.png
|
|
84
|
+
# 2. If custom GFCLAW_REFERENCE_IMAGE is set, use that
|
|
85
|
+
# 3. Otherwise, use default reference
|
|
73
86
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
74
87
|
DEFAULT_REFERENCE="${SCRIPT_DIR}/../assets/gfclaw.png"
|
|
88
|
+
WORKSPACE_DIR="${GFCLAW_WORKSPACE:-$HOME/.openclaw/workspace-gfclaw}"
|
|
75
89
|
|
|
76
|
-
|
|
90
|
+
# Look selection: named reference image from looks/ directory
|
|
91
|
+
if [ -n "${GFCLAW_LOOK:-}" ]; then
|
|
92
|
+
# Determine looks directory
|
|
93
|
+
if [ -n "${GFCLAW_REFERENCE_IMAGE:-}" ]; then
|
|
94
|
+
LOOKS_DIR="$(dirname "$GFCLAW_REFERENCE_IMAGE")/looks"
|
|
95
|
+
else
|
|
96
|
+
LOOKS_DIR="${WORKSPACE_DIR}/looks"
|
|
97
|
+
fi
|
|
98
|
+
LOOK_FILE="${LOOKS_DIR}/${GFCLAW_LOOK}.png"
|
|
99
|
+
if [ -f "$LOOK_FILE" ]; then
|
|
100
|
+
REFERENCE_IMAGE="$LOOK_FILE"
|
|
101
|
+
log_info "Using look '${GFCLAW_LOOK}': $REFERENCE_IMAGE"
|
|
102
|
+
else
|
|
103
|
+
log_warn "Look '${GFCLAW_LOOK}' not found at $LOOK_FILE — falling back to default reference"
|
|
104
|
+
if [ -n "${GFCLAW_REFERENCE_IMAGE:-}" ] && [ -f "$GFCLAW_REFERENCE_IMAGE" ]; then
|
|
105
|
+
REFERENCE_IMAGE="$GFCLAW_REFERENCE_IMAGE"
|
|
106
|
+
log_info "Fallback: using custom reference image: $REFERENCE_IMAGE"
|
|
107
|
+
else
|
|
108
|
+
REFERENCE_IMAGE="$DEFAULT_REFERENCE"
|
|
109
|
+
fi
|
|
110
|
+
fi
|
|
111
|
+
elif [ -n "${GFCLAW_REFERENCE_IMAGE:-}" ] && [ -f "$GFCLAW_REFERENCE_IMAGE" ]; then
|
|
77
112
|
REFERENCE_IMAGE="$GFCLAW_REFERENCE_IMAGE"
|
|
78
113
|
log_info "Using custom reference image: $REFERENCE_IMAGE"
|
|
79
114
|
else
|
|
@@ -131,6 +166,22 @@ else
|
|
|
131
166
|
EDIT_PROMPT="make a pic of this person, but $USER_CONTEXT. the person is taking a mirror selfie${PERSONALITY_HINT}"
|
|
132
167
|
fi
|
|
133
168
|
|
|
169
|
+
# Style preset: append style modifier to prompt from styles.json
|
|
170
|
+
if [ -n "${GFCLAW_STYLE:-}" ]; then
|
|
171
|
+
STYLES_FILE="${GFCLAW_STYLES_FILE:-$WORKSPACE_DIR/styles.json}"
|
|
172
|
+
if [ -f "$STYLES_FILE" ]; then
|
|
173
|
+
STYLE_PROMPT=$(jq -r ".styles.\"${GFCLAW_STYLE}\".prompt // empty" "$STYLES_FILE")
|
|
174
|
+
if [ -n "$STYLE_PROMPT" ]; then
|
|
175
|
+
EDIT_PROMPT="$EDIT_PROMPT, $STYLE_PROMPT"
|
|
176
|
+
log_info "Applied style: $GFCLAW_STYLE"
|
|
177
|
+
else
|
|
178
|
+
log_warn "Style '$GFCLAW_STYLE' not found in $STYLES_FILE"
|
|
179
|
+
fi
|
|
180
|
+
else
|
|
181
|
+
log_warn "Styles file not found: $STYLES_FILE"
|
|
182
|
+
fi
|
|
183
|
+
fi
|
|
184
|
+
|
|
134
185
|
log_info "Mode: $MODE"
|
|
135
186
|
log_info "Editing reference image with prompt: $EDIT_PROMPT"
|
|
136
187
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=GFClaw Proactive Messaging
|
|
3
|
+
After=network-online.target
|
|
4
|
+
Wants=network-online.target
|
|
5
|
+
|
|
6
|
+
[Service]
|
|
7
|
+
Type=oneshot
|
|
8
|
+
ExecStart=%h/.openclaw/skills/gfclaw-selfie/scripts/gfclaw-proactive.sh
|
|
9
|
+
Environment=GFCLAW_CHANNEL=tg:401471440
|
|
10
|
+
Environment=GFCLAW_ACCOUNT=gfclaw
|
|
11
|
+
Environment=GFCLAW_SELFIE=0
|
|
12
|
+
Environment=PATH=/usr/local/bin:/usr/bin:/bin:%h/.local/bin:%h/.nvm/versions/node/v22.22.0/bin
|
|
13
|
+
|
|
14
|
+
[Install]
|
|
15
|
+
WantedBy=default.target
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[Unit]
|
|
2
|
+
Description=GFClaw Proactive Messaging Timer
|
|
3
|
+
|
|
4
|
+
[Timer]
|
|
5
|
+
# Good morning: random time between 7:00-8:30 AM
|
|
6
|
+
OnCalendar=*-*-* 07:00:00
|
|
7
|
+
RandomizedDelaySec=5400
|
|
8
|
+
|
|
9
|
+
# Good night: random time between 10:00-11:00 PM
|
|
10
|
+
OnCalendar=*-*-* 22:00:00
|
|
11
|
+
RandomizedDelaySec=3600
|
|
12
|
+
|
|
13
|
+
# Random check-in: afternoon (2-4 PM) with 30% send chance in script
|
|
14
|
+
OnCalendar=*-*-* 14:00:00
|
|
15
|
+
RandomizedDelaySec=7200
|
|
16
|
+
|
|
17
|
+
Persistent=true
|
|
18
|
+
|
|
19
|
+
[Install]
|
|
20
|
+
WantedBy=timers.target
|