@moto1203 hat mich über Twitter kontaktiert und gefragt, ob es nicht irgendwie möglich sei ein Programm zu schreiben, dass bei bestimmten Hashtags eine @reply erzeugt. Thematisch ging’s konkret um den bekannten Motorradhersteller “Harley Davidson”, der kürzlich die Tochter- und Traditionsfirma “BUELL” einfach so dicht gemacht hat. Buell Motorräder wurden “nicht oft genug” verkauft, was aber nichts daran ändert, das die Kisten ihren Markt hatten. Das fanden wir jedenfalls schon ziemlich kacke von Harley Davidson.
Also habe ich kurzerhand einen neuen Twitter-Account mit dem klangvollen Namen “@subversives” erstellt und ein kleines PHP-Skript geschrieben, dass neue Tweets mit einem #harley Hashtag sucht und eine zufällig ausgewählte Antwort erstellt. Natürlich konfigurierbar. :-)
Dazu gibt es zwei kleine Funktionen. Die erste Funktion sucht Tweets nach einem bestimmten Suchbegriff:
// Twitter search api
function twt_search( $username, $password, $search, $since_id = 0, $rpp = 30, $page = 1 )
{
// Curl init
$curl = curl_init();
// Curl basic settings
curl_setopt( $curl, CURLOPT_URL, 'http://search.twitter.com/search.json' );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $curl, CURLOPT_USERPWD, "$username:$password" );
curl_setopt( $curl, CURLOPT_POST, true );
// Build POST parameters
$post_arr = array();
$post_arr[] = "q=" . urlencode( $search );
if( !empty( $since_id ) )
$post_arr[] = "since_id=" . $since_id;
if( !empty( $rpp ) )
$post_arr[] = "rpp=" . $rpp;
if( !empty( $page ) )
$post_arr[] = "page=" . $page;
curl_setopt( $curl, CURLOPT_POSTFIELDS, implode( '&', $post_arr ) );
// Exec, set return
$return = json_decode( curl_exec( $curl ) );
// Close curl
curl_close( $curl );
return $return;
}
Übergeben werden Benutzername, Passwort, der Suchbegriff und – wenn bekannt – auch die “since_id”, welche es ermöglicht nur Tweets mit einer höheren ID zu ermitteln. Das ist für unser lustiges Protest-Skript wichtig, denn wir wollen die Leute ja nicht zuspammen und immer wieder auf die gleichen Tweets antworten. Nur neue Tweets seit der letzten Suche sollen berücksichtigt werden, also merken wir uns einfach die höchste Tweet ID und übergeben diese wieder bei der nächsten Suche. Der Parameter “rpp” steht übrigens für “results per page” – mit der Voreinstellung werden also maximal 30 Tweets zurückgegeben.
Technisch wird die Abfrage über die CURL-Bibliothek realisiert, theoretisch ginge es aber auch mit “file_get_contents” oder “fopen”, aber ich mag CURL. Ist ganz praktisch.
Die zweite benötigte Funktion erstellt über die Twitter API einen neuen Tweet. Auch hier stehen wieder einige Parameter zur Verfügung:
// Twitter update api
function twt_update( $username, $password, $status, $in_reply_to_status_id = '', $lat = '', $long = '' )
{
// Curl init
$curl = curl_init();
// Curl basic settings
curl_setopt( $curl, CURLOPT_URL, 'http://twitter.com/statuses/update.json' );
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $curl, CURLOPT_USERPWD, "$username:$password" );
curl_setopt( $curl, CURLOPT_POST, true );
// Build POST parameters
$post_arr = array();
$post_arr[] = "status=" . urlencode( $status );
if( !empty( $in_reply_to_status_id ) )
$post_arr[] = "in_reply_to_status_id=" . $in_reply_to_status_id;
if( !empty( $lat ) )
$post_arr[] = "lat=" . $lat;
if( !empty( $long ) )
$post_arr[] = "long=" . $long;
curl_setopt( $curl, CURLOPT_POSTFIELDS, implode( '&', $post_arr ) );
// Exec, set return
$return = json_decode( curl_exec( $curl ) );
// Close curl
curl_close( $curl );
return $return;
}
Neben Nutzername und Passwort können an diese Funktion der Statustext (Tweet) und der sehr wichtige Parameter “in_reply_to_status_id” übergeben werden. Wird die ID eines Tweets mit diesem Parameter übergeben, erkennt Twitter, dass es sich um eine @rely zu dem Tweet mit der übergebenen ID handelt. Da wir ja Antworten senden wollen, genau das richtige! Die Parameter “lat” und “long” sind übrigens nur schmückendes Beiwerk und werden von mir nicht genutzt. Ich habe das nur der Vollständigkeit halber mit rein genommen.
Nun zur Anwendung der Funktionen:
// Twitter account settings
$username = 'user';
$password = 'pass';
// Configure some reply variations in english and german
$replys_en = array(
'Always remember #BUELL if you talk about Harley Davidson!',
'Hey, remember #BUELL if you talk about Harley Davidson!',
'Remember #BUELL FOREVER! Harley Davidson? NEVER!',
'It makes me sick, that you dont talk about #BUELL ;-)',
'#BUELL #BUELL #BUELL remember the Harley Davidson #FAIL',
'#BUELL killed by Harley Davidson in 2k9. Remember that!',
'#BUELL will never die. Harley Davidson will.'
);
$replys_de = array(
'Erinner dich an #BUELL wenn du über Harley Davidson sprichst!',
'Harley Davidson? Die Firma, die #BUELL zerstört hat?',
'#BUELL FOREVER! Harley Davidson? NEVER!',
'Es macht mich krank, dass nicht auch über #BUELL sprichst!',
'#BUELL #BUELL #BUELL denk an den Harley Davidson #FAIL',
'#BUELL wurde von Harley Davidson 2k9 geschlossen. HD is next!',
'#BUELL wird niemals sterben, aber Harley Davidson. #Rentnerchopper'
);
// Load last parsed tweet id
if( file_exists( 'harley_since_id.dat' ) )
$harley_since_id = file_get_contents( 'harley_since_id.dat' );
else
$harley_since_id = 0;
// Search for tag harley
$harley = twt_search( $username, $password, '#harley', $harley_since_id );
if( is_object( $harley ) )
{
if( count( $harley -> results ) )
{
foreach( $harley -> results AS $rkey => $tweet )
{
// Descending order, so the first array element has the highest id!
if( $rkey == 0 )
$harley_since_id = $tweet -> id;
// Only reply if buell not found
if( stripos( $tweet -> text, 'buell' ) === false && $tweet -> from_user != $username )
{
if( $tweet -> iso_language_code != 'de' )
$reply = "@{$tweet->from_user} " . $replys_en[ array_rand( $replys_en ) ];
else
$reply = "@{$tweet->from_user} " . $replys_de[ array_rand( $replys_de ) ];
echo "$reply\n";
// Send reply
twt_update( $username, $password, $reply, $tweet -> id );
}
}
}
else echo "No new tweets to tag #harley\n";
}
// Store last parsed tweet id
file_put_contents( 'harley_since_id.dat', $harley_since_id );
Zuerst werden in zwei Arrays englische bzw. deutsche Ausdrücke des Protests definiert, danach wird aus einer kleinen Datendatei die ID des letzten gefundenen Tweets geladen und die Suche ausgeführt.
Die Suchfunktion gibt ein Objekt zurück, dass in der Eigenschaft “results” die gefundenen Tweets als Array enthält, welches anschließend durchlaufen wird. Beim ersten gefundenen Tweet wird die “since_id” neu gesetzt. Warum beim ersten? Na, weil die Tweets absteigend sortiert sind – der Erste ist also immer der Aktuellste.
Enthält der Tweet nicht das Wort “buell”, wird in der entsprechenden Sprache (deutsch oder englisch) eine @reply an den Twitter-Account des Tweets gesendet.
Tja, das war’s eigentlich schon. Wer es mal probieren und einen Spruch reingedrückt bekommen will, der zwitschert einfach mal was mit dem Hashtag #harley – es dauert allerdings ein wenig, bis die Antwort kommt, das Skript läuft nur alle 15 Minuten.
Ich bin gespannt, wie lange der Account aktiv ist, ohne als Spam gemeldet zu werden. ;-)
