Author Archives: mike

Generator Labs: New Flexible Pay-Per-Check Pricing Model

The Generator Labs system is an automated service that scans over 60 RBL’s, 20 URIBL’s, and the Google Safe Browsing database, to see if any of your IP addresses and website URL’s are currently blocked.

As of Sept 1st,, Generator Labs is offering a pay-per-check pricing model through its new Ultimate package. This new package lets you add as many hosts to your account as you like, and rather than paying for a certain number of hosts, you pay a low, scaled price per-check.

How does this work?

Per-Check pricing means that you can monitor an unlimited number of hosts, by simply adding credits on your account towards checks. When the credit on your account runs out, the checks stop- it’s that simple.

The cost-per-check scales with the number of hosts on your account. The current per-check pricing sheet is as follows:

  • Between 0 and 199 hosts, the cost-per-check is $0.0050/check
  • Between 200 and 499 hosts, the cost-per-check is $0.0048/check
  • Between 500 and 999 hosts, the cost-per-check is $0.0045/check
  • 1000 hosts and up, the cost-per-check is $0.0040/check

Put as much or as little credit on your account as you like- it will never expire, and we’ll let you know when your credit is running out.

How many hosts can I really add?

Really, you can add as many as you’d like! We’ve already had a customer that added over 6000 hosts to their account!

What else comes with the Ultimate package?

The Ultimate package includes all the same features of our Enterprise package, which includes multiple checks per day, SMS notifications, and much more. See the pricing page for all the package details.

Click here to sign up for free and get started today!

Google Speech API – Full Duplex PHP Version

So this is a follow up to my post a while ago, talking about how to use the Google Speech Recognition API built in to Google Chrome.

Since my last post, Chrome has had some significant upgrades to this feature- specifically around the length of audio you can pass to the API. The old version would only let you pass very short clips (only a few seconds), but the new API is a full-duplex streaming API. What this means, is that it actually uses two HTTP connections- one POST request to upload the content as a “live” chunked stream, and a second GET request to access the results, which makes much more sense for longer audio samples, or for streaming audio.

I created a simple PHP class to access this API; while this likely won’t make sense for anybody that wants to do a real-time stream, it should satisfy most cases where people just want to send “longer” audio clips.

Before you can use this PHP class, you must get a developer API key from Google. The class does not include one, and I cannot give you one- they’re free, and easy to get just go to the Google APIs site, and sign up for one.

Then download the class below, and start with a simple example:

<? 
require 'google_speech.php';

$s = new cgoogle_speech('put your API key here'); 

$output = $s->process('@test.flac', 'en-US', 8000);      

print_r($output);
?>

Audio can be passed as a filename (by prefixing the ‘@’ sign in front of the file name), or by passing in raw FLAC content. The second argument is an IETF language tag. I’ve only been able to test with both English and French, but I assume others work. It defaults to ‘en-US’. The third argument is sample rate, it defaults to 8000.

** Your sample rate must match your file- if it doesn’t, you’ll either get nothing returned, or you’ll get a really bad transcription. **

The output will return as an array, and should look something like this:

Array
(
    [0] => Array
        (
            [alternative] => Array
                (
                    [0] => Array
                        (
                            [transcript] => my CPU is a neural net processor a learning computer
                            [confidence] => 0.74177068
                        )
                    [1] => Array
                        (
                            [transcript] => my CPU is the neuron that process of learning
                        )
                    [2] => Array
                        (
                            [transcript] => my CPU is the neural net processor a learning
                        )
                    [3] => Array
                        (
                            [transcript] => my CPU is the neuron that process a balloon
                        )
                    [4] => Array
                        (
                            [transcript] => my CPU is the neural net processor a living
                        )
                )
            [final] => 1
        )
)

Get the PHP class here: http://mikepultz.com/uploads/google_speech.php.zip

Net_DNS2 Version 1.3.1 Released

I’ve released version 1.3.1 of the PEAR Net_DNS2 library- you can install it now through the command line PEAR installer:

pear install Net_DNS2

Download it directly from the Google Code page here.

Or, you can also add it to your project using composer.

Version 1.3.1

  • added the Net_DNS2_Packet_Request and Net_DNS2_Packet_Response objects to the Net_DNS2_Exception object
  • added support in the TSIG class for SHA algorithms (requires the hash extension, which is included in PHP >= 5.1.2), patch provided by Manuel Mausz
  • added support for the NID, L32, L64, and LP DNS RR’s (RFC6742)
  • lots of phpcs cleanup

Mining Twitter API v1.1 Streams from PHP – with OAuth

This is a quick update to my post about a year ago, with details on how to mine Twitter streams in real-time using PHP. This new code includes updates for the v1.1 API, including authentication using OAuth.

The first thing you need to do is sign in to the Twitter developer portal with your Twitter account here: https://dev.twitter.com/user/login 

Once you’ve logged in, click on your profile icon in the top right hand corner, select
“My applications”, and create a new application if you don’t already have one.

Select the option to create the access token as well, as the requests need to be signed by a Twitter account.

The Code

ctwitter_stream.php

class ctwitter_stream
{
    private $m_oauth_consumer_key;
    private $m_oauth_consumer_secret;
    private $m_oauth_token;
    private $m_oauth_token_secret;

    private $m_oauth_nonce;
    private $m_oauth_signature;
    private $m_oauth_signature_method = 'HMAC-SHA1';
    private $m_oauth_timestamp;
    private $m_oauth_version = '1.0';

    public function __construct()
    {
        //
        // set a time limit to unlimited
        //
        set_time_limit(0);
    }

    //
    // set the login details
    //
    public function login($_consumer_key, $_consumer_secret, $_token, $_token_secret)
    {
        $this->m_oauth_consumer_key     = $_consumer_key;
        $this->m_oauth_consumer_secret  = $_consumer_secret;
        $this->m_oauth_token            = $_token;
        $this->m_oauth_token_secret     = $_token_secret;

        //
        // generate a nonce; we're just using a random md5() hash here.
        //
        $this->m_oauth_nonce = md5(mt_rand());

        return true;
    }

    //
    // process a tweet object from the stream
    //
    private function process_tweet(array $_data)
    {
        print_r($_data);

        return true;
    }

    //
    // the main stream manager
    //
    public function start(array $_keywords)
    {
        while(1)
        {
            $fp = fsockopen("ssl://stream.twitter.com", 443, $errno, $errstr, 30);
            if (!$fp)
            {
                echo "ERROR: Twitter Stream Error: failed to open socket";
            } else
            {
                //
                // build the data and store it so we can get a length
                //
                $data = 'track=' . rawurlencode(implode($_keywords, ','));

                //
                // store the current timestamp
                //
                $this->m_oauth_timestamp = time();

                //
                // generate the base string based on all the data
                //
                $base_string = 'POST&' . 
                    rawurlencode('https://stream.twitter.com/1.1/statuses/filter.json') . '&' .
                    rawurlencode('oauth_consumer_key=' . $this->m_oauth_consumer_key . '&' .
                        'oauth_nonce=' . $this->m_oauth_nonce . '&' .
                        'oauth_signature_method=' . $this->m_oauth_signature_method . '&' . 
                        'oauth_timestamp=' . $this->m_oauth_timestamp . '&' .
                        'oauth_token=' . $this->m_oauth_token . '&' .
                        'oauth_version=' . $this->m_oauth_version . '&' .
                        $data);

                //
                // generate the secret key to use to hash
                //
                $secret = rawurlencode($this->m_oauth_consumer_secret) . '&' . 
                    rawurlencode($this->m_oauth_token_secret);

                //
                // generate the signature using HMAC-SHA1
                //
                // hash_hmac() requires PHP >= 5.1.2 or PECL hash >= 1.1
                //
                $raw_hash = hash_hmac('sha1', $base_string, $secret, true);

                //
                // base64 then urlencode the raw hash
                //
                $this->m_oauth_signature = rawurlencode(base64_encode($raw_hash));

                //
                // build the OAuth Authorization header
                //
                $oauth = 'OAuth oauth_consumer_key="' . $this->m_oauth_consumer_key . '", ' .
                        'oauth_nonce="' . $this->m_oauth_nonce . '", ' .
                        'oauth_signature="' . $this->m_oauth_signature . '", ' .
                        'oauth_signature_method="' . $this->m_oauth_signature_method . '", ' .
                        'oauth_timestamp="' . $this->m_oauth_timestamp . '", ' .
                        'oauth_token="' . $this->m_oauth_token . '", ' .
                        'oauth_version="' . $this->m_oauth_version . '"';

                //
                // build the request
                //
                $request  = "POST /1.1/statuses/filter.json HTTP/1.1\r\n";
                $request .= "Host: stream.twitter.com\r\n";
                $request .= "Authorization: " . $oauth . "\r\n";
                $request .= "Content-Length: " . strlen($data) . "\r\n";
                $request .= "Content-Type: application/x-www-form-urlencoded\r\n\r\n";
                $request .= $data;

                //
                // write the request
                //
                fwrite($fp, $request);

                //
                // set it to non-blocking
                //
                stream_set_blocking($fp, 0);

                while(!feof($fp))
                {
                    $read   = array($fp);
                    $write  = null;
                    $except = null;

                    //
                    // select, waiting up to 10 minutes for a tweet; if we don't get one, then
                    // then reconnect, because it's possible something went wrong.
                    //
                    $res = stream_select($read, $write, $except, 600, 0);
                    if ( ($res == false) || ($res == 0) )
                    {
                        break;
                    }

                    //
                    // read the JSON object from the socket
                    //
                    $json = fgets($fp);

                    //
                    // look for a HTTP response code
                    //
                    if (strncmp($json, 'HTTP/1.1', 8) == 0)
                    {
                        $json = trim($json);
                        if ($json != 'HTTP/1.1 200 OK')
                        {
                            echo 'ERROR: ' . $json . "\n";
                            return false;
                        }
                    }

                    //
                    // if there is some data, then process it
                    //
                    if ( ($json !== false) && (strlen($json) > 0) )
                    {
                        //
                        // decode the socket to a PHP array
                        //
                        $data = json_decode($json, true);
                        if ($data)
                        {
                            //
                            // process it
                            //
                            $this->process_tweet($data);
                        }
                    }
                }
            }

            fclose($fp);
            sleep(10);
        }

        return;
    }
};

The “process_tweet()” method will be called for each matching tweet- just modify that method to process the tweet however you want (load it into a database, print it to screen, email it, etc). The keyword matching isn’t perfect- if you search for a string of words, it won’t necessarily match the words in that exact order, but you can check that yourself from the process_tweet() method.

Then create a simple PHP application to run the collector:

require 'ctwitter_stream.php';

$t = new ctwitter_stream();

$t->login('consumer_key', 'consumer secret', 'access token', 'access secret');

$t->start(array('facebook', 'fbook', 'fb'));

You’ll need to provide the Consumer Key, Consumer Secret, Access Token, and the Access Secret, all of which are available from the Details section of your Application.

This new class uses the PHP hash_hmac() function for OAuth, which is available only in PHP 5.2.1 and up, and in the PECL hash extension 1.1 and up.

You can also Download the file here: http://mikepultz.com/uploads/ctwitter_stream.php.zip

The Decline of Customer Service – and How It’s Mostly the Consumers Fault

I recently wrote an article about my experiences dealing with Wind Mobile’s Customer Service, while I was trying to get a cell phone set up for my brother.

In the end, Wind admitted that they couldn’t actually provide service for my brother in his area, but rather than refunding his money, they offered him a $100 credit, but only if he remained a customer… which we obviously declined.

After writing the article, we received thousands of supportive messages from people all over the world, through Facebook, Twitter, Reddit, email, and comments on my article. I was blown away by the sheer magnitude of the response from the masses- I couldn’t be more grateful.

But I did notice two overwhelming themes in the messages I received, and both point to a very sad state for Customer Service.

The first type: “I know how you feel, the same thing happened to me.”

The “misery loves company” type; I received message after message from other Wind customers (sometimes they weren’t even Wind customers) about how they were mistreated in a similar fashion- and sometimes the only common element in our two stories, was that we both felt ripped off.

As a consumer, we felt like the company took advantage of us, or wasn’t fair, and we just had to accept that and move on- we had no recourse.

The second type: “You should have known better”

This was actually a big surprise to me. I’m a pretty pessimistic person as it is, but I was blown away by the number of people that blamed me, and said “that I should have known better than to trust Wind”, or “why would you believe what they told you?”

I believe that consumers should be smart about their purchases; they should do their homework and understand what they’re getting in to when they sign something. But I also think that companies have a responsibility to be honest about their products and services and own up to it when they can’t or won’t deliver.

In my case, I had Wind Mobile insisting over the course of a month, that the cell coverage in my brothers area was “good, and definitely not the problem”, only to find out in the end, that it’s NOT good, and WAS definitely the problem.

Even with their lies, or at best, completely uninformed (intentionally or otherwise) staff, I had people saying that I should have known better than to believe them.

Is that really where we are? Consumers being trained to not trust anything that comes out of a company representative’s mouth, and if we fall for it, it’s our own fault for believing them?

Good Customer Service shouldn’t be a “niche” market for smaller guys to distinguish themselves.

Not a huge surprise, but the bigger companies are the worst offenders. It doesn’t really affect them as much if they lose a customer here or there- it’s the CODB, and in a lot of cases, the customer needs the service more than the company needs the customer. So consumers feel trapped- and simply put up with whatever the company tries to get away with.

The reality is, that if enough customers refuse to accept what they’re handing out; choose not to accept their excessive pricing, their arbitrary rules, and their poor customer service, we could make a difference; we could force them to change and put honesty before profits- or at least, “more’ of the profits towards being honest and fair.

… but we won’t. Most people, after years of putting up with it, don’t believe they can actually make a difference, so they become complacent and apathetic, and sometimes, in an ironic, Stockholm syndrome-like twist, evangelists of the shitty things companies do, triggering them to write comments about how people should have known better.

This type of thinking moves a large portion of blame on the consumer, as it tells these companies that’s it’s “OK”- we’ve developed a coping mechanism to deal with their recidivism, and they’re more than welcome to ratchet up the shit they want us to eat.