Getting Socket.IO to Run On Windows Node.JS Setup (Until NPM is Built)

November 21st, 2011

I just spent a little bit of time figuring out how to get Socket.IO to run on a Windows Node.JS setup. The best line in Ryan Dahl’s intro to Node.JS video is:

Windows is very important. Just like php

The package manager for Node.JS NPM (in Windows) is currently in development. Until that gets released, getting Socket.IO to run on Windows takes a little downloading, copy and pasting (basically copying the folder structure in Linux). First download the source for socket.io, socket.io.client and uglifyjs. Then in the same folder were node.exe exists create the following folder structure:

Code:
node_modules
   -socket.io (extract download here)
     - node_modules
       - socket.io.client (extract download here)
          - node_modules
             - uglify-js (extract download here)

That’s it, follow the instructions on the socket.io website and all should work.

Apache SVN Add New User to dav_svn.authz

November 11th, 2011
Code:
htpasswd -m /etc/apache2/dav_svn.passwd user

A Sister’s Eulogy for Steve Jobs – NYTimes.com

November 1st, 2011
Incredibly moving, an amazing piece. I loved “Fashion is what seems
beautiful now but looks ugly later; art can be ugly at first but it
becomes beautiful later.”

http://www.nytimes.com/2011/10/30/opinion/mona-simpsons-eulogy-for-steve-jobs…

Why Is This Cargo Container Emitting So Much Radiation?

October 29th, 2011
http://m.wired.com/magazine/2011/10/ff_radioactivecargo/all/1

This article isn’t that interesting, but this one piece is:

“It was hardly the first fishy shipment to pass through Gioia Tauro.
Famously, just six weeks after 9/11, workers there heard noises coming
from inside a container being transshipped to Nova Scotia via
Rotterdam. Inside, police found an Egyptian-born Canadian carrying a
Canadian passport, a satellite phone, a cell phone, a laptop, cameras,
maps, and security passes to airports in Canada, Thailand, and Egypt.
The container’s interior was outfitted with a bed, a water supply, a
heater, and a toilet. Nicknamed Container Bob, the man posted bail in
Italian court and was never seen again.”

..

Debugging VSTO Office Addin with Visual Studio 2010

July 16th, 2011

I ran into a strange problem today that my breakpoints were not being hit in Visual Studio 2010 for an Office Addin. After reading here, here and here. I realized I needed to let Visual Studio know the .NET version Office was using. So by adding excel.exe.config in the Excel.exe folder with:

Code:
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

fixed the problem.

The State of the Union – Signed The Boss

June 1st, 2011

This was supposedly written by a genuine small business owner, but I supposed it could be apocryphal.

—————————————————————————-

To All My Valued Employees,

There have been some rumblings around the office about the future of this company, and more specifically, your job. As you know, the economy has changed for the worse and presents many challenges. However, the good news is this: The economy doesn’t pose a threat to your job. What does threaten your job however, is the changing political landscape in this country. However, let me tell you some little tidbits of fact which might help you decide what is in your best interests.

First, while it is easy to spew rhetoric that casts employers against employees, you have to understand that for every business owner there is a Back Story. This back story is often neglected and overshadowed by what you see and hear. Sure, you see me park my Mercedes outside. You’ve seen my big home at last years Christmas party. I’m sure; all these flashy icons of luxury conjure up some idealized thoughts about my life.

However, what you don’t see is the BACK STORY: I started this company 28 years ago. At that time, I lived in a 300 square foot studio apartment for 3 years. My entire living apartment was converted into an office so I could put forth 100% effort into building a company, which by the way, would eventually employ you.

My diet consisted of Ramen Pride noodles because every dollar I spent went back into this company. I drove a rusty Toyota Corolla with a defective transmission. I didn’t have time to date. Often times, I stayed home on weekends, while my friends went out drinking and partying. In fact, I was married to my business — hard work, discipline, and sacrifice.

Meanwhile, my friends got jobs. They worked 40 hours a week and made a modest $50K a year and spent every dime they earned. They drove flashy cars and lived in expensive homes and wore fancy designer clothes. Instead of hitting the Nordstrom’s for the latest hot fashion item, I was trolling through the discount store extracting any clothing item that didn’t look like it was birthed in the 70′s. My friends refinanced their mortgages and lived a life of luxury.

I, however, did not. I put my time, my money, and my life into a business with a vision that eventually, some day, I too, will be able to afford these luxuries my friends supposedly had.

So, while you physically arrive at the office at 9 A.M., mentally check in at about noon, and then leave at 5 P.M., I don’t. There is no “off” button for me. When you leave the office, you are done and you have a weekend all to yourself. I unfortunately do not have the freedom. I eat, and breathe this company every minute of the day. There is no rest. There is no weekend. There is no happy hour.

Every day this business is attached to my hip like a 1 year old special-needs child. You, of course, only see the fruits of that garden — the nice house, the Mercedes, the vacations… you never realize the Back Story and the sacrifices I’ve made.

Now, the economy is falling apart and I, the guy that made all the right decisions and saved his
money, have to bail-out all the people who didn’t. The people that overspent their paychecks suddenly
feel entitled to the same luxuries that I earned and sacrificed a decade of my life for.

Yes, business ownership has is benefits but the price I’ve paid is steep and not without wounds. Unfortunately, the cost of running this business, and employing you, is starting to eclipse the threshold of marginal benefit and let me tell you why:

I am being taxed to death and the government thinks I don’t pay enough. I have state taxes. Federal taxes. Property taxes. Sales and use taxes. Payroll taxes. Workers compensation taxes. Unemployment taxes. Taxes on taxes. I have to hire a tax man to manage all these taxes and then guess
what? I have to pay taxes for employing him.

Government mandates and regulations and all the accounting that goes with it, now occupy most of my
time. On Oct 15th, I wrote a check to the US Treasury for $288,000 for quarterly taxes. You know what my “stimulus” check was? Zero.. Nada. Zilch.

The question I have is this: Who is stimulating the economy? Me, the guy who has provided 14 people good paying jobs and serves over 2,200,000 people per year with a flourishing business? Or, the single mother sitting at home pregnant with her fourth child waiting for her next welfare check? Obviously, government feels the latter is the economic stimulus of this country.

The fact is, if I deducted (Read: Stole) 50% of your paycheck you’d quit and you wouldn’t work here. I mean, why should you? That’s nuts. Who wants to get rewarded only 50% of their hard work? Well, I agree which is why your job is in jeopardy. Here is what many of you don’t understand … to stimulate the economy you need to stimulate what runs the economy. Had suddenly government mandated to me that I didn’t need to pay taxes, guess what? Instead of depositing that $288,000 into the Washington black-hole, I would have spent it, hired more employees, and generated substantial economic growth. My employees would have enjoyed the wealth of that tax cut in the form of promotions and better salaries. But you can forget it now.

When you have a comatose man on the verge of death, you don’t defibrillate and shock his thumb thinking that will bring him back to life, do you? Or, do you defibrillate his heart? Business is at the heart of America and always has been. To restart it, you must stimulate it, not kill it. Suddenly, the power brokers in washington believe the poor of America are the essential drivers of the American economic engine. Nothing could be further from the truth and this is the type of change you can keep. So where am I going with all this? It’s quite simple.

If any new taxes are levied on me, or my company, my reaction will be swift and simple. I’ll fire you. I’ll fire your co-workers. You can then plead with the government to pay for your mortgage, your SUV, and your child’s future. Frankly, it isn’t my problem any more.

Then, I will close this company down, move to another country, and retire. You see, I’m done. I’m done with a country that penalizes the productive and gives to the unproductive. My motivation to work and to provide jobs will be destroyed, and with it, will be my citizenship.

So, if you lose your job, it won’t be at the hands of the economy; it will be at the hands of a political hurricane that swept through this country, steamrolled the constitution, and will have changed its landscape forever. If that happens, you can find me sitting on a beach, retired, and with no employees to worry about….

Signed, THE BOSS

PHP: How to Redirect Server-Side (Code 301 to 307)

April 23rd, 2011

I needed a way to do a server-side PHP redirect based on the RFC 2616 spec. After some research and some help on the net, I put together this:

Code:
function redirect($to,$code=307) {
		$location = null;
		$sn = $_SERVER['SCRIPT_NAME'];
		$cp = dirname($sn);
		if (substr($to,0,4)=='http') $location = $to; // Absolute URL
		else {
			$schema = $_SERVER['SERVER_PORT']=='443'?'https':'http';
			$host = strlen($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST']:$_SERVER['SERVER_NAME'];
			if (substr($to,0,1)=='/') $location = "$schema://$host$to";
			elseif (substr($to,0,1)=='.') {
				$location = "$schema://$host/";
				$pu = parse_url($to);
				$cd = dirname($_SERVER['SCRIPT_FILENAME']).'/';
				$np = realpath($cd.$pu['path']);
				$np = str_replace($_SERVER['DOCUMENT_ROOT'],'',$np);
				$location.= $np;
				if ((isset($pu['query'])) && (strlen($pu['query'])>0)) $location.= '?'.$pu['query'];
			}
		}

		$hs = headers_sent();
		if ($hs==false) {
			if ($code==301) header("301 Moved Permanently HTTP/1.1"); // Convert to GET
			elseif ($code==302) header("302 Found HTTP/1.1"); // Conform re-POST
			elseif ($code==303) header("303 See Other HTTP/1.1"); // dont cache, always use GET
			elseif ($code==304) header("304 Not Modified HTTP/1.1"); // use cache
			elseif ($code==305) header("305 Use Proxy HTTP/1.1");
			elseif ($code==306) header("306 Not Used HTTP/1.1");
			elseif ($code==307) header("307 Temorary Redirect HTTP/1.1");
			else trigger_error("Unhandled redirect() HTTP Code: $code",E_USER_ERROR);
			header("Location: $location");
			header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
		}
		elseif (($hs==true) || ($code==302) || ($code==303)) {
			// todo: draw some javascript to redirect
			$cover_div_style = 'background-color: #ccc; height: 100%; left: 0px; position: absolute; top: 0px; width: 100%;';
			echo "<div style='$cover_div_style'>\n";
			$link_div_style = 'background-color: #fff; border: 2px solid #f00; left: 0px; margin: 5px; padding: 3px; ';
			$link_div_style.= 'position: absolute; text-align: center; top: 0px; width: 95%; z-index: 99;';
			echo "<div style='$link_div_style'>\n";
			echo "<p>Please See: <a href='$to'>".htmlspecialchars($location)."</a></p>\n";
			echo "</div>\n</div>\n";
		}
		exit(0);
	}

PHP: Functions to Check if String Starts & Ends With String

April 23rd, 2011

Another example of functions that aren’t part of PHP, but should be. Luckily there was a good solution on StackOverflow:

Code:
function startsWith($haystack,$needle,$case=true) {
    if($case){return (strcmp(substr($haystack, 0, strlen($needle)),$needle)===0);}
    return (strcasecmp(substr($haystack, 0, strlen($needle)),$needle)===0);
}

function endsWith($haystack,$needle,$case=true) {
    if($case){return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)),$needle)===0);}
    return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)),$needle)===0);
}

PHP: Splitting an Array into Multiple Arrays

April 23rd, 2011

I had an array that I needed to split into multiple arrays, surprised there wasn’t a built in function for this, here it is:

Code:
function array_split($array, $pieces=2) {
	if ($pieces < 2) {
		return array($array);
	}
	$newCount = ceil(count($array)/$pieces);
	$a = array_slice($array, 0, $newCount);
	$b = $this->array_split(array_slice($array, $newCount), $pieces-1);
	return array_merge(array($a),$b);
}

Loving Netflix Canada..

April 22nd, 2011

I’ve become a big fan of Netflix Canada, I know the inventory isn’t that great and a lot of the stuff is old, but there are some real gems on there. I go to NewNetflix.ca pretty much daily to see if anything new has come up. Some of my favorites include:

  • Damages (TV Series)
  • The Tudors (TV Series)
  • Mad Men (TV Series)
  • King of Kong (Documentary)
  • .. Too many documentaries to list ..
  • Top Gear (TV Series)
  • .. Too many comedy specials to list ..

For $8 a month, how can you go wrong? A warning: be careful your internet bandwidth!

Using PHP to Do [Some kind] of Spelling Suggest (“Did you mean”) [PHP]

February 22nd, 2011

I wanted to figure out a way to have a “Did you mean” spell check for search terms that didn’t return any results. By default the .dll is not available in XAMPP, so need to:

1. Install Pspell (full install)
2. Install the English dictionary on the same page
3. Copy over aspell-15.dll from C:\Program Files\Aspell\bin\ to C:\xampp\apache\bin\
4. Enable extension=php_pspell.dll in php.ini

In Debain it’s simple:

Code:
apt-get install php5-pspell

Now to figure out some good code.. I’ll update it here if I come up with anything interesting

Update

Using pspell suggest alone doesn’t really give the best results. It’s smarter to calculate the metaphone string of the word and then compare that against the suggested results. Also, you’ll need to create your own dictionary so it doesn’t suggest a correct word. This is basically the function I made to do that (I have a cron job to update the dictionary nightly):

Code:
function addString($string) {
	global $pspell_link;
	$exp = explode(" ",$string);
	foreach ($exp as $e) {
		if (strpos($e,'-') === false && $e != '&') {
			pspell_add_to_personal($pspell_link, rtrim($e,","));
		}
	}
}

Here is the code that does the suggest, I’ve just ripped it out of a class I made, but it shows 95% of it:

Code:
		$result = array();
		$wordExp = explode(' ',$term);
		$i = 0;
		foreach ($wordExp as $w) {
			$wordMeta = metaphone($w);
			$newWord = '';
			if (!pspell_check($this->pspell, $w)) {
				//
				$suggest = pspell_suggest($this->pspell, $w);
				//$util->out($suggest);
				// loop all suggestions looking for the first metaphone match
				foreach ($suggest as $s) {
					// returns back case-sensative results
					if (strtolower($s) != strtolower($w)) {
						// also returns back words with spaces and dashes
						if (metaphone($s) == $wordMeta && strpos($s,' ') === false && strpos($s,'-') === false) {
							$newWord = $s;
							break;
						}
					}
				}
				// if no metaphone match
				if ($newWord == '') {
					foreach ($suggest as $s) {
						// just grab the first result with no space or dash
						if (strpos($s,' ') === false && strpos($s,'-') === false) {
							$newWord = $s;
							break;
						}

					}
					// if there is still no new word, just return the original
					if ($newWord == '') {
						$newWord = $w;
					}
				}
			} else {
				$newWord = $w;
			}
			//
			$result[$i][0] = $w;
			$result[$i][1] = strtolower($newWord);
			$i = $i + 1;
		}
		return $result;

So the function would return back and array with the original word and the suggested word:

Code:
Array
(
    [0] => Array
        (
            [0] => cranberrry
            [1] => cranberry
        )

)

How to Properly Remove Special Characters a String [PHP]

February 15th, 2011

After getting ridiculously frustrated with all the confusing regex out there for stripping special characters from a string, I thought: “there must be a PHP way of doing this” – and then it clicked in -

Code:
filter_var($string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH)

That is all.

The pain of sending email

January 21st, 2011

Last night I decided to finally try and understand why some of the emails I was sending was getting caught in spam filters, particularly Yahoo and Hotmail. So it began.

#0 Read this. Get the basics.

#1 To test everything out, send yourself an email to a GMail account. When viewing the message, click the drop down and click “View Original”. From there you will be able to see the return-path and comments on the SPF.

#2

Make sure the return-path is set correctly in your code. This return-path will be used to do the reverse ptr lookup. In PHP Pear Mail that meant:

Code:
$mail =& Mail::factory('mail', array('Return-Path' => sprintf("-f %s", '[email protected]')));

#2

Read this. You need to request your hosting provider, in my case ServerBeach, to update your IPs SPF record.

Then the txt spf record needs to be updated to allow permit the server IP:

Code:
v=spf1 ip4:11.11.111.11 include:_spf.google.com ~all

#3

Setting up DKIM is hard. Read this. I’m using Exim4 and by default DKIM support is built in. To do it read this. But you’ll quickly realize that won’t work on etch! So you need to upgrade to Lenny and then make sure the lenny backports are on, because you need to go to exim 4.7+

Code:
# Main
deb http://http.us.debian.org/debian/ stable main non-free contrib
# Source
deb-src http://http.us.debian.org/debian/ stable main non-free contrib
# Security
deb http://security.debian.org/ stable/updates main contrib non-free
# backports for lenny
deb http://backports.debian.org/debian-backports lenny-backports main

So now install the backports version of exim after you’ve upgraded Debian to Lenny:

Code:
apt-get -t lenny-backports install exim4

Basically create the keys in the exim config directories /etc/exim4/:

Code:
openssl genrsa -out dkim.private.key 768
openssl rsa -in dkim.private.key -out dkim.public.key -pubout -outform PEM

Then update the file exim4.conf.template:

Make sure the dkim_private_key is to a fully qualified path

driver = smtp
dkim_domain = example.com
dkim_selector = x
dkim_private_key = /etc/exim4/dkim.private.key
dkim_canon = relaxed

Then update DNS with the txt entry and it should work. Just remember George you can’t do this in etch, you need to be in lenny and using exim > 4.70.

The pain of a new server setup

December 30th, 2010

Setup a new server on SB etchy, true to form, the pain of setting up the server ensues..

For some reason /etc/apt/sources.list is off, by default had:

Code:
# Main
deb http://http.us.debian.org/debian/ etch main non-free contrib
# Source
deb-src http://http.us.debian.org/debian/ etch main non-free contrib
# Security
deb http://security.debian.org/ etch/updates main contrib non-free

Changed it to:

Code:
# Main
deb http://http.us.debian.org/debian/ stable main non-free contrib
# Source
deb-src http://http.us.debian.org/debian/ stable main non-free contrib
# Security
deb http://security.debian.org/ stable/updates main contrib non-free

Installed apt-get install apache2, but didn’t know how to check that worker was installed by default:

Code:
apache2 -l
Compiled in modules:
  core.c
  mod_log_config.c
  mod_logio.c
  worker.c
  http_core.c
  mod_so.c

apt-get install php5 libapache2-mod-php5

And then I realized PHP should not run with worker in a production env:

62.1. Why shouldn’t I use Apache2 with a threaded MPM in a production environment?

PHP is glue. It is the glue used to build cool web applications by sticking dozens of 3rd-party libraries together and making it all appear as one coherent entity through an intuitive and easy to learn language interface. The flexibility and power of PHP relies on the stability and robustness of the underlying platform. It needs a working OS, a working web server and working 3rd-party libraries to glue together. When any of these stop working PHP needs ways to identify the problems and fix them quickly. When you make the underlying framework more complex by not having completely separate execution threads, completely separate memory segments and a strong sandbox for each request to play in, feet of clay are introduced into PHP’s system.

If you feel you have to use a threaded MPM, look at a FastCGI configuration where PHP is running in its own memory space.

And finally, this warning against using a threaded MPM is not as strong for Windows systems because most libraries on that platform tend to be threadsafe.

Setup Memcaches: http://www.sematopia.com/2009/03/setting-up-memcached-on-ubuntudebian-and-windows-xampp/

Disable Etags:

Code:
sudo a2enmod headers
sudo /etc/init.d/apache2 restart

Header unset ETag
FileETag None

Install Subversion:

Code:
apt-get install subversion
apt-get install php-pear

Intall APC:

Code:
apt-get install apache2-dev php5-dev php-pear make
ln -s /usr/bin/apxs2 /usr/bin/apxs
pecl install apc

Install Mysql:

Code:
apt-get install mysql-client mysql-common mysql-server php5-mysql

Add to apache2.conf:

Code:
AliasMatch \.svn /non-existant-page
Options -Indexes

Header unset ETag
FileETag None

<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
Header set Expires "Thu, 15 Apr 2015 20:00:00 GMT"
</FilesMatch>

Enable mod-rewrite

Code:
a2enmod rewrite

Install SSL:

Code:
apt-get install openssl ssl-cert

Configure Exim4 (cause by default does not support external mail sending)

Code:
dpkg-reconfigure exim4-config

Change the time zone to est:

Code:
dpkg-reconfigure tzdata

Add spelling:

Code:
apt-get install php5-pspell
apt-get install aspell-en

Koumbaro

May 1st, 2010

65703118

Where Tumblr Comes In

May 1st, 2010

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

Twitter: How To (Auto) Gain Followers & Influence Others (No One)

April 10th, 2010

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:

Code:
$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:

Code:
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.

Code:
<?

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->[email protected]/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->[email protected]/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:

Code:
<?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

  • Twitter has a limit on the number of API calls you can execute based on a user name and IP address. So don’t run this a) too often and b) on your personal computer. You’ll need to run this on a separate box with a separate IP address.
  • You’ll need to automate this with a cron job. I run it every 10 minutes. If your hosting company doesn’t allow for cron jobs, you can use WebBasedCron, which I made 4-5 years ago.
  • The code is released under GPL. Use at your own risk. You can download the files here.

A Comment on the Yen

March 31st, 2010
"At this point, it is worth noting a small bit of history. We began our career trading foreign exchange back in the 70's for what was then NCNB in Charlotte, N. Carolina, and we can recall well trading the Yen/dollar at 165 and higher. Indeed, we can recall not too long before that when the Yen/dollar rate had been centred upon 225. Thus, with that past history it is not difficult for us to envision the Yen/dollar rate moving readily to "Par" when it may well be quite difficult for others to even imagine that possible. It is to our advantage then to recall that history, and it is further to our advantage to understand that something truly tectonic in nature is taking place in the Yen/dollar and Yen/Non-US dollar relationships. Henceforth we shall try our best not to be concerned about 25 and 50 "pips" in the Yen/dollar relationship, for we envision hundreds upon hundreds of "pips" as the trade unfolds… multiples of "big figures" passing by over the course of the next several months and years." – D. Gartman (Mar 31/2010)

Mrs Watanabe Likes….

March 26th, 2010
"Of particular interest, Mrs Watanabe likes Brazilian bonds. Holdings of Brazil fixed income instruments stood at JPY1.34 trillion in February, nearly a third more than last February. Consider that a Brazilian real money market pays her 6% a year, 55 times more than she can get if she takes advantage of the government’s relaxation of the deposit limit. Some reports also suggest keen interest in Chinese equities, but Japanese investors have a clear preference for bonds over equity, according to Ministry of Finance capital account data." – Marc Chandler

Think About It This Way

March 25th, 2010

“‘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

If I Could Go Back..

March 7th, 2010
..and change one thing in my life, I would have lived with my sister in University.

PHP: Removing All Non-Printable (Special Characters) From String

February 12th, 2010

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:

Code:
$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.

Code:
$str = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $str); 

A Comment on Greece from June ’93

February 2nd, 2010

“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.”

Apache: Disabling ETags to Improve Performance

January 30th, 2010

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:

Code:
a2enmod headers

Then, within your apache2.conf file, add the following:

Code:
Header unset ETag
FileETag None

PHP: Remove New Lines From a String

January 19th, 2010

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:

Code:
$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:

Code:
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;

}

A Christmas Note from Ben Stein

December 6th, 2009

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.

A Toronto Rainbow

December 3rd, 2009

A serious rainbow