├── FAQ.Markdown ├── LICENSE ├── classes ├── Config.php └── SQL.php ├── .htaccess ├── bot ├── BaseTwitBot.php ├── EchoTwitBot.php ├── RssTwitBot.php └── QuestionTwitBot.php ├── README.Markdown ├── assets └── twitbot.sql ├── bot_runner.php └── api └── Twitter.php /FAQ.Markdown: -------------------------------------------------------------------------------- 1 | If you have any questions about the code in this repository, please fork the repository, ask them in this file and then post a pull request on GitHub. This way anyone else who has the same question later will be able to find the answer. Thanks. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008, Neil Crosby ( http://neilcrosby.com/vcard ) 2 | 3 | The contents of all files and directories are licensed for reuse under the 4 | Creative Commons "Attribution-Share Alike 3.0 Unported" license unless 5 | otherwise noted in the individual files. 6 | 7 | Full details of this license can be found at: 8 | 9 | http://creativecommons.org/licenses/by-sa/3.0/ ) 10 | 11 | -------------------------------------------------------------------------------- /classes/Config.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine on 2 | 3 | FileETag none 4 | 5 | 6 | # Insert filter 7 | SetOutputFilter DEFLATE 8 | 9 | # Netscape 4.x has some problems... 10 | BrowserMatch ^Mozilla/4 gzip-only-text/html 11 | 12 | # Netscape 4.06-4.08 have some more problems 13 | BrowserMatch ^Mozilla/4\.0[678] no-gzip 14 | 15 | # MSIE masquerades as Netscape, but it is fine 16 | # BrowserMatch \bMSIE !no-gzip !gzip-only-text/html 17 | 18 | # NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48 19 | # the above regex won't work. You can use the following 20 | # workaround to get the desired effect: 21 | BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html 22 | 23 | # Don't compress images 24 | SetEnvIfNoCase Request_URI \ 25 | \.(?:gif|jpe?g|png)$ no-gzip dont-vary 26 | 27 | # Make sure proxies don't deliver the wrong content 28 | # Header append Vary User-Agent env=!dont-vary 29 | 30 | 31 | ################################################### 32 | # Don't let people see config etc directories 33 | ################################################### 34 | RewriteRule ^\. - [F,L] 35 | RewriteRule ^api(/.*)?$ - [F,L] 36 | RewriteRule ^bot(/.*)?$ - [F,L] 37 | RewriteRule ^classes(/.*)?$ - [F,L] 38 | RewriteRule ^README.Markdown$ - [F,L] 39 | RewriteRule ^LICENSE$ - [F,L] 40 | -------------------------------------------------------------------------------- /bot/BaseTwitBot.php: -------------------------------------------------------------------------------- 1 | aOptions = $aOptions; 9 | } 10 | 11 | /** 12 | * @return true if everything completed successfully 13 | **/ 14 | public function run() { 15 | echo "bot ran - {$this->aOptions['username']}
"; 16 | } 17 | 18 | public function getLastDataTime() { 19 | if ( !isset($this->aOptions['last_data_time']) ) { 20 | $this->aOptions['last_data_time'] = 0; 21 | } 22 | return $this->aOptions['last_data_time']; 23 | } 24 | 25 | } 26 | 27 | /* 28 | $username = 'twitbotcom'; 29 | $password = 'sillypassword'; 30 | $fileName = 'last_checked.txt'; 31 | 32 | $last = file_exists( $fileName ) ? file_get_contents( $fileName ) : null; 33 | 34 | $bot = new Twitter( $username, $password ); 35 | $messages = $bot->directMessages->direct_messages(); 36 | echo "
";
37 | print_r($messages);
38 | echo "
"; 39 | // now sort the messages so that the oldest is first 40 | $messages = array_reverse( $messages ); 41 | 42 | foreach( $messages as $message ) { 43 | $sender = $message->sender->screen_name; 44 | $status = trim( $message->text ); 45 | $last = $message->created_at; 46 | 47 | $bot->status->update( "@$sender says \"$status\"" ); 48 | } 49 | 50 | file_put_contents( $fileName, $last ); 51 | */ 52 | 53 | ?> -------------------------------------------------------------------------------- /README.Markdown: -------------------------------------------------------------------------------- 1 | # TwitBot 2 | 3 | TwitBot is a library intended to allow easy creation of Bots using the Twitter website. I gave [a talk about TwitBot][1] at BarCamp Brighton 2. 4 | 5 | ## Types of TwitBot 6 | 7 | ### RSS TwitBot 8 | 9 | The simplest type of bot. This type simply watches a given RSS feed and outputs new feed entries as a new tweet by a given username. 10 | 11 | This bot will tweet at most once per 15 minute interval. If multiple items have been added to the watched RSS feed since the last tweet then the bot will not tweet each item, but will instead tweet a single message along the lines of "$num new items posted to $link since the last time I tweeted". 12 | 13 | The RSS TwitBot is currently used by [ttwr][2] to follow the latest reviews feed on [The Ten Word Review][3]. 14 | 15 | ### Echo TwitBot 16 | 17 | Accepts direct messages from people who it follows and retweets them. This allows the creation of "Twitter Channels". 18 | 19 | The Echo TwitBot is currently used by [yahoofood][4] to alert London-based Yahoos of tasty food in the vicinity. 20 | 21 | ### Question TwitBot 22 | 23 | The most exciting type of bot. This bot watches for direct messages, then passes them through to an external web service. It can then pass any response from the webservice back to the user who initially sent it the message. 24 | 25 | This bot type is currently in flux and could conceivably change a lot - it has a lot of scope for what it could do. 26 | 27 | The Question TwitBot is currently used by [ttwr][2] to allow a few alpha testers to tweet reviews to [The Ten Word Review][3]. 28 | 29 | 30 | [1]: http://www.slideshare.net/neilcrosby/twitter-bots 31 | [2]: http://twitter.com/ttwr 32 | [3]: http://thetenwordreview.com 33 | [4]: http://twitter.com/yahoofood 34 | -------------------------------------------------------------------------------- /assets/twitbot.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 2.11.7.1 3 | -- http://www.phpmyadmin.net 4 | -- 5 | -- Host: localhost 6 | -- Generation Time: Mar 15, 2009 at 10:06 AM 7 | -- Server version: 5.0.51 8 | -- PHP Version: 5.2.4-2ubuntu5.4 9 | 10 | SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; 11 | 12 | -- 13 | -- Database: `twitbot` 14 | -- 15 | 16 | -- -------------------------------------------------------- 17 | 18 | -- 19 | -- Table structure for table `tbl_bot` 20 | -- 21 | 22 | CREATE TABLE IF NOT EXISTS `tbl_bot` ( 23 | `uid` int(11) NOT NULL auto_increment, 24 | `username` varchar(25) collate utf8_unicode_ci NOT NULL, 25 | `password` varchar(255) collate utf8_unicode_ci NOT NULL, 26 | `email` varchar(255) collate utf8_unicode_ci NOT NULL, 27 | PRIMARY KEY (`uid`), 28 | UNIQUE KEY `username` (`username`,`email`) 29 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=15 ; 30 | 31 | -- -------------------------------------------------------- 32 | 33 | -- 34 | -- Table structure for table `tbl_bot_echo` 35 | -- 36 | 37 | CREATE TABLE IF NOT EXISTS `tbl_bot_echo` ( 38 | `uid` int(11) NOT NULL, 39 | `last_data_time` datetime NOT NULL, 40 | `sender_is_private` binary(1) NOT NULL default '0', 41 | PRIMARY KEY (`uid`) 42 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 43 | 44 | -- -------------------------------------------------------- 45 | 46 | -- 47 | -- Table structure for table `tbl_bot_question` 48 | -- 49 | 50 | CREATE TABLE IF NOT EXISTS `tbl_bot_question` ( 51 | `uid` int(11) NOT NULL, 52 | `last_data_time` datetime NOT NULL, 53 | `url` varchar(255) collate utf8_unicode_ci NOT NULL, 54 | `method` varchar(6) collate utf8_unicode_ci NOT NULL default 'GET', 55 | `params` varchar(255) collate utf8_unicode_ci NOT NULL, 56 | PRIMARY KEY (`uid`) 57 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 58 | 59 | -- -------------------------------------------------------- 60 | 61 | -- 62 | -- Table structure for table `tbl_bot_rss` 63 | -- 64 | 65 | CREATE TABLE IF NOT EXISTS `tbl_bot_rss` ( 66 | `uid` int(11) NOT NULL, 67 | `last_data_time` datetime NOT NULL, 68 | `url` varchar(255) collate utf8_unicode_ci NOT NULL, 69 | PRIMARY KEY (`uid`) 70 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 71 | -------------------------------------------------------------------------------- /bot_runner.php: -------------------------------------------------------------------------------- 1 | array('class' => 'EchoTwitBot', 'table' => 'tbl_bot_echo'), 23 | 'rss' => array('class' => 'RssTwitBot', 'table' => 'tbl_bot_rss'), 24 | 'question' => array('class' => 'QuestionTwitBot', 'table' => 'tbl_bot_question'), 25 | 'werewolf' => array('class' => 'WerewolfTwitBot', 'table' => 'tbl_bot_werewolf'), 26 | 'friendly' => array('class' => 'FriendlyTwitBot', 'table' => 'tbl_bot_friendly'), 27 | ); 28 | 29 | if ( !isset($_GET['type']) || !array_key_exists( $_GET['type'], $aValidTypes ) ) { 30 | echo "

Invalid type

"; 31 | exit(); 32 | } 33 | 34 | // Set up DB uname/pword, error reporting etc. 35 | new Config(); 36 | 37 | $botType = $aValidTypes[$_GET['type']]; 38 | $botClass = $botType['class']; 39 | $botTable = $botType['table']; 40 | 41 | $safeBotTable = SQL::makeSafe($botTable); 42 | $sql = "SELECT * 43 | FROM tbl_bot 44 | INNER JOIN $safeBotTable ON $safeBotTable.uid = tbl_bot.uid"; 45 | $aBots = SQL::doReadQuery($sql); 46 | 47 | foreach ( $aBots as $aBotVars ) { 48 | $aBotVars['last_data_time'] = strtotime($aBotVars['last_data_time']); 49 | $lastDataTime = $aBotVars['last_data_time']; 50 | 51 | $bot = new $botClass( $aBotVars ); 52 | $bot->run(); 53 | $newLastDataTime = $bot->getLastDataTime(); 54 | 55 | echo "

{$aBotVars['username']} $lastDataTime $newLastDataTime

"; 56 | echo '

'.date("D M j H:i:s +0000 Y", $newLastDataTime).'

'; 57 | 58 | if ( $newLastDataTime != $lastDataTime ) { 59 | $safeUid = SQL::makeSafe($aBotVars['uid']); 60 | $safeNewLastDataTime = SQL::makeSafe(date('Y-m-d H:i:s', $newLastDataTime)); 61 | 62 | $sql = "UPDATE $safeBotTable 63 | SET last_data_time = '$safeNewLastDataTime' 64 | WHERE uid = '$safeUid'"; 65 | SQL::doWriteQuery($sql); 66 | } 67 | } 68 | 69 | ?> -------------------------------------------------------------------------------- /bot/EchoTwitBot.php: -------------------------------------------------------------------------------- 1 | aOptions['username']; 20 | $password = $this->aOptions['password']; 21 | $lastDataTimeAsStr = date("D M j H:i:s +0000 Y", $this->aOptions['last_data_time']); 22 | 23 | $bot = new Twitter( $username, $password ); 24 | $messages = $bot->directMessages->direct_messages( array( 'since' => $lastDataTimeAsStr ) ); 25 | 26 | if ( !is_array($messages) ) { 27 | return; 28 | } 29 | 30 | // Now sort the messages so that the oldest is first 31 | // We do this because we want messages to be echoed back out to 32 | // Twitter in the same order as they were received. 33 | $messages = array_reverse( $messages ); 34 | 35 | $badPhrases = array( 36 | 'Thanks for following', 37 | 'Thank you for following', 38 | 'danke, dass Du meinen Tweets folgst', 39 | 'myexquisitefoods', 40 | ); 41 | 42 | foreach( $messages as $message ) { 43 | $sender = $message->sender->screen_name; 44 | $status = trim( $message->text ); 45 | 46 | $isBad = false; 47 | foreach ( $badPhrases as $phrase ) { 48 | if ( mb_stristr($status, $phrase) ) { 49 | $isBad = true; 50 | break; 51 | } 52 | } 53 | if ($isBad) { 54 | continue; 55 | } 56 | 57 | $this->aOptions['last_data_time'] = strtotime($message->created_at); 58 | 59 | // $output = ( isset($this->aOptions['sender_is_private']) && 1 == $this->aOptions['sender_is_private'] ) 60 | // ? $status 61 | // : "@$sender says \"$status\""; 62 | $output = ( 'kapowatch' == $username || 'neilisannoyedby' == $username ) 63 | ? $status 64 | : "@$sender says \"$status\""; 65 | // $output = "@$sender says \"$status\""; 66 | 67 | $bot->status->update( $output ); 68 | } 69 | 70 | } 71 | 72 | } 73 | 74 | ?> -------------------------------------------------------------------------------- /bot/RssTwitBot.php: -------------------------------------------------------------------------------- 1 | aOptions['url'] ); 13 | 14 | if ( !$feed ) { 15 | return; 16 | } 17 | 18 | $feedItems = array(); 19 | foreach ( $feed->channel->item as $item ) { 20 | array_push( $feedItems, $item ); 21 | } 22 | 23 | // now sort the messages so that the oldest is first 24 | $feedItems = array_reverse( $feedItems ); 25 | 26 | $username = $this->aOptions['username']; 27 | $password = $this->aOptions['password']; 28 | $bot = new Twitter( $username, $password ); 29 | 30 | // TODO: needs generalising out to the DB 31 | $now = time(); 32 | if ( $now - $this->aOptions['last_data_time'] <= 60 * 15 ) { // 15 minute gaps 33 | echo "

Not enough time passed - $now - ".$this->aOptions['last_data_time'].' - '.($now - $this->aOptions['last_data_time']).' - '.(60 * 15)."

"; 34 | return; 35 | } 36 | 37 | $itemsToPost = array(); 38 | foreach( $feedItems as $item ) { 39 | $itemTime = strtotime($item->pubDate); 40 | 41 | if ( $itemTime <= $this->aOptions['last_data_time'] ) { 42 | continue; 43 | } 44 | 45 | //echo "

{$item->title}: {$item->link}

"; 46 | array_push($itemsToPost, $item); 47 | } 48 | 49 | $toPost = null; 50 | if ( 1 == sizeof($itemsToPost) ) { 51 | $item = $itemsToPost[0]; 52 | 53 | $titleLength = mb_strlen($item->title); 54 | $urlLength = mb_strlen('http://tinyurl.com/6dvl5n'); // a short url created by twitter - TODO switch to bit.ly (it's shorter) 55 | $shortDescAllowedLength = 140 - $urlLength - $titleLength - 3; 56 | 57 | $shortDesc = html_entity_decode(strip_tags($item->description), ENT_COMPAT); 58 | 59 | if ( mb_strlen($shortDesc) > $shortDescAllowedLength ) { 60 | $shortDesc = mb_substr($shortDesc, 0, $shortDescAllowedLength - 3).'...'; 61 | } 62 | 63 | $toPost = "{$item->title}: $shortDesc {$item->link}"; 64 | } else if ( 1 < sizeof($itemsToPost) ) { 65 | $num = sizeof($itemsToPost); 66 | $link = $feed->channel->link; 67 | $toPost = "$num new items posted to $link since the last time I tweeted."; 68 | } 69 | 70 | if ($toPost) { 71 | $this->aOptions['last_data_time'] = $now; 72 | $bot->status->update($toPost); 73 | } 74 | } 75 | 76 | } 77 | 78 | ?> -------------------------------------------------------------------------------- /bot/QuestionTwitBot.php: -------------------------------------------------------------------------------- 1 | aOptions['url']; 13 | $method = $this->aOptions['method']; 14 | $params = $this->aOptions['params']; 15 | 16 | $username = $this->aOptions['username']; 17 | $password = $this->aOptions['password']; 18 | $lastDataTimeAsStr = date("D M j H:i:s +0000 Y", $this->aOptions['last_data_time']); 19 | 20 | $bot = new Twitter( $username, $password ); 21 | $messages = $bot->directMessages->direct_messages( array( 'since' => $lastDataTimeAsStr ) ); 22 | // $messages = $bot->status->user_timeline( array( 'since' => $lastDataTimeAsStr ) ); 23 | 24 | if ( !is_array($messages) ) { 25 | echo "

No messages

"; 26 | return; 27 | } 28 | 29 | // now sort the messages so that the oldest is first 30 | $messages = array_reverse( $messages ); 31 | 32 | // echo "
";
33 | //        print_r($messages);
34 | //        echo "
"; 35 | 36 | foreach( $messages as $message ) { 37 | echo "
";
38 |             print_r($message);
39 |             echo "
"; 40 | $sender = $message->sender->screen_name; 41 | $status = trim( $message->text ); 42 | $this->aOptions['last_data_time'] = strtotime($message->created_at); 43 | 44 | $thisParams = $params; 45 | $thisParams = str_replace( '!!TWITBOT_TEXT!!', urlencode($status), $thisParams ); 46 | $thisParams = str_replace( '!!TWITBOT_USER!!', urlencode($sender), $thisParams ); 47 | echo "

$thisParams

"; 48 | 49 | $session = curl_init(); 50 | 51 | curl_setopt( $session, CURLOPT_HEADER, false ); 52 | curl_setopt( $session, CURLOPT_RETURNTRANSFER, 1 ); 53 | 54 | if ( 'POST' == $method ) { 55 | curl_setopt( $session, CURLOPT_POST, 1 ); 56 | curl_setopt( $session, CURLOPT_POSTFIELDS, $thisParams ); 57 | } else { 58 | $url .= '?'.$thisParams; 59 | } 60 | 61 | curl_setopt( $session, CURLOPT_URL, $url ); 62 | 63 | $result = curl_exec( $session ); 64 | 65 | curl_close( $session ); 66 | 67 | echo "

returned

"; 68 | 69 | if ( !isset($result) ) { 70 | // echo "

No result

"; 71 | continue; 72 | } 73 | 74 | echo "
".print_r($result)."
"; 75 | 76 | if ( $json = json_decode($result) ) { 77 | if ( $json['error'] ) { 78 | //$bot->directMessages->new_message( $json['error'] ); 79 | } 80 | } 81 | 82 | echo "

still here

"; 83 | 84 | // echo "

Result: $result

"; 85 | //$bot->directMessages->new_message( $result ); 86 | } 87 | 88 | } 89 | 90 | } 91 | 92 | ?> -------------------------------------------------------------------------------- /classes/SQL.php: -------------------------------------------------------------------------------- 1 | check() ) { 61 | $cached = $cache->get(); 62 | return $cached['data']; 63 | } 64 | } 65 | 66 | $conn = SQL::getReadConnection(); 67 | if ( self::CONNECTION_ERROR == $conn ) { 68 | return null; 69 | } 70 | 71 | $startTime = microtime( true ); 72 | $result = mysql_query($sql, $conn); 73 | $endTime = microtime( true ); 74 | $totalTime = $endTime - $startTime; 75 | 76 | if ( $logExists && $totalTime > 1 ) { 77 | Log::logSQL( SQL::getCallingFunction(), $endTime - $startTime, $sql ); 78 | } 79 | 80 | if ( $logExists && (!$result || mysql_error($conn)) ) { 81 | Log::error("SQL performed: $sql\n\nError: ".mysql_error($conn)); 82 | return null; 83 | } 84 | 85 | $rows = array(); 86 | while ( $row = mysql_fetch_array($result, MYSQL_ASSOC) ) { 87 | array_push( $rows, $row ); 88 | } 89 | 90 | if ( $cacheExists ) { 91 | $cached = array(); 92 | $cached['function'] = $callingFunc; 93 | $cached['data'] = $rows; 94 | 95 | $cache->set($cached); 96 | } 97 | 98 | return $rows; 99 | } 100 | 101 | public static function doWriteQuery($sql) { 102 | $conn = SQL::getWriteConnection(); 103 | if ( self::CONNECTION_ERROR == $conn ) { 104 | return null; 105 | } 106 | 107 | $result = mysql_query($sql, $conn); 108 | 109 | return $result; 110 | } 111 | 112 | public static function getLastInsertId() { 113 | $conn = SQL::getWriteConnection(); 114 | return mysql_insert_id($conn); 115 | } 116 | 117 | public static function makeSafe($val) { 118 | $conn = SQL::getReadConnection(); 119 | if ( self::CONNECTION_ERROR == $conn ) { 120 | return null; 121 | } 122 | 123 | return mysql_real_escape_string($val, $conn); 124 | } 125 | 126 | } 127 | 128 | ?> -------------------------------------------------------------------------------- /api/Twitter.php: -------------------------------------------------------------------------------- 1 | 7 | * This implementation is _not_ fully tested, but it does definitely work 8 | * for the functionality required by the EchoTwitBot and RssTwitBot. 9 | * 10 | * @author Neil Crosby, neil@twitbot.com 11 | **/ 12 | class Twitter { 13 | 14 | const OK = 200; 15 | const NOT_MODIFIED = 304; 16 | const BAD_REQUEST = 400; 17 | const NOT_AUTHORIZED = 401; 18 | const FORBIDDEN = 403; 19 | const NOT_FOUND = 404; 20 | const INTERNAL_SERVER_ERROR = 500; 21 | const BAD_GATEWAY = 502; 22 | const SERVICE_UNAVAILABLE = 503; 23 | 24 | var $status = null; 25 | var $user = null; 26 | var $directMessages = null; 27 | var $friendship = null; 28 | var $account = null; 29 | var $favorite = null; 30 | var $notification = null; 31 | 32 | function __construct( $username=null, $password=null, $apiPath="http://twitter.com/" ) { 33 | if ( !$username || !$password || !$apiPath ) { 34 | return; 35 | } 36 | 37 | $this->status = new TwitterStatus( $username, $password, $apiPath ); 38 | $this->user = new TwitterUser( $username, $password, $apiPath ); 39 | $this->directMessages = new TwitterDirectMessages( $username, $password, $apiPath ); 40 | $this->friendship = new TwitterFriendship( $username, $password, $apiPath ); 41 | $this->account = new TwitterAccount( $username, $password, $apiPath ); 42 | $this->favorite = new TwitterFavorite( $username, $password, $apiPath ); 43 | $this->notification = new TwitterNotification( $username, $password, $apiPath ); 44 | } 45 | } 46 | 47 | class TwitterBase { 48 | function __construct( $username=null, $password=null, $apiPath=null ) { 49 | if ( !$username || !$password || !$apiPath ) { 50 | return; 51 | } 52 | 53 | $this->username = $username; 54 | $this->password = $password; 55 | $this->apiPath = $apiPath; 56 | } 57 | 58 | protected function getAsQueryString( $array = array() ) { 59 | 60 | if ( !is_array($array) ) { 61 | return ''; 62 | } 63 | 64 | $items = array(); 65 | 66 | foreach ( $array as $key => $value ) { 67 | array_push( $items, urlencode( $key ).'='.urlencode( $value ) ); 68 | } 69 | 70 | $return = implode( '&', $items ); 71 | return $return; 72 | } 73 | 74 | protected function curl( $url, $aOptions = array() ) { 75 | $session = curl_init(); 76 | 77 | curl_setopt( $session, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 78 | curl_setopt( $session, CURLOPT_USERPWD, $this->username . ":" . $this->password); 79 | curl_setopt( $session, CURLOPT_URL, $this->apiPath . $url ); 80 | curl_setopt( $session, CURLOPT_HEADER, false ); 81 | curl_setopt( $session, CURLOPT_RETURNTRANSFER, 1 ); 82 | 83 | foreach ( $aOptions as $key => $value ) { 84 | curl_setopt( $session, $key, $value ); 85 | } 86 | 87 | $result = curl_exec( $session ); 88 | 89 | curl_close( $session ); 90 | 91 | if ( !isset($result) ) { 92 | $result = false; 93 | } 94 | 95 | error_log($this->apiPath . $url); 96 | error_log("HTTP CODE: ".curl_getinfo($session, CURLINFO_HTTP_CODE)); 97 | 98 | return $result; 99 | } 100 | } 101 | 102 | class TwitterStatus extends TwitterBase { 103 | function __construct( $username=null, $password=null, $apiPath=null ) { 104 | parent::__construct( $username, $password, $apiPath ); 105 | } 106 | 107 | public function public_timeline( $aOptions = array() ) { 108 | // since_id 109 | $queryString = $this->getAsQueryString( $aOptions ); 110 | $messages = $this->curl("statuses/public_timeline.json?$queryString"); 111 | return json_decode($messages); 112 | } 113 | 114 | public function friends_timeline( $aOptions = array() ) { 115 | // id 116 | // since 117 | // page 118 | $queryString = $this->getAsQueryString( $aOptions ); 119 | $messages = $this->curl("statuses/friends_timeline.json?$queryString"); 120 | return json_decode($messages); 121 | } 122 | 123 | public function user_timeline( $aOptions = array() ) { 124 | // id 125 | // count 126 | // since 127 | $queryString = $this->getAsQueryString( $aOptions ); 128 | $messages = $this->curl("statuses/user_timeline.json?$queryString"); 129 | return json_decode($messages); 130 | } 131 | 132 | public function show( $id ) { 133 | $id = urlencode($id); 134 | $messages = $this->curl("statuses/show/$id.json"); 135 | return json_decode($messages); 136 | } 137 | 138 | public function update( $status ) { 139 | $status = urlencode( $status ); 140 | 141 | $this->curl( 142 | "statuses/update.xml", 143 | array( 144 | CURLOPT_POST => 1, 145 | CURLOPT_POSTFIELDS => "status=" . $status, 146 | CURLOPT_TIMEOUT => 1, 147 | ) 148 | ); 149 | } 150 | 151 | public function replies( $aOptions = array() ) { 152 | // page 153 | $queryString = $this->getAsQueryString( $aOptions ); 154 | $messages = $this->curl("statuses/replies.json?$queryString"); 155 | return json_decode($messages); 156 | } 157 | 158 | public function destroy( $id ) { 159 | $id = urlencode($id); 160 | $messages = $this->curl("statuses/destroy/$id.json"); 161 | return json_decode($messages); 162 | } 163 | 164 | } 165 | 166 | class TwitterUser extends TwitterBase { 167 | function __construct( $username=null, $password=null, $apiPath=null ) { 168 | parent::__construct( $username, $password, $apiPath ); 169 | } 170 | 171 | public function friends( $aOptions = array() ) { 172 | // id 173 | // page 174 | $queryString = $this->getAsQueryString( $aOptions ); 175 | $messages = $this->curl("statuses/friends.json?$queryString"); 176 | return json_decode($messages); 177 | } 178 | 179 | public function followers( $aOptions = array() ) { 180 | // lite 181 | $queryString = $this->getAsQueryString( $aOptions ); 182 | $messages = $this->curl("statuses/followers.json?$queryString"); 183 | return json_decode($messages); 184 | } 185 | 186 | public function featured() { 187 | $messages = $this->curl("statuses/featured.json"); 188 | return json_decode($messages); 189 | } 190 | 191 | public function show( $id, $aOptions = array() ) { 192 | // email 193 | $id = urlencode($id); 194 | $queryString = $this->getAsQueryString( $aOptions ); 195 | $messages = $this->curl("users/show/$id.json?$queryString"); 196 | return json_decode($messages); 197 | } 198 | 199 | } 200 | 201 | class TwitterDirectMessages extends TwitterBase { 202 | function __construct( $username=null, $password=null, $apiPath=null ) { 203 | parent::__construct( $username, $password, $apiPath ); 204 | } 205 | 206 | public function direct_messages( $aOptions = array() ) { 207 | // since 208 | // since_id 209 | // page 210 | $queryString = $this->getAsQueryString( $aOptions ); 211 | $messages = $this->curl("direct_messages.json?$queryString"); 212 | return json_decode($messages); 213 | } 214 | 215 | public function sent( $aOptions = array() ) { 216 | // since 217 | // since_id 218 | // page 219 | $queryString = $this->getAsQueryString( $aOptions ); 220 | $messages = $this->curl("direct_messages/sent.json?$queryString"); 221 | return json_decode($messages); 222 | } 223 | 224 | public function new_message( $user, $text ) { 225 | $user = urlencode($user); 226 | $text = urlencode($text); 227 | echo "about to curl new message"; 228 | $this->curl( 229 | "direct_messages/new.json", 230 | array( 231 | CURLOPT_POST => 1, 232 | CURLOPT_POSTFIELDS => "user=$user&text=$text", 233 | CURLOPT_TIMEOUT => 1, 234 | ) 235 | ); 236 | } 237 | 238 | public function destroy( $id ) { 239 | $id = urlencode($id); 240 | $messages = $this->curl("direct_messages/destroy/$id.json"); 241 | return json_decode($messages); 242 | } 243 | 244 | } 245 | 246 | class TwitterFriendship extends TwitterBase { 247 | function __construct( $username=null, $password=null, $apiPath=null ) { 248 | parent::__construct( $username, $password, $apiPath ); 249 | } 250 | 251 | public function create( $id ) { 252 | $id = urlencode($id); 253 | $friend = $this->curl("friendships/create/$id.json"); 254 | return json_decode($friend); 255 | } 256 | 257 | public function destroy( $id ) { 258 | $id = urlencode($id); 259 | $friend = $this->curl("friendships/destroy/$id.json"); 260 | return json_decode($friend); 261 | } 262 | 263 | } 264 | 265 | class TwitterAccount extends TwitterBase { 266 | function __construct( $username=null, $password=null, $apiPath=null ) { 267 | parent::__construct( $username, $password, $apiPath ); 268 | } 269 | 270 | public function verify_credentials() { 271 | $messages = $this->curl("account/verify_creditials.json"); 272 | return json_decode($messages); 273 | // TODO: need to do something a bit more special to see if this returned okay 274 | } 275 | 276 | public function end_session() { 277 | $this->curl("account/end_session"); 278 | } 279 | 280 | public function archive( $aOptions = array() ) { 281 | $queryString = $this->getAsQueryString( $aOptions ); 282 | $messages = $this->curl("account/archive.json?$queryString"); 283 | return json_decode($messages); 284 | } 285 | 286 | } 287 | 288 | class TwitterFavorite extends TwitterBase { 289 | function __construct( $username=null, $password=null, $apiPath=null ) { 290 | parent::__construct( $username, $password, $apiPath ); 291 | } 292 | 293 | public function favorites( $aOptions = array() ) { 294 | // The API definition of this function is weird=ass 295 | 296 | // id 297 | // page 298 | $user = ''; 299 | if ( isset( $aOptions['id'] ) ) { 300 | $user = '/'.urlencode($aOptions['id']); 301 | $aOptions['id'] = null; 302 | } 303 | 304 | $queryString = $this->getAsQueryString( $aOptions ); 305 | $messages = $this->curl("favorites$user.json?$queryString"); 306 | return json_decode($messages); 307 | 308 | } 309 | 310 | public function create( $id ) { 311 | $id = urlencode($id); 312 | $messages = $this->curl("favorites/create/$id.json"); 313 | return json_decode($messages); 314 | } 315 | 316 | public function destroy( $id ) { 317 | $id = urlencode($id); 318 | $messages = $this->curl("favourites/destroy/$id.json"); 319 | return json_decode($messages); 320 | } 321 | 322 | } 323 | 324 | class TwitterNotification extends TwitterBase { 325 | function __construct( $username=null, $password=null, $apiPath=null ) { 326 | parent::__construct( $username, $password, $apiPath ); 327 | } 328 | 329 | public function follow( $id ) { 330 | $id = urlencode($id); 331 | $messages = $this->curl("notifications/follow/$id.json"); 332 | return json_decode($messages); 333 | } 334 | 335 | public function leave( $id ) { 336 | $id = urlencode($id); 337 | $messages = $this->curl("notifications/leave/$id.json"); 338 | return json_decode($messages); 339 | } 340 | 341 | } 342 | 343 | ?> --------------------------------------------------------------------------------