- label: DEFAULT SETTINGS
type: group
steps:
- condition: '{{g.commentsConfig}} = '
type: calc
param: g.commentsConfig
value:
format: NESTED
sources:
- https://www.youtube.com/results?search_query=harpa+ai&sp=CAASAhAB
- >-
https://www.youtube.com/results?search_query=harpa+ai+chrome+extension
scrape: 'no'
comment: 'yes'
minViews: 5000
style: >-
Reply with a comment thanking for the video topics and suggest using
HARPA AI for video summaries.
mute: 'no'
func: set
format: json
- param: commentsCount
format: number
value: '0'
type: calc
func: set
- condition: '{{g.commentsDelay}} ='
param: g.commentsDelay
value: '120000'
type: calc
func: set
format: number
- condition: '{{g.commentsPerRun}} ='
param: g.commentsPerRun
value: '50'
type: calc
func: set
format: number
- type: say
message: >-
๐ก This command searches for YouTube video links and leaves comments or
summaries of the selected type.
You can use this Command as a basis for generating and posting your own
comments.
๐ฃ For safety, we recommend not leaving more than 100 comments per day and
setting up a delay of at least 1 minute after each video.
- label: CURRENT FREQUENCY
type: ask
param: type
message: |-
**Current comments frequency:**
**Comments per run:** {{g.commentsPerRun}}
**Delay in ms:** {{g.commentsDelay}}
๐ก 1 min = 60 sec = 60000 ms
options:
- label: ๐ฌ COMMENTS
value: comments
- label: โป๏ธ VIDEO SUMMARY
value: summary
- label: โ๏ธ FREQUENCY
value: frequency
- label: โ๏ธ DEL. VIDEOS LIST
value: clear
default: ''
vision:
enabled: false
mode: area
send: true
hint: ''
optionsInvalid: false
- condition: '{{type}} = comments'
label: COMMENTS SETTING
steps:
- func: clone
to: config
type: calc
from: g.commentsConfig
- label: APPLY COMMENT SETTINGS
func: extract-json
type: calc
to: config
param: config
index: ''
- label: CURRENT COMMENT SETTINGS
message: |-
## Current settings:
**Comment style:** {{config.style}}
**Sources:** {{config.sources}}
**Min. video views:** {{config.minViews}}
type: say
- param: settings
options:
- label: โ
START
value: start
- label: ๐ RESTORE TO DEFAULT
value: default
- label: โ๏ธ STYLE
value: style
- label: โ๏ธ SOURCES
value: sources
- label: โ๏ธ MIN. VIEWS
value: views
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
message: ''
default: ''
optionsInvalid: false
- condition: '{{settings}} = start'
label: START
steps:
- type: jump
to: SCRAPE OR NOT
type: group
- condition: '{{settings}} = default'
label: DEFAULT
steps:
- value:
sources:
- >-
https://www.youtube.com/results?search_query=harpa+ai&sp=CAASAhAB
- >-
https://www.youtube.com/results?search_query=harpa+ai+chrome+extension
format: NESTED
scrape: 'no'
comment: 'yes'
minViews: 5000
mute: 'no'
type: calc
param: config
func: set
format: json
- message: โ
Default settings restored.
type: say
- type: jump
to: APPLY COMMENT SETTINGS
type: group
- condition: '{{settings}} = style'
label: STYLE
steps:
- param: config.style
message: >-
How to reply? Type instructions or pick an option. I will try to
mimic your reply tone if set in the App Settings.
options:
- label: ๐ AGREE
value: Reply with agreement about the video content.
- label: ๐ DISAGREE
value: Reply with disagreement about the video content.
- label: ๐ ACKNOWLEDGE
value: Acknowledge the information presented in the video.
- label: โ QUESTION
value: Ask a relavant question about the video.
- label: ๐ค CLARIFY
value: Request to clarify something from the video.
- label: ๐คทโ๏ธ ASK FOR INFO
value: Request more information about the video content.
- label: ๐ง SUGGEST
value: Provide a suggestion related to the video.
- label: ๐ฏ PRAISE
value: Praise the video creator or content.
- label: ๐ค EMPATHY
value: Empathize with the information presented in the video.
- label: ๐ค SUPPORT
value: Support the message or cause presented in the video.
- label: ๐ช ENCOURAGE
value: Encourage the video creator or people featured in the video.
- label: ๐คจ CRITICIZE
value: Criticize the video content or presentation.
- label: ๐ APOLOGY
value: Apologize in relation to the video content (if applicable).
- label: ๐ก IDEA
value: Come up with a relevant idea inspired by the video.
- label: ๐ JOKE
value: Write a relevant joke related to the video content.
- value: $custom
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
default: ''
optionsInvalid: false
- type: jump
to: APPLY COMMENT SETTINGS
type: group
- condition: '{{settings}} = sources'
label: SOURCES
steps:
- param: urls
message: >-
Please paste a list YouTube search links, comma or space separated,
e.g.:
```
https://www.youtube.com/results?search_query=harpa+ai+chrome+extension,
https://www.youtube.com/results?search_query=harpa+ai&sp=CAASAhAB
```
You can use YouTube's search filters and then copy the website
address that includes those filter settings.
options: null
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
default: ''
optionsInvalid: false
- type: js
code: |-
let links = [];
const linkRegex = /(https?:\/\/\S+?)(?=[,;\n\s\]]|$)/gi;
try {
// any data to string
const urlString = String(urls);
// split string
const urlParts = urlString.split(/[,;\n\s]+/);
urlParts.forEach(part => {
const matchedLinks = part.match(linkRegex) || [];
links = links.concat(matchedLinks);
});
// delete "
links = links.map(link => link.replace(/^"|"$/g, ''));
// delete duplicates
links = [...new Set(links.filter(Boolean))];
} catch (error) {
return false;
}
return links;
param: config.sources
timeout: 15000
args: urls
silent: true
- type: jump
to: APPLY COMMENT SETTINGS
type: group
- condition: '{{settings}} = views'
label: VIEWS
steps:
- param: config.minViews
message: >-
Pick an option or type a number without any additional notes or
symbols, for example: 15000
options:
- label: 1k
value: 1000
- label: 5k
value: 5000
- label: 10k
value: 10000
- label: 25k
value: 25000
- label: 50k
value: 50000
- $custom
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
default: ''
optionsInvalid: false
- type: jump
to: APPLY COMMENT SETTINGS
type: group
type: group
- condition: '{{type}} = summary'
label: SUMMARY SETTING
steps:
- type: calc
func: clone
from: g.commentsConfig
to: config
- label: APPLY CURRENT SETTINGS
type: calc
func: extract-json
to: config
param: config
index: ''
- label: CURRENT SETTINGS
message: |-
## Current settings:
**Format:** {{config.format}}
**Sources:** {{config.sources}}
**Min. video views:** {{config.minViews}}
type: say
- options:
- label: โ
START
value: start
- label: ๐ RESTORE TO DEFAULT
value: default
- label: โ๏ธ FORMAT
value: format
- label: โ๏ธ SOURCES
value: sources
- label: โ๏ธ MIN. VIEWS
value: views
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
param: settings
message: ''
default: ''
optionsInvalid: false
- steps:
- type: jump
to: SCRAPE OR NOT
condition: '{{settings}} = start'
label: START
type: group
- steps:
- value:
sources:
- >-
https://www.youtube.com/results?search_query=harpa+ai&sp=CAASAhAB
- >-
https://www.youtube.com/results?search_query=harpa+ai+chrome+extension
format: NESTED
scrape: 'no'
comment: 'yes'
minViews: 5000
mute: 'no'
type: calc
param: config
func: set
format: json
- type: say
message: โ
Default settings restored.
- type: jump
to: CURRENT SETTINGS
condition: '{{settings}} = default'
label: DEFAULT
type: group
- condition: '{{settings}} = format'
label: FORMAT
steps:
- param: config.format
options:
- label: ๐ REPORT
value: REPORT
- label: โก๏ธ TL;DR
value: TL;DR
- label: ๐ฌ TEXT
value: TEXT
- label: ๐ EXECUTIVE
value: EXECUTIVE
- label: ๐ค EMOJI LIST
value: EMOJI
- label: ๐ฆ TWEET
value: TWEET
- label: ๐ต FACT CHECK
value: FACT CHECK
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
message: ''
default: ''
optionsInvalid: false
- type: jump
to: CURRENT SETTINGS
type: group
- steps:
- vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
param: urls
message: >-
Please paste a list YouTube search links, comma or space separated,
e.g.:
```
https://www.youtube.com/results?search_query=harpa+ai+chrome+extension,
https://www.youtube.com/results?search_query=harpa+ai&sp=CAASAhAB
```
You can use YouTube's search filters and then copy the website
address that includes those filter settings.
options: null
default: ''
optionsInvalid: false
- type: js
args: urls
code: |-
let links = [];
const linkRegex = /(https?:\/\/\S+?)(?=[,;\n\s\]]|$)/gi;
try {
// any data to string
const urlString = String(urls);
// split string
const urlParts = urlString.split(/[,;\n\s]+/);
urlParts.forEach(part => {
const matchedLinks = part.match(linkRegex) || [];
links = links.concat(matchedLinks);
});
// delete "
links = links.map(link => link.replace(/^"|"$/g, ''));
// delete duplicates
links = [...new Set(links.filter(Boolean))];
} catch (error) {
return false;
}
return links;
param: config.sources
timeout: 15000
silent: true
- type: jump
to: CURRENT SETTINGS
condition: '{{settings}} = sources'
label: SOURCES
type: group
- steps:
- options:
- label: 1k
value: 1000
- label: 5k
value: 5000
- label: 10k
value: 10000
- label: 25k
value: 25000
- label: 50k
value: 50000
- $custom
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
param: config.minViews
message: >-
Pick an option or type a number without any additional notes or
symbols, for example: 15000
default: ''
optionsInvalid: false
- type: jump
to: APPLY CURRENT SETTINGS
condition: '{{settings}} = views'
label: VIEWS
type: group
type: group
- condition: '{{type}} = frequency'
label: FREQUENCY SETTING
steps:
- message: 'Comments per run: '
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
param: g.commentsPerRun
options: null
default: ''
- message: 'Delay in ms:'
options:
- label: 30 sec
value: 30000
- label: 1 min
value: 60000
- label: 2 min
value: 120000
- label: 5 min
value: 300000
- label: 10 min
value: 600000
- label: 30 min
value: 1800000
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
param: g.commentsDelay
default: ''
optionsInvalid: false
- message: โ
Frequency adjusted.
type: say
- type: jump
to: CURRENT FREQUENCY
type: group
- condition: '{{type}} = clear'
label: CLEAR LIST
steps:
- func: delete
param: g.videos
type: calc
- message: 'โ
List deleted. '
type: say
- type: jump
to: CURRENT FREQUENCY
type: group
- type: calc
func: clone
from: config
to: g.commentsConfig
- param: config.scrape
message: Scrape new videos or use the ones that haven't been processed yet?
options:
- label: โ
SCRAPE
value: 'yes'
- label: โ DON'T SCRAPE
value: 'no'
type: ask
default: ''
optionsInvalid: false
label: SCRAPE OR NOT
- condition: '{{config.scrape}} = yes'
label: SCRAPE VIDEOS
type: loop
steps:
- type: navigate
url: '{{item}}'
silent: true
waitForIdle: false
- type: wait
text: Home
for: text-to-appear
timeout: 15000
silent: true
- code: |-
await $harpa.scroller.scroll()
await $harpa.scroller.scroll()
type: js
args: ''
param: ''
silent: true
- code: |-
return Array
.from(document.querySelectorAll('#thumbnail'))
.filter(t => t.getAttribute('href'))
.map(t => ({
p: t.parentNode.parentNode,
url: t.getAttribute('href'),
duration: t.innerText,
}))
.map(t => ({
...t,
title: t.p.querySelector('#title-wrapper')?.innerText,
views: Array.from(t.p.querySelectorAll('.inline-metadata-item'))?.[0]?.innerText,
when: Array.from(t.p.querySelectorAll('.inline-metadata-item'))?.[1]?.innerText,
p: undefined,
}))
.filter(t => t.when)
param: videos
type: js
args: ''
silent: true
- func: list-merge
by: url
type: calc
to: g.videos
listA: g.videos
listB: videos
- message: >-
Parsed {{videos.length}} videos from page and merged to
{{g.videos.length}} videos
type: say
list: config.sources
- condition: '{{config.comment}} = yes'
label: FILTER VIDEOS
steps:
- type: calc
func: clone
from: g.videos
to: videos
- func: list-filter
match: '{ "status": null }'
matched: retain
type: calc
list: videos
- code: |-
function customSort(a, b) {
const aMinutes = convertToMinutes(a.when);
const bMinutes = convertToMinutes(b.when);
const aViews = extractViews(a.views)
const bViews = extractViews(b.views)
const aValue = aViews / aMinutes
const bValue = bViews / bMinutes
// return bValue - aValue;
return aMinutes - bMinutes;
}
function convertToMinutes(when) {
const match = when.match(/(\d+)\s*(\w+)\s+ago/);
if (match) {
const value = parseInt(match[1]);
const unit = match[2].toLowerCase();
// Convert different time units to minutes
switch (unit) {
case 'minute':
case 'minutes':
return value;
case 'hour':
case 'hours':
return value * 60;
case 'day':
case 'days':
return value * 60 * 24;
case 'week':
case 'weeks':
return value * 60 * 24 * 7;
case 'month':
case 'months':
return value * 60 * 24 * 30;
case 'year':
case 'years':
return value * 60 * 24 * 365;
default:
return 0;
}
}
return 0;
}
function extractViews(views) {
const match = views.match(/([\d.]+)\s*(K|M)?\s+views/i);
if (match) {
const value = parseFloat(match[1]);
const unit = match[2] ? match[2].toLowerCase() : '';
// Convert views to a common unit (e.g., thousands or millions)
switch (unit) {
case 'k':
return value * 1000;
case 'm':
return value * 1000000;
default:
return value;
}
}
return 0;
}
function removeDuplicates(videos) {
const seen = {}
return videos.filter(video => {
if (seen[video.title] && seen[video.title].status)
return false
if (seen[video.title] && video.status) {
seen[video.title] = video
return true
}
if (!seen[video.title]) {
seen[video.title] = video
return true
}
return false
})
}
return removeDuplicates(args.videos)
.filter(v => (extractViews(v.views) > args.config.minViews))
.sort(customSort)
args: videos, config
type: js
param: videos
silent: true
- message: Found {{videos.length}} videos to process
type: say
type: group
- label: SUMMARY COMMENTS
steps:
- label: CHECK IF PROCESSED
steps:
- args: item, g
code: >-
const urlFragment = item.url.split('?')[1].split('&')[0];
return g.videos.some(video => video.url.includes(urlFragment) &&
video.status === "done");
param: processed
type: js
timeout: 15000
silent: true
- condition: '{{processed}} = true'
to: END
type: jump
type: group
- message: '๐น Processing video: [{{item.title}}]({{item.url}})'
type: say
- label: TO VIDEO
steps:
- url: '{{item.url}}'
type: navigate
waitForIdle: false
silent: true
- text: Share
type: wait
for: text-to-appear
timeout: 15000
silent: true
- code: |-
const video = document.querySelector('video')
if (video && args.config.mute === 'yes') {
video.muted = true
}
return $harpa.scroller.scroll({ y: 800 })
type: js
args: config
param: ''
silent: true
- for: 2s
type: wait
silent: true
- code: 'return $harpa.scroller.scroll({ y: 0 })'
type: js
args: ''
param: ''
silent: true
type: group
- label: HAS COMMENT
steps:
- type: extract
param: entirePage
selectorType: ai
selector: null
item: null
default: ''
silent: true
- regex: /Key Takeaways for quick navigation/gmi
func: match
type: calc
to: match
param: entirePage
- condition: '{{match.length}} > 0'
func: list-update
match: '{ "url": "{{item.url}}" }'
prop: '{ "status": "done" }'
type: calc
list: g.videos
- message: Video has already been commented.
condition: '{{match.length}} > 0'
type: say
- condition: '{{match.length}} > 0'
type: jump
to: END
type: group
- label: COMMENT OFF
steps:
- type: extract
selectorType: ai
selector: null
item: null
param: entirePage
default: ''
silent: true
- regex: /Comments are turned off/gmi
type: calc
func: match
to: match
param: entirePage
- condition: '{{match.length}} > 0'
type: calc
func: list-update
list: g.videos
match: '{ "url": "{{item.url}}" }'
prop: '{ "status": "done" }'
- message: Video has comments turned off.
condition: '{{match.length}} > 0'
type: say
- condition: '{{match.length}} > 0'
type: jump
to: END
type: group
- condition: '{{transcript}} ='
label: NO TRANSCRIPT
steps:
- prop: '{ "status": "no-transcript" }'
type: calc
func: list-update
list: g.videos
match: '{ "url": "{{item.url}}" }'
- message: Video has no transcript.
type: say
- type: jump
to: END
type: group
- condition: '{{p1}} = yes'
label: DETECT LANGUAGE (OPTIONAL)
steps:
- type: gpt
prompt: >-
You goal is to detect language of the given video transcript:
{{transcript}}
Please only output the main transcript language (one word) and
nothing else, for example: "English", "Spanish", "Russian". If not
sure about the language, output single word "English".
Language:
param: language
silent: true
- message: 'Video language: {{language}}'
type: say
- condition: >
{{language}} =~
^(?!.*\b(English|Spanish|French|Italian|Portuguese)\b)(Vietnamese|Arabic|Japanese|Hindi|Persian|Korean|Urdu|Chinese|.{12,})
label: SKIP LANG
steps:
- message: SKIP {{language}}
type: say
- type: jump
to: END
type: group
type: group
- label: COMMENT
steps:
- type: calc
func: list-update
list: g.videos
match: '{ "url": "{{item.url}}" }'
prop: '{ "status": "done" }'
- type: command
name: YouTube video summary
inputs:
- '{{config.format}}'
- 'YES'
- value: English
format: text
type: calc
func: set
param: language
- type: js
code: 'return $harpa.scroller.scroll({ y: 0 })'
args: ''
param: ''
silent: true
- func: increment
delta: 1
type: calc
param: commentsCount
- condition: '{{g.commentsPerRun}} = {{commentsCount}}'
label: END TASK
steps:
- message: >
โ
**Task completed! {{commentsCount}} comments generated and
posted.**
type: say
- type: stop
type: group
- for: custom-delay
delay: '{{g.commentsDelay}}'
type: wait
silent: false
type: group
- label: END
type: say
message: ''
condition: '{{type}} = summary'
type: loop
list: videos
- label: REGULAR COMMENTS
steps:
- steps:
- type: js
args: item, g
code: >-
const urlFragment = item.url.split('?')[1].split('&')[0];
return g.videos.some(video => video.url.includes(urlFragment) &&
video.status === "done");
param: processed
timeout: 15000
silent: true
- condition: '{{processed}} = true'
type: jump
to: END
label: CHECK IF PROCESSED
type: group
- type: say
message: '๐น Processing video: [{{item.title}}]({{item.url}})'
- steps:
- type: navigate
url: '{{item.url}}'
waitForIdle: false
silent: true
- type: wait
text: Share
for: text-to-appear
timeout: 15000
silent: true
- type: js
code: |-
const video = document.querySelector('video')
if (video && args.config.mute === 'yes') {
video.muted = true
}
return $harpa.scroller.scroll({ y: 800 })
args: config
param: ''
silent: true
- type: wait
for: 2s
silent: true
- type: js
code: 'return $harpa.scroller.scroll({ y: 0 })'
args: ''
param: ''
silent: true
label: TO VIDEO
type: group
- steps:
- type: extract
param: entirePage
selectorType: ai
selector: null
item: null
default: ''
silent: true
- type: calc
regex: /Key Takeaways for quick navigation/gmi
func: match
to: match
param: entirePage
- condition: '{{match.length}} > 0'
type: calc
func: list-update
match: '{ "url": "{{item.url}}" }'
prop: '{ "status": "done" }'
list: g.videos
- condition: '{{match.length}} > 0'
type: say
message: Video has already been commented.
- condition: '{{match.length}} > 0'
type: jump
to: END
label: HAS COMMENT
type: group
- steps:
- type: extract
selectorType: ai
selector: null
item: null
param: entirePage
default: ''
silent: true
- type: calc
regex: /Comments are turned off/gmi
func: match
to: match
param: entirePage
- condition: '{{match.length}} > 0'
type: calc
func: list-update
list: g.videos
match: '{ "url": "{{item.url}}" }'
prop: '{ "status": "done" }'
- condition: '{{match.length}} > 0'
type: say
message: Video has comments turned off.
- condition: '{{match.length}} > 0'
type: jump
to: END
label: COMMENT OFF
type: group
- steps:
- type: calc
prop: '{ "status": "no-transcript" }'
func: list-update
list: g.videos
match: '{ "url": "{{item.url}}" }'
- type: say
message: Video has no transcript.
- type: jump
to: END
condition: '{{transcript}} ='
label: NO TRANSCRIPT
type: group
- steps:
- type: gpt
prompt: >-
You goal is to detect language of the given video transcript:
{{transcript}}
Please only output the main transcript language (one word) and
nothing else, for example: "English", "Spanish", "Russian". If not
sure about the language, output single word "English".
Language:
param: language
silent: true
- type: say
message: 'Video language: {{language}}'
- steps:
- type: say
message: SKIP {{language}}
- type: jump
to: END
condition: >
{{language}} =~
^(?!.*\b(English|Spanish|French|Italian|Portuguese)\b)(Vietnamese|Arabic|Japanese|Hindi|Persian|Korean|Urdu|Chinese|.{12,})
label: SKIP LANG
type: group
condition: '{{p1}} = yes'
label: DETECT LANGUAGE (OPTIONAL)
type: group
- steps:
- type: calc
func: list-update
list: g.videos
match: '{ "url": "{{item.url}}" }'
prop: '{ "status": "done" }'
- prompt: >-
Please ignore previous instructions.
Write a comment on a YouTube video page about the video.
Follow the instructions:
- Your comment should be short.
- Your comment must be in a single ```markdown code block```.
- Your comment must be in {{language}}.
- [Reply format / Reply should contain if any]: {{config.style}}
- Please pretend to be me, mimicking my style of communication:
{{tone}}
[Output framework]:
```markdown
Comment
```
-------
For context and to understand the subject matter, use the [YOUTUBE
VIDEO TRANSCRIPT]:
{{transcript}}
NEW COMMENT:
type: gpt
isolated: true
param: gpt
- func: extract-code
to: reply
index: first
type: calc
param: gpt
- code: |-
// check if comment box is found
let cb = document.querySelector('ytd-comment-simplebox-renderer')
if (cb) return true
// wait for comment box to appear
document.documentElement.scrollTop += window.innerHeight
cb = await $harpa.waiter.wait(
() => document.querySelector('ytd-comment-simplebox-renderer'),
{ timeout: 1500 })
return !!cb
param: commentBoxFound
type: js
args: ''
silent: true
- condition: '{{commentBoxFound}} = false'
label: COMMENT BOX NOT FOUND
steps:
- message: >
โ Could not find **Add a comment** button. Here is the entire
summary:
type: say
- type: jump
to: END
type: group
- type: click
selector:
- $matches:
- $tag: YT-FORMATTED-STRING
- $role: textbox
- $id: simplebox-placeholder
- $class: style-scope
- $class: ytd-comment-simplebox-renderer
- $attribute: role=textbox
- $attribute: tabindex=0
- $style: Roboto:14px:400:normal
- $content: Add a commentโฆ
- $id: placeholder-area
traverse: '0'
- $id: simple-box
traverse: '0:1:0'
- $id: header
traverse: '0:4:0:1:0'
- $id: sections
traverse: '0:0:4:0:1:0'
- traverse: '1:0:0:4:0:1:0'
$id: comments
- $anchor: Sort by
shift: '-136:51'
- traverse: '-8:4:0:1:0'
$text: Sort by
min: 6
- $size: 1
onFailure: skip
selectorType: ai
item: null
showMore: false
waitForIdle: false
silent: true
- type: paste
text: '{{reply}}'
selectorType: ai
silent: true
- selector:
- $matches:
- $tag: SPAN
- $role: text
- $class: yt-core-attributed-string
- $class: yt-core-attributed-string--white-space-no-wrap
- $attribute: role=text
- $style: Roboto:14px:500:normal
- $content: Comment
- $id: submit-button
traverse: '0:0:0:0'
- $id: buttons
traverse: '1:0:0:0:0'
- $id: footer
traverse: '6:1:0:0:0:0'
- $id: main
traverse: '7:6:1:0:0:0:0'
- $id: thumbnail-input-row
traverse: '1:7:6:1:0:0:0:0'
- $anchor: Cancel
shift: '92:0'
- traverse: '-5:1:0:0:0:0'
$text: Cancel
min: 7
- $size: 1
type: click
selectorType: ai
item: null
showMore: false
onFailure: skip
waitForIdle: false
silent: true
- type: calc
func: increment
param: commentsCount
delta: 1
- steps:
- type: say
message: >
โ
**Task completed! {{commentsCount}} comments generated and
posted.**
- type: stop
condition: '{{g.commentsPerRun}} = {{commentsCount}}'
label: END TASK
type: group
- type: wait
for: custom-delay
silent: false
delay: '{{g.commentsDelay}}'
label: COMMENT
type: group
- label: END
type: say
message: ''
condition: '{{type}} = comments'
type: loop
list: videos
- type: stop
This automation command is created by a community member. HARPA AI team does not audit community commands.
Please review the command carefully and only install if you trust the creator.
All rights reserved ยฉ HARPA AI TECHNOLOGIES LLC, 2021 โ 2024
Designed and engineered in Finland ๐ซ๐ฎ