Skip to content

Instantly share code, notes, and snippets.

@pingpoli
Created November 2, 2020 16:20
Show Gist options
  • Save pingpoli/ae6c140001d82a5e94d772d7222f37ac to your computer and use it in GitHub Desktop.
Save pingpoli/ae6c140001d82a5e94d772d7222f37ac to your computer and use it in GitHub Desktop.
<?php
// based on https://stackoverflow.com/questions/12684765/twitter-api-returns-error-215-bad-authentication-data
require_once( "mysql_config.php" );
define( 'ACCESS_TOKEN' , 'YOUR_ACCESS_TOKEN' );
define( 'ACCESS_TOKEN_SECRET' , 'YOUR_ACCESS_TOKEN_SECRET' );
define( 'CONSUMER_KEY' , 'YOUR_CONSUMER_KEY' );
define( 'CONSUMER_KEY_SECRET' , 'YOUR_CONSUMER_KEY_SECRET' );
// get the follower ids
// query parameters
$query = array(
'screen_name' => 'YOUR_TWITTER_USERNAME',
'stringify_ids' => true
);
$result = twitterRequest( "https://api.twitter.com/1.1/followers/ids.json" , "GET" , $query );
$json = json_decode( $result );
if ( isset( $json->ids ) )
{
// build a comma seperated list of all follower ids
$followerIDs = "";
foreach ( $json->ids as $id )
{
$followerIDs .= $id.",";
}
$followerIDs = rtrim( $followerIDs , "," );
// mysql connect
$mysql = new mysqli( MYSQL_HOST , MYSQL_USER , MYSQL_PASS , MYSQL_DATABASE );
if ( $mysql->connect_errno ) error_log( "Failed to connect to MySQL: ".$mysql->connect_error.PHP_EOL );
$mysql->set_charset( "utf8mb4" );
// because this is executed at 2 am, the data is for yesterday, which means the real yesterday is the two days ago
$yesterdaysDataFound = false;
$yesterdaysData = "";
$sql = "SELECT ids FROM followers WHERE `date`=SUBDATE(CURDATE(),2)";
if ( $result = lmysql_select( $mysql , $sql , "" ) )
{
if ( $result->num_rows > 0 )
{
$row = $result->fetch_assoc();
$yesterdaysDataFound = true;
$yesterdaysData = $row['ids'];
}
}
$numberOfFollowers = count( $json->ids );
$new = "";
$unfollowed = "";
if ( $yesterdaysDataFound )
{
$today = explode( "," , $followerIDs );
$yesterday = explode( "," , $yesterdaysData );
// new followers are people that are in the data from today but not in the data from yesterday
for ( $i = 0 ; $i < count( $today ) ; ++$i )
{
$newFollower = true;
for ( $j = 0 ; $j < count( $yesterday ) ; ++$j )
{
if ( $today[$i] == $yesterday[$j] )
{
$newFollower = false;
break;
}
}
if ( $newFollower )
{
$new .= $today[$i].",";
}
}
// unfollowers are people that are in yesterdays data but not in todays
for ( $i = 0 ; $i < count( $yesterday ) ; ++$i )
{
$hasUnfollowed = true;
for ( $j = 0 ; $j < count( $today ) ; ++$j )
{
if ( $yesterday[$i] == $today[$j] )
{
$hasUnfollowed = false;
break;
}
}
if ( $hasUnfollowed )
{
$unfollowed .= $yesterday[$i].",";
}
}
}
$new = rtrim( $new , "," );
$unfollowed = rtrim( $unfollowed , "," );
echo "new: ".$new."<br>";
echo "unfollowed: ".$unfollowed."<br>";
$newUsernames = "";
$unfollowersUsernames = "";
// get new followers usernames
if ( $new != "" )
{
// query parameters
$query = array(
'user_id' => $new
);
$newUsernamesResult = twitterRequest( "https://api.twitter.com/1.1/users/lookup.json" , "POST" , $query );
$jsonUserObjects = json_decode( $newUsernamesResult );
foreach ( $jsonUserObjects as $jsonUserObject )
{
if ( isset( $jsonUserObject->screen_name ) )
{
$newUsernames .= $jsonUserObject->screen_name;
$newUsernames .= ",";
}
}
$newUsernames = rtrim( $newUsernames , "," );
}
// get unfollowers usernames
if ( $unfollowed != "" )
{
// query parameters
$query = array(
'user_id' => $unfollowed
);
$newUsernamesResult = twitterRequest( "https://api.twitter.com/1.1/users/lookup.json" , "POST" , $query );
$jsonUserObjects = json_decode( $newUsernamesResult );
foreach ( $jsonUserObjects as $jsonUserObject )
{
if ( isset( $jsonUserObject->screen_name ) )
{
$unfollowersUsernames .= $jsonUserObject->screen_name;
$unfollowersUsernames .= ",";
}
}
$unfollowersUsernames = rtrim( $unfollowersUsernames , "," );
}
echo "<br>newUsernames: ".$newUsernames."<br>";
echo "<br>unfollowersUsernames: ".$unfollowersUsernames."<br>";
$sql = "INSERT INTO followers ( `date` , `followers` , `ids` , `new` , `unfollowed` ) VALUES ( SUBDATE(NOW(),1) , ? , ? , ? , ? )";
if ( !lmysql_execute( $mysql , $sql , "isss" , $numberOfFollowers , $followerIDs , $newUsernames , $unfollowersUsernames ) )
{
echo "failed to insert<br>";
}
}
function twitterRequest( $url , $method , $query )
{
// oauth parameters
$oauth = array(
'oauth_consumer_key' => CONSUMER_KEY,
'oauth_token' => ACCESS_TOKEN,
'oauth_nonce' => (string)mt_rand(),
'oauth_timestamp' => time(),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_version' => '1.0'
);
// must be encoded before sorting
$oauth = array_map( "rawurlencode" , $oauth );
$query = array_map( "rawurlencode" , $query );
$arr = array_merge( $oauth , $query ); // combine the values then sort
asort( $arr ); // secondary sort (value)
ksort( $arr ); // primary sort (key)
// http_build_query automatically encodes, but the parameters are already encoded, and must be by this point, so we undo the encoding step
$querystring = urldecode( http_build_query( $arr , '' , '&' ) );
// mash everything together for the text to hash
$base_string = $method."&".rawurlencode($url)."&".rawurlencode($querystring);
// same with the key
$key = rawurlencode(CONSUMER_KEY_SECRET)."&".rawurlencode(ACCESS_TOKEN_SECRET);
// generate the hash, which is the oauth signature
$oauth['oauth_signature'] = rawurlencode( base64_encode( hash_hmac( 'sha1' , $base_string , $key , true ) ) );
// some query parameters include commas and they don't work when the parameter is urlencoded above, so decode it here and use it raw, WARNING: this may be a little hacky
if ( $method == "GET" )
{
// build the GET parameters
$url .= "?";
while ( $element = current($query) )
{
$url .= key($query)."=".urldecode($element)."&";
next($query);
}
$url = rtrim( $url , "&" );
}
else
{
// build the POST parameters
$postParameters = "";
while ( $element = current($query) )
{
$postParameters .= key($query)."=".urldecode($element)."&";
next($query);
}
$postParameters = rtrim( $postParameters , "&" );
}
// this is the full value of the authorization line
$auth = "OAuth ".urldecode( http_build_query( $oauth , '' , ', ' ) );
// curl options
$options = array( CURLOPT_HTTPHEADER => array("Authorization: $auth"),
CURLOPT_HEADER => false,
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false);
// add the POST parameters
if ( $method == "POST" )
{
$options[CURLOPT_POSTFIELDS] = $postParameters;
}
// curl
$curl = curl_init();
curl_setopt_array( $curl , $options );
$result = curl_exec( $curl );
curl_close( $curl );
if ( !$result )
{
echo "twitter request failed".PHP_EOL;
return false;
}
return $result;
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment