Skip to content

Instantly share code, notes, and snippets.

@mallardduck
Last active November 16, 2023 23:02
Show Gist options
  • Save mallardduck/fe9a7aed38a156e821980815b27ec5d2 to your computer and use it in GitHub Desktop.
Save mallardduck/fe9a7aed38a156e821980815b27ec5d2 to your computer and use it in GitHub Desktop.
Symfony Panther Tweets Deleter
placeholder to control gist title name

How to use

  1. Use Chrome to login to twitter - do any 2FA you need in browser.
  2. Edit the TWITTER_PROFILE_USERNAME to your Twitter user name (what's in your profile URL).
  3. Edit the CHROME_PROFILE_PATH to your current system OS user's google chrome userdata path.
  4. Run multiple times until all the tweets are removed. (Edit $replies = to false and repeat).
{
"require": {
"symfony/panther": "^2.0",
"dbrekelmans/bdi": "^1.0"
}
}
<?php
use Facebook\WebDriver\Remote\RemoteWebElement;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverElement;
use Symfony\Component\Panther\Client;
require __DIR__.'/vendor/autoload.php'; // Composer's autoloader
global $client;
CONST TWITTER_PROFILE_USERNAME='dan_in_tweets';
const CHROME_PROFILE_PATH='/home/dan/.config/google-chrome';
$client = Client::createChromeClient(null, [
'--no-sandbox',
'--window-size=1200,1500',
'--user-data-dir=' . CHROME_PROFILE_PATH,
]);
$client->request('GET', 'https://twitter.com/' . TWITTER_PROFILE_USERNAME); // Yes, this website is 100% written in JavaScript
$client->waitFor('//span[ text() = "Tweets" ]');
echo "found menu..." . PHP_EOL;
$client->waitForVisibility('//*[@id="react-root"]/div/div/div[2]/main/div/div/div/div[1]/div/div[3]/div/div/section/div');
echo "found timeline" . PHP_EOL;
$replies = true;
if ($replies === false) {
sleep(1);
while (
$element = $client->findElement(WebDriverBy::cssSelector('section article:first-of-type'))
) {
$element->getLocationOnScreenOnceScrolledIntoView();
echo "found element" . PHP_EOL;
sleep(1);
delete_tweet_or_unretweet($element);
sleep(1);
}
} else {
$repliesReload = false;
replies_reloaded:
if ($repliesReload) {
$client->waitFor('//span[ text() = "Tweets" ]');
echo "found menu..." . PHP_EOL;
$client->waitForVisibility('//*[@id="react-root"]/div/div/div[2]/main/div/div/div/div[1]/div/div[3]/div/div/section/div');
echo "found timeline" . PHP_EOL;
$repliesReload = false;
}
$client->findElement(WebDriverBy::xpath('//span[ text() = "Tweets & replies" ]'))->click();
$client->waitForVisibility('//*[@id="react-root"]/div/div/div[2]/main/div/div/div/div[1]/div/div[3]/div/div/section/div');
try {
while ($articles = $client->findElements(WebDriverBy::cssSelector('[aria-label^="Timeline"] article'))) {
$articles = array_values(
array_filter(
$articles,
function (RemoteWebElement $val) {
try {
$val->findElement(WebDriverBy::cssSelector('*[data-testid="UserAvatar-Container-dan_in_tweets"]'));
return true;
} catch (\Exception $e) {
return false;
}
})
);
foreach ($articles as $article) {
$article->getLocationOnScreenOnceScrolledIntoView();
sleep(1);
delete_tweet($article);
}
}
} catch (\Exception $e) {
$client->reload();
$repliesReload = true;
goto replies_reloaded;
}
}
function delete_tweet_or_unretweet(WebDriverElement $element)
{
try {
$unretweet = $element->findElement(WebDriverBy::xpath('//div[@data-testid="unretweet"]'));
} catch (\Exception $e) {
$unretweet = null;
}
if ($unretweet !== null) {
confirm_unretweet($unretweet);
return;
}
delete_tweet($element);
}
function confirm_unretweet(WebDriverElement $unretweet)
{
global $client;
try {
$unretweet->click();
} catch (\Exception $e) {
dd(
$unretweet->getLocation()
);
}
try {
$unretweetConfirm = $client->findElement(WebDriverBy::xpath('//div[@data-testid="unretweetConfirm"]'));
} catch (\Exception $e) {
echo "Couldn't find the unretweet confirm button..." . PHP_EOL;
return false;
}
$unretweetConfirm->click();
return true;
}
function delete_tweet(WebDriverElement $element)
{
global $client;
$caretMenu = null;
try {
$caretMenu = $element->findElement(WebDriverBy::cssSelector('div[data-testid="caret"]'));
echo "found caret" . PHP_EOL;
} catch (\Exception $e) {
echo "CANNOT find caret" . PHP_EOL;
sleep(30);
// TODO: add exception for can't find caret...
}
$caretMenu->click();
$deleteButton = null;
try {
$deleteButton = $client->findElement(WebDriverBy::cssSelector('div[role="menuitem"][tabindex="0"]:first-of-type'));
echo "found delete" . PHP_EOL;
} catch (\Exception $e) {
echo "CANNOT find delete" . PHP_EOL;
sleep(30);
// TODO: add exception for can't find delete...
}
var_dump($deleteButton->getText());
if ($deleteButton->getText() === "Delete") $deleteButton->click();
try {
$deleteConfirm = $client->findElement(WebDriverBy::xpath('//div[@data-testid="confirmationSheetConfirm"]'));
} catch (\Exception $e) {
echo "Couldn't find the delete confirm button..." . PHP_EOL;
return false;
}
$deleteConfirm->click();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment