- type: calc
func: set
param: result
format: number
value: '0'
- type: ask
param: input
message: >-
Send the Instagram profile as **@username** or **username**, the link as
**https://www.instagram.com/username/**, or start processing this tab.
options:
- label: โ
USE THIS TAB
value: tab
- $custom
default: ''
vision:
enabled: false
mode: area
hint: ''
send: true
optionsInvalid: false
label: ASK FOR INPUT
- type: group
steps:
- type: js
args: url
code: |-
let regex = /instagram\.com\/[\w\.]+(?:\/.*)?$/;
let isValid = regex.test(url);
return isValid;
param: linkCheck
timeout: 15000
silent: true
- type: say
message: >-
โ๏ธ The webpage is not suitable for processing. Please start on a new
page or provide me with a username or link.
condition: '{{linkCheck}} = false'
- type: jump
to: ASK FOR INPUT
condition: '{{linkCheck}} = false'
- type: js
args: url
code: |-
let urlPattern = /^https:\/\/www\.instagram\.com\/([\w\.]+)/;
let result = url.match(urlPattern)[1];
return result;
param: username
timeout: 15000
silent: true
- type: jump
to: EXTRACT STATS
label: CHECK CURRENT TAB
condition: '{{input}} = tab'
- type: group
steps:
- type: js
args: input
code: |-
let urlPattern = /^https:\/\/www\.instagram\.com\/([\w\.]+)/;
let result = input; // Default to the original value
if (input.startsWith('@')) {
result = input.slice(1); // Remove '@' symbol if it exists
} else if (urlPattern.test(result)) {
result = input.match(urlPattern)[1]; // Extract username from the URL
}
console.log(result);
return result;
param: username
timeout: 15000
silent: true
condition: '{{input-option}} = $custom'
label: EXTRACT USERNAME
- label: ASK FOR USERS
type: ask
param: input
message: |-
Send me a list of profiles either as:
1. @usernames separated by commas, e.g.:
```
@harpa.ai, @chrome, @extension
```
2. JSON with URLs, e.g.:
```json
[
{
"url": "https://www.instagram.com/harpa.ai/"
},
{
"url": "https://www.instagram.com/username/"
}
]
```
options: null
default: ''
vision:
enabled: false
mode: area
send: true
hint: ''
- type: group
steps:
- type: say
message: โณ Extracting followers' profiles, wait a bit.
- type: navigate
url: https://www.instagram.com/{{username}}/
waitForIdle: false
silent: true
onFailure: ''
- type: wait
for: 2s
silent: true
- type: js
args: ''
code: |-
let text = document.querySelector('header section ul').innerText;
let formattedText = text.replace(/\n/g, ', ');
return formattedText;
param: stats
timeout: 15000
label: EXTRACT STATS
silent: true
label: NAVIGATE
- type: ask
param: count
message: >-
**[@{{username}}](https://www.instagram.com/{{username}}/)** has {{stats}}.
How many followers should be extracted and followed? Pick an option or enter
a number.
options:
- label: โป๏ธ ALL
value: 99999999999
- label: 50
value: 50
- label: 100
value: 100
- label: 250
value: 250
- label: 500
value: 500
- label: 1k
value: 1000
- label: 5k
value: 5000
- label: 10k
value: 10000
- $custom
default: ''
vision:
enabled: false
mode: area
hint: ''
send: true
optionsInvalid: false
- type: group
steps:
- type: navigate
url: https://www.instagram.com/{{username}}/followers/
waitForIdle: false
silent: true
onFailure: ''
- type: control
action: show
- type: wait
for: idle
timeout: 3500
silent: true
- type: js
args: ''
code: |-
let linkOpened = !!document.querySelector('div[role="dialog"]');
return linkOpened;
param: linkOpened
timeout: 15000
label: CHECK IF OPENED
silent: true
- type: js
args: linkOpened
code: document.querySelector('header section ul li:nth-child(2) a').click();
param: ''
timeout: 15000
label: CHECK IF FAILED
condition: '{{linkOpened}} = false'
silent: true
- type: wait
for: 2s
condition: '{{linkOpened}} = false'
silent: true
- type: js
args: linkOpened
code: |-
let openData = !!document.querySelector('[placeholder="Search"]');
return openData;
param: openData
timeout: 15000
label: CHECK IF HIDDEN
condition: '{{linkOpened}} = false'
silent: true
- type: say
message: ๐ก Followers info hidden. Extracting what's accessible...
condition: '{{openData}} = false'
- type: js
args: count
code: |-
async function extractDataWithScroll(count) {
// Find the dialog element
const dialog = document.querySelector('div[role="dialog"]');
if (!dialog) {
console.error('Dialog not found');
return null;
}
const results = [];
const uniqueUsernames = new Set();
let retries = 0;
const maxRetries = 10; // Limit the number of scrolling attempts
// Function to scroll and wait for new content to load
async function scrollAndWait() {
const scrollableElement = document.querySelector('div[role="dialog"] div:has(> div > input) ~ div:last-child');
if (scrollableElement) {
console.log('Scrolling to load more users...');
scrollableElement.scrollBy(0, 1000); // Scroll down by 1000 pixels
await new Promise(resolve => setTimeout(resolve, 3000)); // Wait for 1 second for new elements to load
} else {
console.error('Error: Unable to find the scrollable element.');
}
}
// Function to extract user data from the current view
async function extractUsers() {
const userElements = dialog.querySelectorAll('div[role="dialog"] div[style*="flex-direction: column"] > div > div > div:nth-child(1)');
let newUsersFound = false;
for (const userElement of userElements) {
// Find the link element containing the username
const linkElement = userElement.querySelector('a[href^="/"][role="link"]');
if (!linkElement) continue;
// Extract username from the link
const link = linkElement.getAttribute('href');
const username = link.slice(1).replace('/', '');
// Skip if this username has already been processed
if (uniqueUsernames.has(username)) continue;
// Try to find the full name
let fullNameElement = linkElement.nextElementSibling;
let fullName = fullNameElement && fullNameElement.tagName === 'SPAN'
? fullNameElement.textContent.trim()
: '';
// If full name not found, search in other span elements
if (!fullName) {
const spans = userElement.querySelectorAll('span');
for (const span of spans) {
const text = span.textContent.trim();
if (text && text !== username && text !== 'Search') {
fullName = text;
break;
}
}
}
// If both username and full name are found, add to results
if (username && fullName) {
uniqueUsernames.add(username);
results.push({
username: username,
fullName: fullName,
link: `https://www.instagram.com/${username}`
});
newUsersFound = true;
}
}
return newUsersFound;
}
// Main loop: extract users and scroll until target count is reached or max retries hit
while (results.length < count && retries < maxRetries) {
const newUsersFound = await extractUsers();
console.log(`Collected ${results.length} users so far.`);
if (results.length < count) {
await scrollAndWait();
retries++;
}
if (!newUsersFound) {
retries++;
} else {
retries = 0; // Reset the retry counter if new users were found
}
}
console.log(`Total users found: ${results.length}`);
// Trim the results array to the requested count
if (results.length > count) {
console.log(`Trimming results from ${results.length} to ${count}`);
results.splice(count);
}
// Return the results as a JSON string, or undefined if no results
return results.length > 0 ? JSON.stringify(results, null, 2) : undefined;
}
// Function call and result output
extractDataWithScroll(count).then(data => {
console.log(data ? data : "No data found");
});
// Return the promise from the function call
return extractDataWithScroll(count);
param: followers
timeout: 1500000
silent: true
- type: calc
func: extract-json
to: followers
param: followers
index: all
- condition: '{{followers.length}} = 0'
label: FAIL
steps:
- type: say
message: โ๏ธ No data on followers was extracted.
- type: jump
to: ASK FOR INPUT
type: group
- type: say
message: โ
Extracted {{followers.length}} followers' profiles.
label: DATA EXTRACTION
- label: EXTRACT USERNAMES
type: group
steps:
- type: js
code: |-
function extractUsernames(input) {
let usernames = [];
// Convert input to string
const inputStr = String(input);
// Unified regex to extract @mentions and Instagram URLs
const usernameRegex = /@(\w+(\.\w+)*(\_\w+)*(\.\w+)*)|https:\/\/www\.instagram\.com\/([\w\.]+)/g;
let matches;
// Search through the string input for usernames and URLs
while ((matches = usernameRegex.exec(inputStr)) !== null) {
// Push the non-null match group, either mention or URL segment
usernames.push(matches[1] || matches[5]);
}
return usernames;
}
// Using the function, assuming input is already defined
return extractUsernames(input);
param: list
timeout: 15000
args: input
silent: true
- label: ASK SPEED
param: delay
message: >-
๐ก Choose an option or enter a custom delay in milliseconds (1 min = 60 sec
= 60000 ms).
options:
- label: โฉ NORMAL (30 sec.)
value: 30000
- label: โถ๏ธ SLOW (5 min.)
value: 300000
- label: โญ FAST (10 sec.)
value: 10000
- $custom
vision:
enabled: false
mode: area
hint: ''
send: true
type: ask
default: ''
optionsInvalid: false
- type: loop
steps:
- label: NAVIGATE
steps:
- message: ๐ Navigating to **[@{{item.username}}]({{item.link}})**'s profile.
type: say
- type: navigate
url: '{{item.link}}'
waitForIdle: false
silent: true
- type: wait
for: 2s
silent: true
type: group
- label: FOLLOW
steps:
- type: click
selectorType: ai
selector:
- $matches:
- $tag: BUTTON
- $role: button
- $class: _acan
- $class: _acap
- $class: _acas
- $class: _aj1-
- $class: _ap30
- $attribute: type=button
- $style: '-apple-system:14px:600:normal'
- $content: Follow
- $class: x150jy0e
traverse: '-1'
- $class: x1e558r4
traverse: '-1'
- $class: xxz05av
traverse: '1:0:0:0'
- $class: xkfe5hh
traverse: '1:0:0:0'
- $class: xg1prrt
traverse: '0:1:0:0:0'
- $anchor: TAGGED
shift: 23:-184
min: 6
- $size: 1
item:
type: element
container:
id: 0
__$ht: element
value: Follow
silent: true
- selector:
- $matches:
- $tag: BUTTON
- $role: button
- $class: _acan
- $class: _acap
- $class: _acas
- $class: _aj1-
- $class: _ap30
- $attribute: type=button
- $style: '-apple-system:14px:600:normal'
- $content: Follow Back
- $class: xxz05av
traverse: '1:0:0:0'
- $class: xkfe5hh
traverse: '1:0:0:0'
- $class: xg1prrt
traverse: '0:1:0:0:0'
- $class: x1quol0o
traverse: '0:1:0:0:0'
- $anchor: This account is private
shift: 148:-208
- traverse: '-7:0:1:0:1:0:0:0'
$text: This account is private
min: 10
- $size: 1
item:
container:
__$ht: element
id: 1
type: element
value: Follow Back
type: click
selectorType: ai
silent: true
- for: 1s
type: wait
silent: true
- message: '**[@{{item.username}}]({{item.link}})** followed! '
type: say
- type: calc
func: increment
param: result
delta: 1
type: group
- condition: '{{action}} = unfollow'
label: UNFOLLOW
steps:
- selector:
- $matches:
- $tag: BUTTON
- $role: button
- $class: _acan
- $class: _acap
- $class: _acat
- $class: _aj1-
- $class: _ap30
- $attribute: type=button
- $style: '-apple-system:14px:600:normal'
- $content: Following
- $class: x150jy0e
traverse: '-1'
- $class: x1e558r4
traverse: '-1'
- $class: xxz05av
traverse: '1:0:0:0'
- $class: xkfe5hh
traverse: '1:0:0:0'
- $class: xg1prrt
traverse: '0:1:0:0:0'
min: 6
- $size: 1
item:
container:
id: 2
__$ht: element
type: element
value: Following
type: click
selectorType: ai
silent: true
- type: wait
for: 1s
silent: true
- selector:
- $matches:
- $tag: DIV
- $class: x9f619
- $class: xjbqb8w
- $class: x78zum5
- $class: x168nmei
- $class: x13lgxp2
- $class: x5pf9jr
- $class: xo71vjh
- $class: x1pi30zi
- $class: x1swvt13
- $class: x1l90r2v
- $class: xyamay9
- $class: x1uhb9sk
- $class: x1plvlek
- $class: xryxfnj
- $class: x1c4vz4f
- $class: x2lah0s
- $class: xdt5ytf
- $class: xqjyukv
- $class: x1qjc9v5
- $class: x1oa3qoh
- $class: x1nhvcw1
- $style: '-apple-system:14px:400:normal'
- $content: Unfollow
- $class: x7r02ix
traverse: '0:0:0:7:0'
- $class: xf1ldfh
traverse: '0:0:0:7:0'
- $class: x131esax
traverse: '0:0:0:7:0'
- $class: xdajt7p
traverse: '0:0:0:7:0'
- $class: xxfnqb6
traverse: '0:0:0:7:0'
- $anchor: Restrict
shift: '0:50'
- traverse: '-9:7:0'
$text: Restrict
- $anchor: Mute
shift: '0:100'
min: 24
- $size: 1
item:
container:
id: 3
__$ht: element
type: element
value: Unfollow
type: click
selectorType: ai
silent: true
- type: wait
for: 1s
silent: true
- message: >-
โ **[@{{username}}](https://www.instagram.com/{{username}}/)**
unfollowed!
type: say
type: group
- for: custom-delay
delay: '{{delay}}'
type: wait
list: followers
- message: 'โ๏ธ {{result}} users followed. '
type: say
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 ๐ซ๐ฎ