Twitter automization test (4 factors, 32 accounts)

dohyung97022

Regular Member
Joined
Feb 22, 2021
Messages
253
Reaction score
269
This post is for sharing my results to attempt twitter automization.
There are 4 main factors that will be considered in this test.


What chromedriver tool you using.
1-1. Undetected-chromedriver (https://github.com/ultrafunkamsterdam/undetected-chromedriver)
1-2. Real browser in debugging mode
1-3. Chromium

Automated actions or manual actions.
2-1. Automated action
2-2. Manual action

If you Profiled or not.
3-1. Profiled
3-2. Non profiled

If you run in Headless or not.
4-1. Headless
4-2. Non headless


Common factors.
- All accounts are created in 4G proxy ips.
- All test actions will be done in 4G proxy ips.
- All accounts will be following 10 accounts per day, liking 10 posts per day.
- All tests will be done for 10 days+.
- All tests accounts are 5 days old.
- All test cookies will be saved and loaded.


Total of 32 accounts.

1-1 2-1 3-1 4-1

1-1 2-1 3-1 4-2

1-1 2-1 3-2 4-1

1-1 2-1 3-2 4-2

1-1 2-2 3-1 4-1

1-1 2-2 3-1 4-2

1-1 2-2 3-2 4-1

1-1 2-2 3-2 4-2


1-2 2-1 3-1 4-1

1-2 2-1 3-1 4-2

1-2 2-1 3-2 4-1

1-2 2-1 3-2 4-2

1-2 2-2 3-1 4-1

1-2 2-2 3-1 4-2

1-2 2-2 3-2 4-1

1-2 2-2 3-2 4-2


1-3 2-1 3-1 4-1

1-3 2-1 3-1 4-2

1-3 2-1 3-2 4-1

1-3 2-1 3-2 4-2

1-3 2-2 3-1 4-1

1-3 2-2 3-1 4-2

1-3 2-2 3-2 4-1

1-3 2-2 3-2 4-2
 
Will follow this thread if you're serious. Do you currently use any anti-detect browsers, or is this out of the picture for the test?
 
Will follow this thread if you're serious. Do you currently use any anti-detect browsers, or is this out of the picture for the test?
Dead serious.

I will not use any anti-detection browsers.
 
Dead serious.

I will not use any anti-detection browsers.
Is there any reason specifically why? I currently use anti-detect browsers when I need to, but I'm just curious as to why you won't use them for this test. Is this because of the cost, failing to do the job, or some other purpose?
 
Is there any reason specifically why? I currently use anti-detect browsers when I need to, but I'm just curious as to why you won't use them for this test. Is this because of the cost, failing to do the job, or some other purpose?

Well, the cost is the main factor.
I have used multilogin and incognition, camelio before.

1. Multilogin is too costly, and is not great to scale, since it has limited account number per plan.

2. Incognition is not frequently updated.

3. Camelio gets caught and is not as great to be honest.

I do not think these anti-detect browsers are that complicated to make. (Except for multilogin with webgl and graphics card manipulation, I don't know how they pulled that off.)

They use different profiles, cookies, useragents, ips, and some cases, graphics, but if you use a common graphics card, that is hard to detect.
 
Python version : 3.9
Selenium version : 4.7.2
undetected-chromedriver version : 3.1.7

So, I think I am almost done with the code here,
If you want to run this code, you would have to change it a bit.

Locations like 'Applications/Google Chrome.app/Contents/MacOS/Google Chrome' is mac specific.
Some xpaths are written in Korean, But if you test it a bit, you can change it to your native language.

Run the code by changing the followings.
Python:
test(
    input_test_case=f'{REAL_CHROMEDRIVER}_{AUTOMATION}_{NON_PROFILED}_{NON_HEADLESS}',
    input_username='',
    input_password=''
)

You can change the 4 parameters of 'input_test_case'
UNDETECTED_CHROMEDRIVER
REAL_CHROMEDRIVER
DEFAULT_CHROMEDRIVER

AUTOMATION
MANUAL

PROFILED
NON_PROFILED

HEADLESS
NON_HEADLESS

the input() function waits for your input in the console.

The program will first ask you for the test number,
Type in the test number and press enter in the console.

When the task is MANUAL, the program will ask you to do a task and wait for your input.
Finish it and press enter in the console.


Apologies for the spaghetti code here, but I wanted to post it as a single file.

Source.
Python:
import os
import subprocess
import time

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.remote.webelement import WebElement
import undetected_chromedriver
from selenium.webdriver.common.by import By
import pickle

UNDETECTED_CHROMEDRIVER: str = '1-1'
REAL_CHROMEDRIVER: str = '1-2'
DEFAULT_CHROMEDRIVER: str = '1-3'

AUTOMATION: str = '2-1'
MANUAL: str = '2-2'

PROFILED: str = '3-1'
NON_PROFILED: str = '3-2'

HEADLESS: str = '4-1'
NON_HEADLESS: str = '4-2'

driver: webdriver.Chrome
options: Options
profile: str = ''
test_case: str
username: str
password: str
test_no: int
picture_log_no: int = 0
article_no: int = 0
like_no: int = 0


# 1-1
def set_undetected_chromedriver_options():
    global options
    options = undetected_chromedriver.ChromeOptions()


def open_undetected_chromedriver():
    global options, driver, profile
    if profile == '':
        driver = undetected_chromedriver.Chrome(options=options)
    else:
        driver = undetected_chromedriver.Chrome(options=options, user_data_dir=profile)


# 1-2
def set_real_chromedriver_options():
    global options
    options = webdriver.ChromeOptions()
    options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")


def open_real_chromedriver():
    global options, driver, profile

    cmd = [r'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', '--remote-debugging-port=9222',
           '--no-first-run', '--no-default-browser-check']
    if profile != '':
        cmd.append(f'--user-data-dir={profile}')
        cmd.append(f'--profile-directory=default')
    subprocess.Popen(cmd)

    driver = webdriver.Chrome(options=options)


# 1-3
def set_default_chromedriver_options():
    global options
    options = webdriver.ChromeOptions()


def open_default_chromedriver():
    global options, driver
    driver = webdriver.Chrome(options=options)


# 2-1 (first time when no cookie)
def automated_login():
    def login():
        driver.find_element(By.XPATH, "//span[text()='로그인']").click()
        capture_picture_log()
        driver.find_element(By.XPATH, "//input[@autocomplete='username']").send_keys(username)
        capture_picture_log()
        driver.find_element(By.XPATH, "//span[text()='다음']").click()
        capture_picture_log()
        driver.find_element(By.XPATH, "//input[@name='password']").send_keys(password)
        capture_picture_log()
        driver.find_element(By.XPATH, "//span[text()='로그인하기']").click()
        capture_picture_log()

    automation_retry(function=login, pass_if_fail=True)


# 2-1
def automated_likes():
    def click_close_popup():
        close_buttons = driver_find_multiple_query_elements(["//div[@aria-label='닫기']", "//div[@aria-label='Close']"])
        if len(close_buttons) > 0:
            close_buttons[0].click()
        capture_picture_log()

    def click_next_article():
        global article_no
        articles = driver.find_elements(By.XPATH, "//article")
        article = articles[article_no]
        article_no = article_no + 1
        article.click()
        capture_picture_log()

    def click_people_who_like_button():
        people_who_like_button = driver_find_multiple_query_elements(
            ["//span[text()='마음에 들어요']", "//span[text()='Likes']"])
        # if no people_who_like_button
        if len(people_who_like_button) == 0:
            # go back
            driver.back()
            capture_picture_log()
            # retry
            return
        else:
            people_who_like_button[0].click()

    def follow_article_liked():
        global like_no
        follow_buttons = driver_find_multiple_query_elements(["//span[text()='팔로우']", "//span[text()='Follow']"])
        capture_picture_log()

        for i in range(len(follow_buttons)):
            follow_buttons[i].click()
            capture_picture_log()
            like_no = like_no + 1
            if like_no == 10:
                break

    def click_home_button():
        home_buttons = driver.find_elements(By.XPATH, "//a[@href='/home']")
        if len(home_buttons) > 0:
            home_buttons[0].click()

    print('click_next_article')
    automation_retry(function=click_close_popup)
    automation_retry(function=click_next_article)

    print('click_people_who_like_button')
    automation_retry(function=click_close_popup)
    automation_retry(function=click_people_who_like_button)

    print('follow_article_liked')
    automation_retry(function=follow_article_liked)

    print('click_home_button')
    automation_retry(function=click_close_popup)
    automation_retry(function=click_home_button)

    global like_no
    if like_no < 10:
        automated_likes()


def driver_find_multiple_query_elements(querys: list[str], by: By = By.XPATH) -> list[WebElement]:
    elements = []
    for query in querys:
        elements.extend(driver.find_elements(by, query))
    return elements


# 2-1
def automation_retry(function: callable, pass_if_fail: bool = False, retry: int = 5):
    for i in range(retry):
        try:
            function()
            return
        except:
            time.sleep(1)
            pass
    if not pass_if_fail:
        function()


# 2-2 (first time when no cookie)
def manual_login():
    print(f'username: {username}')
    print(f'password: {password}')
    input("manually login.\n")
    print('good job!')


# 2-2
def manual_likes():
    input("manually like people.\n")
    print('good job!')


# 3-1
def profiled():
    global profile, test_case, options
    profile = f'./profiles/{test_case}'
    options.add_argument(f'--user-data-dir={profile}')
    options.add_argument(f'--profile-directory=default')


# 3-2
def non_profiled():
    global profile
    profile = ''


# 4-1
def headless():
    global options
    options.add_argument('--headless')


# 4-2
def non_headless():
    return


# common
def open_twitter():
    global driver
    driver.get('https://www.twitter.com')


# common
def save_cookies():
    global test_case, driver
    pickle.dump(driver.get_cookies(), open(f"./cookies/{test_case}_cookie.pkl", "wb"))


# common
def load_cookies():
    global profile, driver
    cookies = pickle.load(open(f"./cookies/{test_case}_cookie.pkl", "rb"))
    for cookie in cookies:
        driver.add_cookie(cookie)


# common
def cookie_exists():
    return os.path.isfile(f"./cookies/{test_case}_cookie.pkl")


# common
def reset_4g_proxy():
    input("reset your 4g proxy ip.\n")
    print('good job!')


# common
def common_setup():
    global picture_log_no, article_no, like_no
    picture_log_no = 0
    article_no = 0
    like_no = 0

    if not os.path.isdir(f"./cookies"):
        os.mkdir(f"./cookies")

    if not os.path.isdir(f"./profiles"):
        os.mkdir(f"./profiles")

    if not os.path.isdir(f"./log"):
        os.mkdir(f"./log")
    if not os.path.isdir(f"./log/{test_no}"):
        os.mkdir(f"./log/{test_no}")
    if not os.path.isdir(f"./log/{test_no}/{test_case}"):
        os.mkdir(f"./log/{test_no}/{test_case}")


# common
def capture_picture_log():
    global picture_log_no
    time.sleep(5)
    driver.save_screenshot(f'./log/{test_no}/{test_case}/{picture_log_no}.png')
    picture_log_no = picture_log_no + 1


def get_test_case_arguments(input_test_case: str):
    test_arguments = input_test_case.split('_')
    argument_1 = test_arguments[0]
    argument_2 = test_arguments[1]
    argument_3 = test_arguments[2]
    argument_4 = test_arguments[3]
    return argument_1, argument_2, argument_3, argument_4


def test(input_test_case: str, input_username: str, input_password: str):
    global test_case, username, password

    test_case = input_test_case
    username = input_username
    password = input_password
    driver_type, action_type, profile_type, headless_type = get_test_case_arguments(input_test_case)

    if headless_type == HEADLESS and action_type == MANUAL:
        raise 'HEADLESS cannot be MANUAL'

    # setup
    common_setup()

    # set chromedriver options
    if driver_type == UNDETECTED_CHROMEDRIVER:
        set_undetected_chromedriver_options()
    elif driver_type == REAL_CHROMEDRIVER:
        set_real_chromedriver_options()
    elif driver_type == DEFAULT_CHROMEDRIVER:
        set_default_chromedriver_options()

    # set profile
    if profile_type == PROFILED:
        profiled()
    elif profile_type == NON_PROFILED:
        non_profiled()

    # set headless
    if headless_type == HEADLESS:
        headless()
    elif headless_type == NON_HEADLESS:
        non_headless()

    # open chromedriver
    if driver_type == UNDETECTED_CHROMEDRIVER:
        open_undetected_chromedriver()
    elif driver_type == REAL_CHROMEDRIVER:
        open_real_chromedriver()
    elif driver_type == DEFAULT_CHROMEDRIVER:
        open_default_chromedriver()

    open_twitter()

    # load cookie
    if cookie_exists():
        load_cookies()
        open_twitter()

    # login, like
    if action_type == AUTOMATION:
        automated_login()
        automated_likes()
    if action_type == MANUAL:
        manual_login()
        manual_likes()

    # save cookie
    save_cookies()

    print('SUCCESS')


test_no = int(input("test No. is?\n"))
print(f'test No. {test_no}')

test(
    input_test_case=f'{REAL_CHROMEDRIVER}_{AUTOMATION}_{NON_PROFILED}_{NON_HEADLESS}',
    input_username='',
    input_password=''
)


Will start testing tomorrow.

If I suspect that my code is the problem,
I will redo the automation process with changed code.


Spoilers,
I have already tested some cases to code this,
But the driver seems to have a big impact.
 
Last edited:
Is there need for VPS to run this or can it be run on chrome browser directly without VPS or other server.
VPS is better for “quantifying” results. A desktop can have several other factors which can have false positives.


Good thread OP. :)
 
Nice share, I'm not available to copy and run the test right now but I will test later.
When I tested with the default chromedriver, the website seems to be much more slow.

This might be a lte problem or a local computer slowing down issue?
Or it might be twitter blocking me.
I am not sure.

I am going to test it tomorrow again, but to be sure, Can you or somebody test and share it too?


Much help would be apreceated.

The Test case.
DEFAULT_CHROMEDRIVER
AUTOMATION
NON_PROFILED
NON_HEADLESS
 
So, I have some bad news.
But some interesting insights.

Every test cases got instant banned.

My first thought was that my accounts had a red flag when created,
I had no issue when using this code with my main twitter account,
and a bit aged twitter account. (phone varified, 2 years old, 2 weeks old) to code this program.

If the 2 week aged account did not get blocked,
but the new accounts were blocked,
It must be the new accounts that has the problem.

So I thought the accounts were the problem.

Opened a guest browser from chrome.
Logged into the guest browser and have done some manual testing on the created accounts.
All got banned after a single action is taken. (like, comment, but not watching posts.)

Follow as chrome guest.


Just browse and take 1 action as chrome guest.


Just browse and take 1 action as chrome profile.


Just browse and take 1 action as firefox.


See that this is not a warming up issue.
I only took 1 action, and it was a instant ban.
I am leaning more towards a creation issue.


possible red flags on account creation.

1. the email address

I did automate the process with the same emails addresses again and again. Could have a domain trust score.
(@coooooool.com, @popcornfly.com, @coffeetimer24.com etc)
(https://rapidapi.com/calvinloveland335703-0p6BxLYIH8f/api/temp-mail44/).


2. the user-agent
I did not change the user-agent while creating the accounts.
I have doubted this would be a problem.
The user-agent was very common, android phone user agent.
But to be careful, I sould change this if this was the problem.


3. unexpected amounts of traffic.
screen-data-dog.png

This is the log of successfully created twitter accounts.
I have created a batch of 300 accounts within the period of 2 days.
They could have detected unusual traffic.


4. Automated process
They could have detected automation when the account was created.
Need to test manualy next time.


5. The chromium browser itself
They might have tricked me, making it look like it has passed.
But It was a fake card all along.


Well,
It is a bit depressing that I have to flush down 300 accounts,
And recode the automation process of registering.
I might have to rethink the browsers, or tools I was using.

But I will keep on testing with differently created accounts.


Considering the 5 red flags I assume twitter has on me.

I will
1. Create the accounts on a trusted email domain. (bing, google)
2. Change user-agents when created.
3. Create only 32 accounts in a separated timeframe to give less suspicion on traffic.
4. Do it manually.
5. Use the default browser chrome, no selenium attached.



Will create the accounts today and test again tomorrow,
Will be a bit delayed.
 
Last edited:
Well, an account of mine got banned 2 days back with zero post and zero activities that too from a new device. It’s funny how it said they found botting activities when in fact i did not do shit. Just created the account, added profile picture and cover picture.

That could be the same with your results too op. Perhaps keep an account with no activity as another case, and see if gets banned.

I dunno what twitter engineers are smoking these days but hey..
 
Well, an account of mine got banned 2 days back with zero post and zero activities that too from a new device. It’s funny how it said they found botting activities when in fact i did not do shit. Just created the account, added profile picture and cover picture.

That could be the same with your results too op. Perhaps keep an account with no activity as another case, and see if gets banned.

I dunno what twitter engineers are smoking these days but hey..
A while ago, I also tried automate and manual Twitter, all locked and ask for PVA after some action. I think the problem could be like IG that they want all accounts got a phone number attach to it.
 
Well, an account of mine got banned 2 days back with zero post and zero activities that too from a new device. It’s funny how it said they found botting activities when in fact i did not do shit. Just created the account, added profile picture and cover picture.

That could be the same with your results too op. Perhaps keep an account with no activity as another case, and see if gets banned.

I dunno what twitter engineers are smoking these days but hey..
Smoke weed every day.

Thanks for the insight.
Will create about 4 more accounts and do nothing after creation.
All account will take no action after creation.
account 1,2 will wait about 3 days and see if it gets blocked.
account 3,4 will wait about 5 days (my case) and see if it gets blocked.

You should replace the selenium send_keys() function with something that looks more human, since send_keys() sends all the text at once and use random waiting intervals between action as to not look like a bot.

https://stackoverflow.com/questions/51651732/how-to-type-like-a-human-via-actionchains-send-keys

I thought that this would not be a problem before testing,
I used send_keys all over the place when I automated the register method,
So I thought twitter did not detect it.
But now that the register method seems to be the problem, I will change that.
Thanks for the reference.

Google seems to check this when automating logins or registers.
 
A while ago, I also tried automate and manual Twitter, all locked and ask for PVA after some action. I think the problem could be like IG that they want all accounts got a phone number attach to it.
If that is the case.
I might be screwed.

Almost no service provides Korean based phone varificaition.
Checked sites like https://5sim.net/ or other websites, but NOPE.

I could buy my own phones, but that is limited by 3 lines per identity.

Wish this was not the case.
 
Back
Top