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.
@@ -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 - Your Google 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: check for custom (user-provided via onboarding), then fall back to default
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
- if [ -n "${GFCLAW_REFERENCE_IMAGE:-}" ] && [ -f "$GFCLAW_REFERENCE_IMAGE" ]; then
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