Twirc / TwittServ

Twitter IRC bots:

Twirc

Twirc is a Twitter client written in PHP and using IRC (Freenode) to connect. Due to flooding policies, it actually serves only one client.

You can add this in your crontab:

*/2 * * * * [ `/usr/bin/pgrep -cfx "/usr/bin/php /SCRIPT_FILENAME"` -eq 0 ] && /usr/bin/php /SCRIPT_FILENAME

Just replace SCRIPT_FILENAME by the full path to the script...

Note that you'll have to create an app in your Twitter account: the app must have read and update permissions. No callback URL is needed, you must use the PIN method for authentification (mobile / desktop apps).

Source code

<?php

/*

Get the twitteroauth lib here : http://github.com/abraham/twitteroauth

To avoid bugs, I recommend to replace "return json_decode($response)" in methods "get", "post" and "delete" by :

    return @json_decode(trim(preg_replace('#((id)|(cursor))":(\d+)#','\1":"\4"',$response)));

Also replace "$http_status" by "$http_code" in method "lastStatusCode()"

*/

require_once dirname(__FILE__) . '/OAuth.php';
require_once 
dirname(__FILE__) . '/twitteroauth.php';

class 
Twirc {
    const 
DEBUG false;
    const 
NICK 'CHOOSE_A_NICKNAME_FOR_YOUR_BOT';
    const 
PASSWORD ''// Optionnal, set the nickserv password if needed
    
const TWITTER_INTERVAL 60// in seconds
    
const TWITTER_UNDO_DELAY 60// in seconds
    
const EXPAND_URLS true;

    
// You need to register a new app here: https://twitter.com/apps/new

    
const CONSUMER_KEY '';
    const 
CONSUMER_SECRET '';

    const 
SHARED_SECRET ''// Optionnal, if set you'll have to enter this to connect

    // OK, done ! No need to change anything else

    
protected $conn null;
    protected 
$backupFile '';
    protected 
$twitterStatus false;
    protected 
$twitterLastId '';
    protected 
$twitterLastChecked 0;
    protected 
$twitterLastUpdated 0;
    protected 
$twitterLastMentionId '';
    protected 
$twitterLastDM '';
    protected 
$twitterUndo '';
    protected 
$owner '';
    protected 
$ownerNick '';
    protected 
$ownerScreenName '';
    protected 
$token null;
    protected 
$oAuth null;
    protected 
$candidate '';

    function 
__construct() {
        if (
self::EXPAND_URLSini_set('user_agent','Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4');
        
$this->backupFile sprintf('%s.data.inc',__FILE__);
        
$this->restore_data();
        
$this->run();
    }

    protected function 
output($msg,$forceOutput=false) {
        if (
self::DEBUG || $forceOutput) echo "$msg\n";
    }

    protected function 
restore_data() {
        if (
file_exists($this->backupFile)) {
            list(
$this->twitterLastId,$this->twitterLastMentionId,$this->twitterLastDM,$this->ownerNick,$this->token,$this->ownerScreenName,$this->owner) = @unserialize(file_get_contents($this->backupFile));

            if (
$this->token) {
                
$this->oAuth = new TwitterOAuth(self::CONSUMER_KEY,self::CONSUMER_SECRET,$this->token['oauth_token'],$this->token['oauth_token_secret']);
            }
        }
    }

    protected function 
backup_data() {
        
file_put_contents($this->backupFile,serialize(array($this->twitterLastId,$this->twitterLastMentionId,$this->twitterLastDM,$this->ownerNick,$this->token,$this->ownerScreenName,$this->owner)));
    }

    protected function 
read() {
        if (
$this->conn) {
            
$line trim(fgets($this->conn));
            if (
$line$this->output($line);
            else 
sleep(1);
            return 
$line;
        }
        return 
false;
    }

    protected function 
write($cmd) {
        if (
$this->conn) {
            
$this->output($cmd);
            
fputs($this->conn,"$cmd\r\n");
        }
    }

    protected function 
expand_urls($text) {
        
$text html_entity_decode($text,ENT_QUOTES,'UTF-8');
        if (
self::EXPAND_URLS) {
            return 
preg_replace_callback('#https?://[^ <>"()\'\[\]]+#si',create_function('$m','$url = $m[0]; $h = @array_reverse(get_headers($url)); if (@count($h)) foreach ($h as $header) if (preg_match("#^Location: (.*)$#si",$header,$matches)) { $url = trim($matches[1]); break; } return $url;'),$text);
        }
        return 
$text;
    }

    protected function 
twitter_update($text) {
        if (!
$this->ownerScreenName) return false;
        
$text mb_check_encoding($text,'UTF-8') ? $text utf8_encode($text);
        
$json $this->oAuth->post('statuses/update',array('status'=>$text));
        
$i $this->oAuth->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error updating status: %s',$this->ownerNick,(string) $json->error));
            else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error updating status ($i)");
        } elseif (!@
$json->id$this->write("PRIVMSG $this->ownerNick :Error sending status: Twitter API not responding...");
        else {
            
$this->twitterUndo $json->id;
            
$this->twitterLastUpdated time();
        }
    }

    protected function 
twitter_check() {
        if (
$this->twitterUndo && (time() - $this->twitterLastUpdated self::TWITTER_UNDO_DELAY)) {
            
$this->twitterUndo '';
        }
        if (!
$this->ownerScreenName) return false;
        if (
$this->twitterStatus && (time() - $this->twitterLastChecked >= self::TWITTER_INTERVAL)) {
            
$this->twitterLastChecked time();
            
$json $this->oAuth->get('statuses/home_timeline',$this->twitterLastId ? array('since_id'=>$this->twitterLastId) : array());
            
$i $this->oAuth->lastStatusCode();
            if (
self::DEBUG) {
                
print_r($json);
            }
            if (
$i != 200) {
                if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error retrieving timeline: %s',$this->ownerNick,(string) $json->error));
                else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error retrieving timeline ($i)");
            } else {
                
$json array_reverse($json);
                foreach (
$json as $k=>$s) {
                    
$this->twitterLastId = (string) $s->id;
                    
$screenName = (string) $s->user->screen_name;
                    if (
$screenName != $this->ownerScreenName) {
                        
$text $this->expand_urls((string) $s->text);
                        
$this->write("PRIVMSG $this->ownerNick :<$screenName$text");
                        
sleep(1);
                    }
                }
                
$this->backup_data();
            }
        }
    }

    protected function 
twitter_mentions() {
        if (!
$this->ownerScreenName) return false;
        
$json $this->oAuth->get('statuses/mentions',$this->twitterLastMentionId ? array('since_id'=>$this->twitterLastMentionId) : array());
        
$i $this->oAuth->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error retrieving mentions: %s',$this->ownerNick,(string) $json->error));
            else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error retrieving mentions ($i)");
        } else {
            
$json array_reverse($json);
            foreach (
$json as $k=>$s) {
                
$this->twitterLastMentionId = (string) $s->id;
                
$screenName = (string) $s->user->screen_name;
                
$text $this->expand_urls((string) $s->text);
                
$this->write("PRIVMSG $this->ownerNick :[Mention] <$screenName$text");
                
sleep(1);
            }
            
$this->backup_data();
        }
    }

    protected function 
twitter_dm($to=null,$text=null) {
        if (!
$this->ownerScreenName) return false;
        if (
$to && $text) {
            
$text mb_check_encoding($text,'UTF-8') ? $text utf8_encode($text);
            
$json $this->oAuth->post('direct_messages/new',array('screen_name'=>$to,'text'=>$text));
            
$i $this->oAuth->lastStatusCode();
            if (
self::DEBUG) {
                
print_r($json);
            }
            if (
$i != 200) {
                if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error sending direct message: %s',$this->ownerNick,(string) $json->error));
                else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error sending direct message ($i)");
            } elseif (!@
$json->id$this->write("PRIVMSG $this->ownerNick :Error sending direct message: Twitter API not responding...");
        } else {
            
$json $this->oAuth->get('direct_messages',$this->twitterLastDM ? array('since_id'=>$this->twitterLastDM) : array());
            
$i $this->oAuth->lastStatusCode();
            if (
self::DEBUG) {
                
print_r($json);
            }
            if (
$i != 200) {
                if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error retrieving direct messages: %s',$this->ownerNick,(string) $json->error));
                else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error retrieving direct messages ($i)");
            } else {
                
$json array_reverse($json);
                foreach (
$json as $k=>$s) {
                    
$this->twitterLastDM = (string) $s->id;
                    
$screenName = (string) $s->sender->screen_name;
                    
$text $this->expand_urls((string) $s->text);
                    
$this->write("PRIVMSG $this->ownerNick :[DM] <$screenName$text");
                    
sleep(1);
                }
                
$this->backup_data();
            }
        }
    }

    protected function 
twitter_timeline($user) {
        if (!
$this->ownerScreenName) return false;
        
$json $this->oAuth->get('statuses/user_timeline',array('screen_name'=>$user));
        
$i $this->oAuth->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error retrieving user timeline: %s',$this->ownerNick,(string) $json->error));
            else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error retrieving user timeline ($i)");
        } else {
            
$json array_reverse($json);
            foreach (
$json as $k=>$s) {
                
$screenName = (string) $s->user->screen_name;
                
$text $this->expand_urls((string) $s->text);
                
$this->write("PRIVMSG $this->ownerNick :[User] <$screenName$text");
                
sleep(1);
            }
        }
    }

    protected function 
twitter_info($user) {
        if (!
$this->ownerScreenName) return false;
        
$json $this->oAuth->get('users/show',array('screen_name'=>$user));
        
$i $this->oAuth->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error retrieving user info: %s',$this->ownerNick,(string) $json->error));
            else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error retrieving user info ($i)");
        } else {
            
$this->write(sprintf('PRIVMSG %s :%s (location: %s, followers: %s, followings: %s, statuses: %s, protected: %s, verified: %s, following: %s)',$this->ownerNick,(string)(string)$json->name,$json->location,(string)$json->followers_count,(string)$json->friends_count,(string)$json->statuses_count,$json->protected 'yes' 'no',$json->verified 'yes' 'no',$json->following 'yes' 'no'));
            
$this->write(sprintf('PRIVMSG %s :Description: %s',$this->ownerNick,(string)$json->description));
            
$this->write(sprintf('PRIVMSG %s :Web site: %s',$this->ownerNick,(string)$json->url));
            
$this->write(sprintf('PRIVMSG %s :Profile image: %s',$this->ownerNick,preg_replace('#_normal(\.[a-z]+)$#si','\1',(string)$json->profile_image_url)));
            
$this->write(sprintf('PRIVMSG %s :Last status: %s',$this->ownerNick,$this->expand_urls((string)$json->status->text)));
        }
    }

    protected function 
twitter_show($id) {
        if (!
$this->ownerScreenName) return false;
        
$json $this->oAuth->get('statuses/show',array('id'=>$id));
        
$i $this->oAuth->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error retrieving a status: %s',$this->ownerNick,(string) $json->error));
            else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error retrieving a status ($i)");
        } else {
            
$screenName = (string) $json->user->screen_name;
            
$text $this->expand_urls((string) $json->text);
            
$this->write("PRIVMSG $this->ownerNick :[SHOW] <$screenName$text");
            
sleep(1);
        }
    }

    protected function 
twitter_follow($user) {
        if (!
$this->ownerScreenName) return false;
        
$json $this->oAuth->post('friendships/create',array('screen_name'=>$user));
        
$i $this->oAuth->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error following user: %s',$this->ownerNick,(string) $json->error));
            else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error following user ($i)");
        } elseif (!@
$json->id$this->write("PRIVMSG $this->ownerNick :Maybe an error following user (API not responding)");
    }

    protected function 
twitter_unfollow($user) {
        if (!
$this->ownerScreenName) return false;
        
$json $this->oAuth->delete('friendships/destroy',array('screen_name'=>$user));
        
$i $this->oAuth->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error unfollowing user: %s',$this->ownerNick,(string) $json->error));
            else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error unfollowing user ($i)");
        } elseif (!@
$json->id$this->write("PRIVMSG $this->ownerNick :Maybe an error unfollowing user (API not responding)");
    }

    protected function 
twitter_undo() {
        if (!
$this->twitterUndo) {
            
$this->write("PRIVMSG $this->ownerNick :No tweet to undo !");
        } else {
            if (!
$this->ownerScreenName) return false;
            
$json $this->oAuth->delete('statuses/destroy',array('id'=>$this->twitterUndo));
            
$i $this->oAuth->lastStatusCode();
            if (
self::DEBUG) {
                
print_r($json);
            }
            if (
$i != 200) {
                if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error deleting status: %s',$this->ownerNick,(string) $json->error));
                else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error deleting status ($i)");
            } elseif (
$this->twitterUndo != @$json->id$this->write("PRIVMSG $this->ownerNick :Maybe an error deleting status (API not responding)");
            else {
                
$this->twitterUndo '';
            }
        }
    }

    protected function 
twitter_status() {
        
$status $this->twitterStatus 'running' 'stopped';
        
$this->write("PRIVMSG $this->ownerNick :Service is $status");
        if (!
$this->ownerScreenName) return false;
        
$json $this->oAuth->get('account/rate_limit_status');
        
$i $this->oAuth->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error retrieving rate limit status: %s',$this->ownerNick,(string) $json->error));
            else 
$this->write("PRIVMSG $this->ownerNick :Maybe an error retrieving rate limit status ($i)");
        } elseif (!@
$json$this->write("PRIVMSG $this->ownerNick :Maybe an error retrieving rate limit status (API not responding)");
        else {
            
$this->write("PRIVMSG $this->ownerNick :API Remaining Hits: $json->remaining_hits");
        }
    }

    protected function 
twitter_search($q,$lang=null) {
        if (!
$this->ownerNick) return false;
        
$data = array('q'=>$q);
        if (
$lang$data['lang'] = $lang;
        if (!
$response = @file_get_contents(sprintf('http://search.twitter.com/search.json?%s',http_build_query($data)))) {
            
$this->write("PRIVMSG $this->ownerNick :No response from Twitter Search API");
            return 
false;
        }
        
$json = @json_decode(trim(preg_replace('#((id)|(cursor))":(\d+)#','\1":"\4"',$response)));
        if (
$json->error) {
            
$this->write("PRIVMSG $this->ownerNick :$json->error");
            return 
false;
        }
        if (@
count($json->results)) {
            
$results array_reverse($json->results);
            foreach (
$results as $s) {
                
$screenName = (string) $s->from_user;
                
$text $this->expand_urls((string) $s->text);
                
$this->write("PRIVMSG $this->ownerNick :[SEARCH] <$screenName$text");
                
sleep(1);
            }
        }
    }

    protected function 
run() {
        if (!
$this->conn = @fsockopen('irc.freenode.net','6667',$errno,$errstr,10)) {
            
$this->output($errmsg,true);
            exit;
        }
        
$this->write(sprintf('NICK %s',self::NICK));
        
$this->write(sprintf('USER %1$s localhost irc_server :%1$s',self::NICK));
        if (
self::PASSWORD) {
            
$this->write(sprintf('PRIVMSG nickserv :IDENTIFY %s %s',self::NICK,self::PASSWORD));
            
$this->write(sprintf('PRIVMSG nickserv :GHOST %s',self::NICK));
            
$this->write(sprintf('NICK %s',self::NICK));
        }

        if (
$this->ownerNick) {
            
$this->write("PRIVMSG $this->ownerNick :I'm online !");
        }

        while (
$this->conn) {
            
$line $this->read();
            if (
$line === false) break;            

            if (
strpos($line,'PING ') === 0$this->write(str_replace('PING','PONG',$line));
            elseif (
preg_match('#^:([^!]+![^ ]+) PRIVMSG ([^ ]+) :(.*)$#s',$line,$m) && ($m[2] == self::NICK)) {
                
$from $m[1];
                
$text $m[3];
                list(
$user,$mask) = explode('!',$from,2);

                if (!
$this->owner || ($from == $this->owner)) {
                    if (
preg_match('#^!quit(.*)$#s',$text,$m) && (ltrim($m[1]) == self::SHARED_SECRET)) break;
                    elseif (
preg_match('#^!connect(.*)$#s',$text,$m) && (ltrim($m[1]) == self::SHARED_SECRET)) {
                        
$this->oAuth = new TwitterOAuth(self::CONSUMER_KEY,self::CONSUMER_SECRET);
                        
$request_token $this->oAuth->getRequestToken();
                        
$url $this->oAuth->getAuthorizeURL($request_token['oauth_token']);
                        
$this->write("PRIVMSG $user :Please retrieve a PIN here : $url");
                        
$this->write("PRIVMSG $user :Once you have the PIN code, type: !pin THE_PIN_CODE");
                        
$this->candidate $this->owner $from;
                        
$this->ownerNick $user;
                    } elseif ((
$from == $this->candidate) && !$this->token && preg_match('#^!pin (.*)$#s',$text,$m)) {
                        
$this->token $this->oAuth->getAccessToken($m[1]);
                        
$this->candidate '';
                        if (
self::DEBUGprint_r($this->token);
                        if (!@
$this->token['screen_name']) {
                            
$this->token null;
                            
$this->oAuth null;
                            
$this->write("PRIVMSG $user :Incorrect PIN code, please recall !connect to authentificate");
                        } else {
                            
$this->ownerScreenName $this->token['screen_name'];
                            
$this->write(sprintf('PRIVMSG %s :Welcome %s !',$user,$this->ownerScreenName));
                            
$this->backup_data();
                        }
                    } elseif (
$this->candidate && !$this->token) {
                        
$this->write("PRIVMSG $user :I'm still waiting for your PIN code !");
                    } elseif (!
$this->candidate && !$this->token) {
                        
$this->write("PRIVMSG $user :Type \"!connect [PASSWORD]\" to start");
                    } else {
                        if (
strpos($text,'!') === 0) {
                            switch (
substr($text,1)) {
                                case 
'start':
                                    
$this->twitterStatus true;
                                    
$this->twitter_check();
                                    break;
                                case 
'stop':
                                    
$this->twitterStatus false;
                                    break;
                                case 
'status':
                                    
$this->twitter_status();
                                    break;
                                case 
'mentions':
                                case 
'mention':
                                    
$this->twitter_mentions();
                                    break;
                                case 
'exit':
                                case 
'bye':
                                case 
'quit':
                                    break 
2;
                                case 
'dm':
                                    
$this->twitter_dm();
                                    break;
                                case 
'undo':
                                    
$this->twitter_undo();
                                    break;
                                default:
                                    if (
preg_match('#^!last ([a-z0-9_]+)$#si',$text,$m)) {
                                        
$this->twitter_timeline($m[1]);
                                    } elseif (
preg_match('#^!info ([a-z0-9_]+)$#si',$text,$m)) {
                                        
$this->twitter_info($m[1]);
                                    } elseif (
preg_match('#^!show ([0-9]+)$#si',$text,$m)) {
                                        
$this->twitter_show($m[1]);
                                    } elseif (
preg_match('#^!follow ([a-z0-9_]+)$#si',$text,$m)) {
                                        
$this->twitter_follow($m[1]);
                                    } elseif (
preg_match('#^!unfollow ([a-z0-9_]+)$#si',$text,$m)) {
                                        
$this->twitter_unfollow($m[1]);
                                    } elseif (
preg_match('#^!dm ([a-z0-9_]+) (.*)$#si',$text,$m)) {
                                        
$this->twitter_dm($m[1],$m[2]);
                                    } elseif (
preg_match('#^!search (.*)$#si',$text,$m)) {
                                        
$this->twitter_search($m[1]);
                                    } elseif (
preg_match('#^!search_([a-z]{2}) (.*)$#si',$text,$m)) {
                                        
$this->twitter_search($m[2],$m[1]);
                                    } else {
                                        
$this->write("PRIVMSG $user :Basic commands are: !start | !stop | !status | !mentions | !quit | !dm[ USER MESSAGE] | !undo | !last USER | !info USER | !show STATUS_ID | !follow USER | !unfollow USER | !search QUERY | !search_xx QUERY (xx=lang code)");
                                    }
                            }
                        }
                        else 
$this->twitter_update($text);
                    }
                } else {
                    
$this->write("PRIVMSG $user :Sorry, I don't know you");
                }
            } else 
$this->twitter_check();
        }

        if (
$this->ownerNick) {
            
$this->write("PRIVMSG $this->ownerNick :I'm leaving !");
        }

        
$this->write("QUIT");

        @
fclose($this->conn);
    }
}

$X = new Twirc;

?>

TwittServ

TwittServ is a Twitter IRC bot written in PHP. Just help yourself to use it :)

<?php

/*

Get the twitteroauth lib here : http://github.com/abraham/twitteroauth

To avoid bugs, I recommend to replace "return json_decode($response)" in methods "get", "post" and "delete" by :

    return @json_decode(trim(preg_replace('#((id)|(cursor))":(\d+)#','\1":"\4"',$response)));

Also replace "$http_status" by "$http_code" in method "lastStatusCode()"

*/

require_once dirname(__FILE__) . '/OAuth.php';
require_once 
dirname(__FILE__) . '/twitteroauth.php';

class 
TwittServ {
    const 
DEBUG false;
    const 
NICK 'CHOOSE_A_NICKNAME_FOR_YOUR_BOT';
    const 
PASSWORD ''// Optionnal, set the nickserv password if needed
    
const VHOST =''// Optionnal, format: VHOST = 'user:password', if set it will send a "/VHOST user password" on connection
    
const IRC_NETWORK 'HOST:PORT'// set your irc network here

    // You need to register a new app here (read/update permissions and PIN method): https://twitter.com/apps/new

    
const CONSUMER_KEY '';
    const 
CONSUMER_SECRET '';

    const 
SHARED_SECRET ''// A secret password to run admin commands: !join CHANNEL SHARED_SECRET, !leave CHANNEL SHARED SECRET, !quit SHARED_SECRET

    
protected $conn null;
    protected 
$channels = array();
    protected 
$clients = array();
    protected 
$backupFile '';

    function 
__construct() {
        
$this->backupFile sprintf('%s.data.inc',__FILE__);
        
$this->restore_data();
        
$this->run();
    }
    
    protected function 
output($msg,$forceOutput=false) {
        if (
self::DEBUG || $forceOutput) echo "$msg\n";
    }

    protected function 
restore_data() {
        if (
file_exists($this->backupFile)) {
            list(
$this->channels,$this->clients) = @unserialize(file_get_contents($this->backupFile));
        }
    }

    protected function 
backup_data() {
        
file_put_contents($this->backupFile,serialize(array($this->channels,$this->clients)));
    }

    protected function 
read() {
        if (
$this->conn) {
            
$line trim(fgets($this->conn));
            if (
$line$this->output($line);
            else 
sleep(1);
            return 
$line;
        }
        return 
false;
    }

    protected function 
write($cmd) {
        if (
$this->conn) {
            
$this->output($cmd);
            
fputs($this->conn,"$cmd\r\n");
        }
    }

    protected function 
irc_join($channel) {
        if (!
$this->conn) return false;
        
$channel strtolower($channel);
        if (!
array_key_exists($channel,$this->channels)) {
            
$this->channels[$channel] = true;
            
$this->backup_data();
            
$this->write("JOIN #$channel");
        }
    }

    protected function 
irc_part($channel) {
        if (!
$this->conn) return false;
        
$channel strtolower($channel);
        if (
array_key_exists($channel,$this->channels)) {
            
$c $this->channels;
            unset(
$c[$channel]);
            
$this->channels $c;
            
$this->backup_data();
            
$this->write("PART #$channel");
        }
    }

    protected function 
add_client($client,$text) {
        list(
$nick,$mask) = explode('!',$client,2);
        
$this->clients[$client] = array('nick'=>$nick,'confirmed'=>false,'text'=>$text,'oAuth'=>new TwitterOAuth(self::CONSUMER_KEY,self::CONSUMER_SECRET));
        
$request_token $this->clients[$client]['oAuth']->getRequestToken();
        
$url $this->clients[$client]['oAuth']->getAuthorizeURL($request_token['oauth_token']);
        
$this->write("PRIVMSG $nick :Please retrieve a PIN here : $url");
        
$this->write(sprintf('PRIVMSG %s :Once you have the PIN code, type: /msg %s !pin THE_PIN_CODE',$nick,self::NICK));
        
$this->backup_data();
    }

    protected function 
confirm_client($client,$pin) {
        if (!
array_key_exists($client,$this->clients) || $this->clients[$client]['confirmed']) return false;
        
$token $this->clients[$client]['oAuth']->getAccessToken($pin);
        if (
self::DEBUGprint_r($token);
        if (!@
$token['screen_name']) {
            
$this->write(sprintf('PRIVMSG %s :Incorrect PIN code',$this->clients[$client]['nick']));
            return 
$this->remove_client($client);
        } else {
            
$this->clients[$client]['confirmed'] = true;
            
$this->write(sprintf('PRIVMSG %s :Welcome %s, your message has been tweeted. Type "!tweet MESSAGE" to tweet, type "/msg %s !signout" to end twitter session',$this->clients[$client]['nick'],$token['screen_name'],self::NICK));
            
$this->tweet($client,$this->clients[$client]['text']);
            
$c $this->clients[$client];
            unset(
$c['text']);
            
$this->clients[$client] = $c;
            
$this->backup_data();
        }
    }

    protected function 
remove_client($client) {
        if (!
array_key_exists($client,$this->clients)) return false;
        
$c $this->clients;
        unset(
$c[$client]);
        
$this->clients $c;
        
$this->backup_data();
    }

    protected function 
tweet($client,$text) {
        if (!
array_key_exists($client,$this->clients) || !$this->clients[$client]['confirmed']) return false;
        
$text mb_check_encoding($text,'UTF-8') ? $text utf8_encode($text);
        
$json $this->clients[$client]['oAuth']->post('statuses/update',array('status'=>$text));
        
$i $this->clients[$client]['oAuth']->lastStatusCode();
        if (
self::DEBUG) {
            
print_r($json);
        }
        if (
$i != 200) {
            if (@
$json->error$this->write(sprintf('PRIVMSG %s :Error updating status: %s',$this->clients[$client]['nick'],(string) $json->error));
            else 
$this->write(sprintf('PRIVMSG %s :Maybe an error updating status (%s)',$this->clients[$client]['nick'],$i));
        } elseif (!@
$json->id$this->write(sprintf('PRIVMSG %s :Error sending status: Twitter API not responding...',$this->clients[$client]['nick']));
    }

    protected function 
run() {
        list(
$ircServer,$ircPort) = explode(':',self::IRC_NETWORK,2);
        if (!
$this->conn = @fsockopen($ircServer,$ircPort,$errno,$errstr,10)) {
            
$this->output($errmsg,true);
            exit;
        }
        
$this->write(sprintf('NICK %s',self::NICK));
        
$this->write(sprintf('USER %1$s localhost irc_server :%1$s',self::NICK));
        if (
self::PASSWORD) {
            
$this->write(sprintf('PRIVMSG NickServ :IDENTIFY %s',self::PASSWORD));
            
$this->write(sprintf('PRIVMSG NickServ :GHOST %s',self::NICK));
            
$this->write(sprintf('NICK %s',self::NICK));
        }
        if (
self::VHOST) {
            list(
$vhostUser,$vhostPassword) = explode(':',self::VHOST,2);
            
$this->write(sprintf('VHOST %s %s',$vhostUser,$vhostPassword));
            
sleep(5);
        }
        if (
count($this->channels)) {
            foreach (
array_keys($this->channels) as $channel$this->write("JOIN #$channel");
        }

        while (
$this->conn) {
            
$line $this->read();
            if (
$line === false) break;            

            if (
strpos($line,'PING ') === 0$this->write(str_replace('PING','PONG',$line));
            elseif (
preg_match('#^:([^!]+![^ ]+) PRIVMSG ([^ ]+) :!tweet (.+)$#s',$line,$m)) {
                
$from $m[1];
                
$text $m[3];
                if (!
array_key_exists($from,$this->clients)) $this->add_client($from,$text);
                elseif (@
$this->clients[$from]['confirmed']) $this->tweet($from,$text);
            }
            elseif (
preg_match('#^:([^!]+![^ ]+) PRIVMSG ([^ ]+) :(.*)$#s',$line,$m) && ($m[2] == self::NICK)) {
                
$from $m[1];
                
$text $m[3];
                if (
preg_match('#^!quit(.*)$#s',$text,$m) && (ltrim($m[1]) == self::SHARED_SECRET)) break;
                elseif (
preg_match('#^!join \#?([^ ]+)(.*)$#s',$text,$m) && (ltrim($m[2]) == self::SHARED_SECRET)) $this->irc_join($m[1]);
                elseif (
preg_match('#^!part \#?([^ ]+)(.*)$#s',$text,$m) && (ltrim($m[2]) == self::SHARED_SECRET)) $this->irc_part($m[1]);
                elseif (
preg_match('#^!pin ([0-9]+)$#s',$text,$m)) $this->confirm_client($from,$m[1]);
                elseif ((
$text == '!signout') || ($text == '!logout')) $this->remove_client($from);
                else {
                    list(
$nick,$mask) = explode('!',$from,2);
                    
$this->write("PRIVMSG $nick :Type \"!tweet MESSAGE\" to tweet \"MESSAGE\" with your Twitter account");
                }
            }
        }

        
$this->write("QUIT");

        @
fclose($this->conn);
    }
}

$X = new Twittserv;

?>

fbparis - Contact: @fbparis