This is where Tumblr comes in. It’s the future of social networking if your image of the future features intelligent discourse. I love reading other Tumblr users replies, because they’re thoughtful by virtue of the fact that if they’re not, they’ll bring the intellectual property value of their own blog down, and that’s a commodity on Tumblr.
— JM
Update: Don’t do this. It’s seems obvious now, but for some reason I didn’t realize this would get you banned. Indeed the Twitter account used in the experiment was banned. Luckily we explained ourselves, promised to stop and Twitter re-instated the account :)
_________________________________________________________________
A couple months ago, I decided to spend an evening to try and figure out how I could automate the finding & following process on Twitter. First off let me be clear, this wasn’t for my personal twitter account @gapcm. God knows I don’t care about having lots of followers, nor do I care for all the self-promotion and spam that comes along with having lots of followers. That said, there are cases were a person would want to grow their follower base, even at the expense of following a ton of people – that’s were my script comes into play! First a little background..
About two years ago, my fiance and I started a site called Thursday for Dinner. Thursday for Dinner is a video blog dedicated to preserving family recipes. Our mission, as the site says, is to capture our family’s most treasured recipes and make them available to everyone. The Thursday for Dinner twitter account @tfdtv would be used as the guinea pig. Before I started this experiment, @tfdtv had about 600 followers and was listed about 8 times. After about 1.5 months of using my scripts @tfdtv is up to 2160 followers and is listed 126 times.
The idea of the script is this: If you have API credits left and your followers count is greater than your friends count, search Twitter for a specific term (you can also randomly choose from an array of terms). I always want to have more followers than friends (for this script), otherwise you’re looking a bit desperate. For each user who tweeted the given term, check to see if their already in our database as recently added. Assuming their not in the db, and that their followers to friends ratio is within a certain range, then follow them and add their user name to our database. The ratio is calculated like this:
$ratio = (($followers_count - $friends_count) / $followers_count);
It’s important to follow people within a specific ratio, cause if their follower to friends count is reasonably close, their more than likely going to follow you back. More important than just the ratio, is the term you use to search Twitter. The term you search needs to be relevant to your twitter stream/site content. For Thursday for Dinner, I search Twitter for the term foodie. It’s obvious that people who Tweet the word foodie, are going to like the premise of our site. Just before the script finishes, it checks all users in the database that are older than 3 days. If the given user is not following back, they are unfollowed. Whether their following or not, their user name is removed from the database.
To use this script, your going to need to know PHP/MySQL/Apache/REST. Your also going to need PEAR MDB2 and curl running. If you don’t know what I’m talking about, save yourself the trouble. First things first, you need to create a new MySQL database with the following table:
CREATE TABLE IF NOT EXISTS `twitter_users` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(128) NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2259 ;
I created a class called SessionControl to manage the whole MySQL process. I won’t show that here, but you can download it in the zip below. The main class is the twitter.class.php. This is were the bulk of the work is done.
<?
require_once 'sessionControl.class.php';
class Twitter {
//
private $username = 'twitter_username';
private $password = 'twitter_password';
//
public function __construct() {
}
//
public function getUsername() {
return $this->username;
}
//
private function getCURL() {
if (!$curld = curl_init()) {
echo "Could not initialize cURL session<br>";
exit();
}
return $curld;
}
//
public function friendshipDetails($u1,$u2) {
$url = "http://twitter.com/friendships/show.json?source_screen_name=$u1&target_screen_name=$u2";
//echo $url;
//exit;
$curld = $this->getCURL();
curl_setopt($curld, CURLOPT_GET, true);
curl_setopt($curld, CURLOPT_URL, $url);
curl_setopt($curld, CURLOPT_HEADER, false);
curl_setopt($curld, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curld, CURLOPT_HTTPHEADER, array('Expect:'));
$output = curl_exec($curld);
$pile = json_decode($output,true);
return $pile;
}
//
public function follow($u) {
$user = SessionControl::getInstance();
//
$url = "http://$this->username:$this->password@twitter.com/friendships/create/$u.json";
$curld = $this->getCURL();
curl_setopt($curld, CURLOPT_POST, true);
curl_setopt($curld, CURLOPT_URL, $url);
curl_setopt($curld, CURLOPT_HEADER, false);
curl_setopt($curld, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curld, CURLOPT_HTTPHEADER, array('Expect:'));
$output = curl_exec($curld);
//$this->out($output);
$pile = json_decode($output,true);
//$this->out($pile);
//
$sql = "INSERT INTO twitter_users (id, name, date) VALUES (NULL, ".$user->quote($u,"text").", NOW())";
$user->query($sql);
//
return $pile;
}
//
public function isUserInDB($u) {
$user = SessionControl::getInstance();
$result = false;
$sql = 'SELECT * FROM twitter_users WHERE name = '.$user->quote($u,"text");
$sqlRes = $user->query($sql)->fetchAll();
if (!empty($sqlRes)) {
$result = true;
}
return $result;
}
//
private function destroy($u) {
$user = SessionControl::getInstance();
//
$url = "http://$this->username:$this->password@twitter.com/friendships/destroy/$u.json";
$curld = $this->getCURL();
curl_setopt($curld, CURLOPT_POST, true);
curl_setopt($curld, CURLOPT_URL, $url);
curl_setopt($curld, CURLOPT_HEADER, false);
curl_setopt($curld, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curld, CURLOPT_HTTPHEADER, array('Expect:'));
$output = curl_exec($curld);
$pile = json_decode($output,true);
//
return $pile;
}
//
private function out($v) {
echo '<pre>';
print_r($v);
echo '</pre>';
}
//
public function unfollow($u = '', $days = 3) {
$user = SessionControl::getInstance();
if ($u == '' && $days > 1) {
$sql = "SELECT * FROM twitter_users WHERE date < ".$user->quote($this->getSQLDate(time() - ($days * 24 * 60 * 60)),'text');
$uf = $user->query($sql)->fetchAll();
foreach ($uf as $u) {
$details = $this->friendshipDetails($this->username,$u->name);
//$this->out($details);
//exit();
if (isset($details['relationship']['target']['following'])) {
$key = $details['relationship']['target']['following'];
} else {
$key = 0;
}
// not following, remove
if ($key != 1) {
$this->destroy($u->name);
$this->out('Removed ' . $u->name);
}
//exit();
// either way remove from db
$sqlDel = "DELETE FROM twitter_users WHERE id = $u->id";
$user->query($sqlDel);
}
}
}
//
public function getSQLDate($t) {
return date('Y-m-d H:i:s',$t);
}
//
public function getUser($u) {
$url = "http://twitter.com/users/show/$u.json";
$curld = $this->getCURL();
curl_setopt($curld, CURLOPT_GET, true);
curl_setopt($curld, CURLOPT_URL, $url);
curl_setopt($curld, CURLOPT_HEADER, false);
curl_setopt($curld, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curld, CURLOPT_HTTPHEADER, array('Expect:'));
$output = curl_exec($curld);
$pile = json_decode($output,true);
return $pile;
}
//
public function getSearch($s) {
$url = "http://search.twitter.com/search.json?q=$s";
$curld = $this->getCURL();
curl_setopt($curld, CURLOPT_GET, true);
curl_setopt($curld, CURLOPT_URL, $url);
curl_setopt($curld, CURLOPT_HEADER, false);
curl_setopt($curld, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curld, CURLOPT_HTTPHEADER, array('Expect:'));
$output = curl_exec($curld);
$pile = json_decode($output,true);
return $pile;
}
//
public function getLimit() {
$url = "http://twitter.com/account/rate_limit_status.json";
$curld = $this->getCURL();
curl_setopt($curld, CURLOPT_GET, true);
curl_setopt($curld, CURLOPT_URL, $url);
curl_setopt($curld, CURLOPT_HEADER, false);
curl_setopt($curld, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curld, CURLOPT_HTTPHEADER, array('Expect:'));
$output = curl_exec($curld);
$pile = json_decode($output,true);
return $pile;
}
//
public function __sleep() {
return array_keys(get_object_vars($this));
}
//
public function __wakeup() {
}
}
?>
From here, the main control file called go.php runs everything. This is were the steps are executed. Here it is:
<?php
require_once 'twitter.class.php';
set_time_limit(-1);
$twitter = new Twitter();
$limit = $twitter->getLimit();
function getSearchTerm() {
//$terms = array("twitter_search_term_1",);
//$r = mt_rand(0,2);
//return $terms[$r];
return 'twitter_search_term';
}
if ($limit['remaining_hits'] > 0){
$me = $twitter->getUser($twitter->getUsername());
if (($me['followers_count']) > $me['friends_count']) {
$sterm = getSearchTerm();
dump('Hits Left: '.$limit['remaining_hits'].' -- Searching: ' . $sterm);
$search = $twitter->getSearch($sterm);
foreach ($search['results'] as $r) {
$user = $r['from_user'];
if (!$twitter->isUserInDB($user)) {
$info = $twitter->getUser($user);
$friends_count = $info['friends_count'];
$followers_count = $info['followers_count'];
if ($friends_count > 0 && $followers_count > 0) {
$ratio = (($followers_count - $friends_count) / $followers_count);
if ($ratio < 0.2 && $ratio > -0.4) {
$twitter->follow($user);
dump($user . ' followed -> friends: ' . $friends_count . ' - Followers: ' . $followers_count);
}
}
} else {
dump($user . ' in db');
}
}
}
//
$twitter->unfollow();
} else {
dump($limit);
}
function dump($r) {
echo '<pre>';
print_r($r);
echo '</pre>';
}
?>
Key points
“‘Tony, think about it this way. If your worst enemy drops sugar in your coffee, what’s going to happen to you? Nothing. But what if your best friend drops strychnine in your coffee? You’re dead. You have to stand guard at the door of your mind.’ He was saying that the selection of [my friends and advisors] will matter more than anything else, and that you can’t take anybody’s approach as sacrosanct.” – Tony Robbins
I was actually trying to find a way to do this in JavaScript, but eventually gave up. I basically want to remove all non-printable special characters from a string. So for a string like this:
$str = 'Characters like© and ® are not all®wed.';
I would want ‘Characters like and are not allwed.’. The following matches anything in the ASCII range of 0-31 & 128-255 and removes it.
$str = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $str);
“If all of the Greek islands were merged with the mainland, it would be about the size of Alabama; there are 10 million Greeks – and perhaps another 4 million living throughout the world, who still think of themselves as Greek. They are, thanks to their history, magnificent patriots and nationalists – and abominable citizens, who deeply mistrust every government they’ve ever had. Essentially they are fierce individualists, who mistrust so much whatever government happens to be in power as the very idea of government. The have almost no sense of civic responsibility – Pericles complained about this at length – and History has never given them much of a chance to work out a stable system of government.”
By removing the ETag header, you disable caches and browsers from being able to validate files, so they are forced to rely on your Cache-Control and Expires header. Basically you can remove If-Modified-Since and If-None-Match requests and their 304 Not Modified Responses.
Entity tags (ETags) are a mechanism web servers and the browser use to determine whether a component in the browser’s cache matches one on the origin server. Since ETags are typically constructed using attributes that make them unique to a specific server hosting a site, the tags will not match when a browser gets the original component from one server and later tries to validate that component on a different server.
Doing this is simple. First step make sure the Headers mod is enabled:
a2enmod headers
Then, within your apache2.conf file, add the following:
Header unset ETag
FileETag None
You would think a function to remove new lines from a string would exist, but surprisingly it doesn’t. My first thought was to use the nl2br function. nl2br “inserts HTML line breaks before all newlines in a string” and to then strip the br’s out. So something like this:
$string = strip_tags(nl2br($string));
The key word above is “before” though. The function preserves the new lines and just adds a br after it. So I decided to write a function to do it for me:
function removeNewLines($string) {
$string = str_replace( "\t", ' ', $string );
$string = str_replace( "\n", ' ', $string );
$string = str_replace( "\r", ' ', $string );
$string = str_replace( "\0", ' ', $string );
$string = str_replace( "\x0B", ' ', $string );
return $string;
}
Here with at this happy time of year, a few confessions from my beating heart: I have no freaking clue who Nick and Jessica are. I see them on the cover of People and Us constantly when I am buying my dog biscuits and kitty litter. I often ask the checkers at the grocery stores. They never know who Nick and Jessica are either. Who are they? Will it change my life if I know who they are and why they have broken up? Why are they so important? I don’t know who Lindsay Lohan is, either, and I do not care at all about Tom Cruise’s wife.
Am I going to be called before a Senate committee and asked if I am a subversive? Maybe, but I just have no clue who Nick and Jessica are. Is this what it means to be no longer young. It’s not so bad.
Next confession: I am a Jew, and every single one of my ancestors was Jewish. And it does not bother me even a little bit when people call those beautiful lit up, bejeweled trees Christmas trees. I don’t feel threatened. I don’t feel discriminated against. That’s what they are: Christmas trees. It doesn’t bother me a bit when people say, “Merry Christmas” to me. I don’t think they are slighting me or getting ready to put me in a ghetto. In fact, I kind of like it. It shows that we are all brothers and sisters celebrating this happy time of year. It doesn’t bother me at all that there is a manger scene on display at a key intersection near my beach house in Malibu. If people want a
creche, it’s just as fine with me as is the Menorah a few hundred yards away. I don’t like getting pushed around for being a Jew and I don’t think Christians like getting pushed around for being Christians. I think people who believe in God are sick and tired of getting pushed around, period. I have no idea where the concept came from that America is an explicitly atheist country. I can’t find it in the Constitution and I don’t like it being shoved down my throat.Or maybe I can put it another way: where did the idea come from that we should worship Nick and Jessica and we aren’t allowed to worship God as we understand Him?
I guess that’s a sign that I’m getting old, too. But there are a lot of us who are wondering where Nick and Jessica came from and where the America we knew went to.
10. Tipping off the Sonics
9. Michelle Carter (his mother)
8. No More Dunking
7. All-Star Controversy
6. Injuries
5. Peterson Ejection
4. Graduation / Game 7
3. “Come on, MVP?”
2. The Trade
1. Didn’t Always Give 100% – “Do you think Vince Carter has pushed himself, as hard as he should have pushed himself?” VC: “In years past, no”.
This is something I do often for development, but never actually knew if I was doing it correctly. I think I’ve finally gotten this right. What I want is: To be able to setup VirtualHosts in XAMPP and also define a PHP include path within that. I’m assuming you generally have an idea how to setup a VirtualHost in Apache.
The first step is to modify your Hosts file to point traffic to your local computer. My hosts file is here C:\WINDOWS\system32\drivers\etc\hosts, you’ll have to search for yours.
127.0.0.1 www.sun.com
127.0.0.1 sun.com
The next step is to define the VirtualHost within the httpd-vhosts.conf file in the conf directory of your XAMPP setup.
NameVirtualHost *
<VirtualHost *>
DocumentRoot "C:\xampp\htdocs"
ServerName localhost
</VirtualHost>
<VirtualHost *>
DocumentRoot "C:\xampp\htdocs\sun"
ServerName www.sun.com
ServerAlias sun.com
ErrorDocument 404 /error.php
<Directory "C:\xampp\htdocs\sun">
Order allow,deny
Allow from all
</Directory>
<Directory "C:\xampp\htdocs\sun">
php_value include_path ".;C:\php\pear;C:\htdocs\sun\xx"
</Directory>
</VirtualHost>
Using a php_value directive you can define the include path for that VirtualHost right from conf file – thank God.
I wrote a post a few years ago about a quick way to backup a MySQL database using email. Since then I’ve updated that script to upload the file to Amazon S3 instead of emailing it. I use an Amazon S3 PHP Class you can find here to make it happen. Your also going to need cURL enabled on your server. On Ubuntu/Debian that’s as easy as:
sudo apt-get install php5-curl
Here is the script:
require_once 'S3.php';
if (!defined('awsAccessKey')) define('awsAccessKey', 'xxxxxxxxxxxxxxxxxxx');
if (!defined('awsSecretKey')) define('awsSecretKey', 'xxxxxxxxxxxxxxxxxxx');
//
// Check for CURL
if (!extension_loaded('curl') && !@dl(PHP_SHLIB_SUFFIX == 'so' ? 'curl.so' : 'php_curl.dll')) {
exit("\nERROR: CURL extension not loaded\n\n");
}
$tmpDir = "/tmp/";
$user = "dbuser";
$password = "dbpassword";
$dbName = "dbname";
$prefix = "db_";
$sqlFile = $tmpDir.$prefix.date('Ymd_hisA').".sql";
$attachment = $tmpDir.$prefix.date('Ymd_hisA').".tgz";
$creatBackup = "mysqldump -u ".$user." --password=".$password." ".$dbName." > ".$sqlFile;
$createZip = "tar cvzf $attachment $sqlFile";
exec($creatBackup);
exec($createZip);
if (!file_exists($attachment) || !is_file($attachment)) {
die("ERROR: No file");
}
$s3 = new S3(awsAccessKey, awsSecretKey);
$result = false;
if ($s3->putObjectFile($attachment, 'bucket-name', baseName($attachment), S3::ACL_PRIVATE)) {
$result = true;
}
unlink($sqlFile);
unlink($attachment);
For reasons I won’t go into, I wanted to force Apache to strip the www from the URLs. So if someone typed in http://www.abc.com, Apache would automatically send them to http://abc.com. First thing to do is make sure ModRewrite is enabled, once that’s done simply put the following in your VirtualHost configuration or in the associated .htaccess file:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.(.+)$
RewriteRule ^(.*)$ http://%1/$1 [R=301,L]