]> git.mxchange.org Git - friendica-addons.git/blobdiff - twitter/twitter.php
twitter: It is now possible to mirror own postings from twitter to friendica
[friendica-addons.git] / twitter / twitter.php
index ef5908396cb393cac3db1bfd88242ea73fa96aa1..ad6577992f46dcaf04ff8483b23d16a6059e7892 100755 (executable)
@@ -1,8 +1,9 @@
 <?php
 /**
  * Name: Twitter Connector
- * Version: 1.0.1
- * Author: Tobias Diekershoff <https://diekershoff.homeunix.net/friendika/profile/tobias>
+ * Description: Relay public postings to a connected Twitter account
+ * Version: 1.0.4
+ * Author: Tobias Diekershoff <http://diekershoff.homeunix.net/friendika/profile/tobias>
  */
 
 
  *     Documentation: http://diekershoff.homeunix.net/redmine/wiki/friendikaplugin/Twitter_Plugin
  */
 
-/*   __TODO__
- *
- *   - what about multimedia content?
- *     so far we just strip HTML tags from the message
- */
+define('TWITTER_DEFAULT_POLL_INTERVAL', 30); // given in minutes
 
 function twitter_install() {
        //  we need some hooks, for the configuration and for sending tweets
@@ -48,6 +45,7 @@ function twitter_install() {
        register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
        register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
        register_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
+       register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
        logger("installed twitter");
 }
 
@@ -58,6 +56,7 @@ function twitter_uninstall() {
        unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
        unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
        unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
+       unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
 
        // old setting - remove only
        unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook');
@@ -77,8 +76,6 @@ function twitter_jot_nets(&$a,&$b) {
                $b .= '<div class="profile-jot-net"><input type="checkbox" name="twitter_enable"' . $selected . ' value="1" /> ' 
                        . t('Post to Twitter') . '</div>';      
        }
-
-
 }
 
 function twitter_settings_post ($a,$post) {
@@ -92,19 +89,23 @@ function twitter_settings_post ($a,$post) {
                 * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
                 * from the user configuration
                 */
-               del_pconfig( local_user(), 'twitter', 'consumerkey'  );
-               del_pconfig( local_user(), 'twitter', 'consumersecret' );
-                del_pconfig( local_user(), 'twitter', 'oauthtoken'  );  
-                del_pconfig( local_user(), 'twitter', 'oauthsecret'  );  
-                del_pconfig( local_user(), 'twitter', 'post' );
-                del_pconfig( local_user(), 'twitter', 'post_by_default' );
+               del_pconfig(local_user(), 'twitter', 'consumerkey');
+               del_pconfig(local_user(), 'twitter', 'consumersecret');
+                del_pconfig(local_user(), 'twitter', 'oauthtoken');
+                del_pconfig(local_user(), 'twitter', 'oauthsecret');
+                del_pconfig(local_user(), 'twitter', 'post');
+                del_pconfig(local_user(), 'twitter', 'post_by_default');
+                del_pconfig(local_user(), 'twitter', 'post_taglinks');
+               del_pconfig(local_user(), 'twitter', 'lastid');
+               del_pconfig(local_user(), 'twitter', 'mirror_posts');
+               del_pconfig(local_user(), 'twitter', 'intelligent_shortening');
        } else {
        if (isset($_POST['twitter-pin'])) {
                //  if the user supplied us with a PIN from Twitter, let the magic of OAuth happen
                logger('got a Twitter PIN');
                require_once('library/twitteroauth.php');
-               $ckey    = get_config('twitter', 'consumerkey'  );
-               $csecret = get_config('twitter', 'consumersecret' );
+               $ckey    = get_config('twitter', 'consumerkey');
+               $csecret = get_config('twitter', 'consumersecret');
                //  the token and secret for which the PIN was generated were hidden in the settings
                //  form as token and token2, we need a new connection to Twitter using these token
                //  and secret to request a Access Token with the PIN
@@ -114,6 +115,7 @@ function twitter_settings_post ($a,$post) {
                set_pconfig(local_user(),'twitter', 'oauthtoken',  $token['oauth_token']);
                set_pconfig(local_user(),'twitter', 'oauthsecret', $token['oauth_token_secret']);
                 set_pconfig(local_user(),'twitter', 'post', 1);
+                set_pconfig(local_user(),'twitter', 'post_taglinks', 1);
                 //  reload the Addon Settings page, if we don't do it see Bug #42
                 goaway($a->get_baseurl().'/settings/connectors');
        } else {
@@ -121,6 +123,9 @@ function twitter_settings_post ($a,$post) {
                //  to post a tweet for every new __public__ posting to the wall
                set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable']));
                 set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default']));
+                set_pconfig(local_user(),'twitter','post_taglinks',intval($_POST['twitter-sendtaglinks']));
+               set_pconfig(local_user(), 'twitter', 'mirror_posts', intval($_POST['twitter-mirror']));
+               set_pconfig(local_user(), 'twitter', 'intelligent_shortening', intval($_POST['twitter-shortening']));
                 info( t('Twitter settings updated.') . EOL);
        }}
 }
@@ -141,6 +146,12 @@ function twitter_settings(&$a,&$s) {
        $checked = (($enabled) ? ' checked="checked" ' : '');
         $defenabled = get_pconfig(local_user(),'twitter','post_by_default');
        $defchecked = (($defenabled) ? ' checked="checked" ' : '');
+        $linksenabled = get_pconfig(local_user(),'twitter','post_taglinks');
+        $linkschecked = (($linksenabled) ? ' checked="checked" ' : '');
+        $mirrorenabled = get_pconfig(local_user(),'twitter','mirror_posts');
+        $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : '');
+        $shorteningenabled = get_pconfig(local_user(),'twitter','intelligent_shortening');
+        $shorteningchecked = (($shorteningenabled) ? ' checked="checked" ' : '');
 
        $s .= '<div class="settings-block">';
        $s .= '<h3>'. t('Twitter Posting Settings') .'</h3>';
@@ -188,12 +199,27 @@ function twitter_settings(&$a,&$s) {
                        $details = $connection->get('account/verify_credentials');
                        $s .= '<div id="twitter-info" ><img id="twitter-avatar" src="'.$details->profile_image_url.'" /><p id="twitter-info-block">'. t('Currently connected to: ') .'<a href="https://twitter.com/'.$details->screen_name.'" target="_twitter">'.$details->screen_name.'</a><br /><em>'.$details->description.'</em></p></div>';
                        $s .= '<p>'. t('If enabled all your <strong>public</strong> postings can be posted to the associated Twitter account. You can choose to do so by default (here) or for every posting separately in the posting options when writing the entry.') .'</p>';
+                        if ($a->user['hidewall']) {
+                            $s .= '<p>'. t('<strong>Note</strong>: Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted.') .'</p>';
+                        }
                        $s .= '<div id="twitter-enable-wrapper">';
                        $s .= '<label id="twitter-enable-label" for="twitter-checkbox">'. t('Allow posting to Twitter'). '</label>';
                        $s .= '<input id="twitter-checkbox" type="checkbox" name="twitter-enable" value="1" ' . $checked . '/>';
                         $s .= '<div class="clear"></div>';
                         $s .= '<label id="twitter-default-label" for="twitter-default">'. t('Send public postings to Twitter by default') .'</label>';
                         $s .= '<input id="twitter-default" type="checkbox" name="twitter-default" value="1" ' . $defchecked . '/>';
+                       $s .= '<div class="clear"></div>';
+
+                        $s .= '<label id="twitter-mirror-label" for="twitter-mirror">'.t('Mirror all posts from twitter that are no replies or retweets').'</label>';
+                        $s .= '<input id="twitter-mirror" type="checkbox" name="twitter-mirror" value="1" '. $mirrorchecked . '/>';
+                       $s .= '<div class="clear"></div>';
+
+                        $s .= '<label id="twitter-shortening-label" for="twitter-shortening">'.t('Shortening method that optimizes the tweet').'</label>';
+                        $s .= '<input id="twitter-shortening" type="checkbox" name="twitter-shortening" value="1" '. $shorteningchecked . '/>';
+                       $s .= '<div class="clear"></div>';
+
+                        $s .= '<label id="twitter-sendtaglinks-label" for="twitter-sendtaglinks">'.t('Send linked #-tags and @-names to Twitter').'</label>';
+                        $s .= '<input id="twitter-sendtaglinks" type="checkbox" name="twitter-sendtaglinks" value="1" '. $linkschecked . '/>';
                        $s .= '</div><div class="clear"></div>';
 
                        $s .= '<div id="twitter-disconnect-wrapper">';
@@ -203,7 +229,7 @@ function twitter_settings(&$a,&$s) {
                        $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="twitter-submit" class="settings-submit" value="' . t('Submit') . '" /></div>'; 
                }
        }
-        $s .= '</div><div class="clear"></div></div>';
+        $s .= '</div><div class="clear"></div>';
 }
 
 
@@ -230,6 +256,139 @@ function twitter_post_local(&$a,&$b) {
        }
 }
 
+if (! function_exists('short_link')) {
+function short_link ($url) {
+    require_once('library/slinky.php');
+    $slinky = new Slinky( $url );
+    $yourls_url = get_config('yourls','url1');
+    if ($yourls_url) {
+            $yourls_username = get_config('yourls','username1');
+            $yourls_password = get_config('yourls', 'password1');
+            $yourls_ssl = get_config('yourls', 'ssl1');
+            $yourls = new Slinky_YourLS();
+            $yourls->set( 'username', $yourls_username );
+            $yourls->set( 'password', $yourls_password );
+            $yourls->set( 'ssl', $yourls_ssl );
+            $yourls->set( 'yourls-url', $yourls_url );
+            $slinky->set_cascade( array( $yourls, new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
+    }
+    else {
+            // setup a cascade of shortening services
+            // try to get a short link from these services
+            // in the order ur1.ca, trim, id.gd, tinyurl
+            $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
+    }
+    return $slinky->short();
+} };
+
+function twitter_shortenmsg($b) {
+       require_once("include/bbcode.php");
+       require_once("include/html2plain.php");
+
+       $max_char = 140;
+
+       // Looking for the first image
+       $image = '';
+       if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$b['body'],$matches))
+               $image = $matches[3];
+
+       if ($image == '')
+               if(preg_match("/\[img\](.*?)\[\/img\]/is",$b['body'],$matches))
+                       $image = $matches[1];
+
+       $multipleimages = (strpos($b['body'], "[img") != strrpos($b['body'], "[img"));
+
+       // When saved into the database the content is sent through htmlspecialchars
+       // That means that we have to decode all image-urls
+       $image = htmlspecialchars_decode($image);
+
+       $body = $b["body"];
+       if ($b["title"] != "")
+               $body = $b["title"]."\n\n".$body;
+
+       // remove the recycle signs and the names since they aren't helpful on twitter
+       // recycle 1
+       $recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
+       $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
+       // recycle 2 (Test)
+       $recycle = html_entity_decode("&#x25CC; ", ENT_QUOTES, 'UTF-8');
+       $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
+
+       // remove the share element
+       $body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body);
+
+       // At first convert the text to html
+       $html = bbcode($body, false, false);
+
+       // Then convert it to plain text
+       //$msg = trim($b['title']." \n\n".html2plain($html, 0, true));
+       $msg = trim(html2plain($html, 0, true));
+       $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
+
+       // Removing multiple newlines
+       while (strpos($msg, "\n\n\n") !== false)
+               $msg = str_replace("\n\n\n", "\n\n", $msg);
+
+       // Removing multiple spaces
+       while (strpos($msg, "  ") !== false)
+               $msg = str_replace("  ", " ", $msg);
+
+       // Removing URLs
+       $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg);
+
+       $msg = trim($msg);
+
+       $link = '';
+       // look for bookmark-bbcode and handle it with priority
+       if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches))
+               $link = $matches[1];
+
+       $multiplelinks = (strpos($b['body'], "[bookmark") != strrpos($b['body'], "[bookmark"));
+
+       // If there is no bookmark element then take the first link
+       if ($link == '') {
+               $links = collecturls($html);
+               if (sizeof($links) > 0) {
+                       reset($links);
+                       $link = current($links);
+               }
+               $multiplelinks = (sizeof($links) > 1);
+       }
+
+       $msglink = "";
+       if ($multiplelinks)
+               $msglink = $b["plink"];
+       else if ($link != "")
+               $msglink = $link;
+       else if ($multipleimages)
+               $msglink = $b["plink"];
+       else if ($image != "")
+               $msglink = $image;
+
+       if (($msglink == "") and strlen($msg) > $max_char)
+               $msglink = $b["plink"];
+
+       if (strlen($msglink) > 20)
+               $msglink = short_link($msglink);
+
+       if (strlen(trim($msg." ".$msglink)) > $max_char) {
+               $msg = substr($msg, 0, $max_char - (strlen($msglink)));
+               $lastchar = substr($msg, -1);
+               $msg = substr($msg, 0, -1);
+               $pos = strrpos($msg, "\n");
+               if ($pos > 0)
+                       $msg = substr($msg, 0, $pos);
+               else if ($lastchar != "\n")
+                       $msg = substr($msg, 0, -3)."...";
+       }
+       $msg = str_replace("\n", " ", $msg);
+
+       // Removing multiple spaces - again
+       while (strpos($msg, "  ") !== false)
+               $msg = str_replace("  ", " ", $msg);
+
+       return(trim($msg." ".$msglink));
+}
 
 function twitter_post_hook(&$a,&$b) {
 
@@ -246,58 +405,119 @@ function twitter_post_hook(&$a,&$b) {
        if($b['parent'] != $b['id'])
                return;
 
+       // if posts comes from twitter don't send it back
+       if($b['app'] == "twitter.com")
+               return;
+
        logger('twitter post invoked');
 
 
        load_pconfig($b['uid'], 'twitter');
 
-       $ckey    = get_config('twitter', 'consumerkey'  );
-       $csecret = get_config('twitter', 'consumersecret' );
-       $otoken  = get_pconfig($b['uid'], 'twitter', 'oauthtoken'  );
-       $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret' );
+       $ckey    = get_config('twitter', 'consumerkey');
+       $csecret = get_config('twitter', 'consumersecret');
+       $otoken  = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
+       $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
+       $intelligent_shortening = get_pconfig($b['uid'], 'twitter', 'intelligent_shortening');
+
+       // Global setting overrides this
+       if (get_config('twitter','intelligent_shortening'))
+                $intelligent_shortening = get_config('twitter','intelligent_shortening');
 
        if($ckey && $csecret && $otoken && $osecret) {
                logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
 
                require_once('library/twitteroauth.php');
-               require_once('include/bbcode.php');     
+               require_once('include/bbcode.php');
                $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
-               $max_char = 138; // max. length for a tweet
-               $msg = strip_tags(bbcode($b['body']));
-               if ( strlen($msg) > $max_char) {
-                       logger('Twitter: have to shorten the message to fit 140 chars', LOGGER_DEBUG);
-                       $shortlink = "";
-                       require_once('library/slinky.php');
-                       $slinky = new Slinky( $b['plink'] );
-                       $yourls_url = get_config('yourls','url1');
-                       if ($yourls_url) {
-                               $max_char = 135;
-                               $yourls_username = get_config('yourls','username1');
-                               $yourls_password = get_config('yourls', 'password1');
-                               $yourls_ssl = get_config('yourls', 'ssl1');
-                               $yourls = new Slinky_YourLS();
-                               $yourls->set( 'username', $yourls_username );
-                               $yourls->set( 'password', $yourls_password );
-                               $yourls->set( 'ssl', $yourls_ssl );
-                               $yourls->set( 'yourls-url', $yourls_url );
-                               $slinky->set_cascade( array( $yourls, new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
+                // in theory max char is 140 but T. uses t.co to make links 
+                // longer so we give them 10 characters extra
+               if (!$intelligent_shortening) {
+                       $max_char = 130; // max. length for a tweet
+                       // we will only work with up to two times the length of the dent 
+                       // we can later send to Twitter. This way we can "gain" some 
+                       // information during shortening of potential links but do not 
+                       // shorten all the links in a 200000 character long essay.
+                       if (! $b['title']=='') {
+                           $tmp = $b['title'] . ' : '. $b['body'];
+       //                    $tmp = substr($tmp, 0, 4*$max_char);
+                       } else {
+                           $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char);
+                       }
+                       // if [url=bla][img]blub.png[/img][/url] get blub.png
+                       $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp);
+                       // preserve links to images, videos and audios
+                       $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp);
+                       $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp);
+                       $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp);
+                       $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp);
+                       $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp);
+                       $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp);
+                       $linksenabled = get_pconfig($b['uid'],'twitter','post_taglinks');
+                       // if a #tag is linked, don't send the [url] over to SN
+                       // that is, don't send if the option is not set in the
+                       // connector settings
+                       if ($linksenabled=='0') {
+                               // #-tags
+                               $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp);
+                               // @-mentions
+                               $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp);
+                               // recycle 1
+                               $recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
+                               $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
+                               // recycle 2 (Test)
+                               $recycle = html_entity_decode("&#x25CC; ", ENT_QUOTES, 'UTF-8');
+                               $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
+                       }
+                       $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp);
+                       $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp);
+                       // find all http or https links in the body of the entry and
+                       // apply the shortener if the link is longer then 20 characters
+                       if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) {
+                           preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls  );
+                           foreach ($allurls as $url) {
+                               foreach ($url as $u) {
+                                   if (strlen($u)>20) {
+                                       $sl = short_link($u);
+                                       $tmp = str_replace( $u, $sl, $tmp );
+                                   }
+                               }
+                           }
+                       }
+                       // ok, all the links we want to send out are save, now strip 
+                       // away the remaining bbcode
+                       //$msg = strip_tags(bbcode($tmp, false, false));
+                       $msg = bbcode($tmp, false, false);
+                       $msg = str_replace(array('<br>','<br />'),"\n",$msg);
+                       $msg = strip_tags($msg);
+
+                       // quotes not working - let's try this
+                       $msg = html_entity_decode($msg);
+                       if (( strlen($msg) > $max_char) && $max_char > 0) {
+                               $shortlink = short_link( $b['plink'] );
+                               // the new message will be shortened such that "... $shortlink"
+                               // will fit into the character limit
+                               $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4));
+                               $msg = str_replace(array('<br>','<br />'),' ',$msg);
+                               $e = explode(' ', $msg);
+                               //  remove the last word from the cut down message to 
+                               //  avoid sending cut words to the MicroBlog
+                               array_pop($e);
+                               $msg = implode(' ', $e);
+                               $msg .= '... ' . $shortlink;
                        }
-                       else {
-                               // setup a cascade of shortening services
-                               // try to get a short link from these services
-                               // in the order ur1.ca, trim, id.gd, tinyurl
-                               $slinky->set_cascade( array( new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL() ) );
-                       }
-                       $shortlink = $slinky->short();
-                       // the new message will be shortened such that "... $shortlink"
-                       // will fit into the character limit
-                       $msg = substr($msg, 0, $max_char-strlen($shortlink)-4);
-                       $msg .= '... ' . $shortlink;
-               }
+
+                       $msg = trim($msg);
+               } else
+                       $msg = twitter_shortenmsg($b);
+
                // and now tweet it :-)
                if(strlen($msg)) {
                        $result = $tweet->post('statuses/update', array('status' => $msg));
-                       logger('twitter_post send' , LOGGER_DEBUG);
+                       logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
+                       if ($result->error) {
+                               logger('Send to Twitter failed: "' . $result->error . '"');
+                       }
                }
        }
 }
@@ -310,11 +530,103 @@ function twitter_plugin_admin_post(&$a){
        info( t('Settings updated.'). EOL );
 }
 function twitter_plugin_admin(&$a, &$o){
-       $t = file_get_contents( dirname(__file__). "/admin.tpl" );
-       $o = replace_macros($t, array(
+       $t = get_markup_template( "admin.tpl", "addon/twitter/" );
+
+       $includes = array(
+               '$field_input' => 'field_input.tpl',
+       );
+       //$includes = set_template_includes($a->theme['template_engine'], $includes);
+
+       $o = replace_macros($t, $includes + array(
                '$submit' => t('Submit'),
                                                                // name, label, value, help, [extra values]
                '$consumerkey' => array('consumerkey', t('Consumer key'),  get_config('twitter', 'consumerkey' ), ''),
                '$consumersecret' => array('consumersecret', t('Consumer secret'),  get_config('twitter', 'consumersecret' ), '')
        ));
 }
+
+function twitter_cron($a,$b) {
+       $last = get_config('twitter','last_poll');
+
+       $poll_interval = intval(get_config('twitter','poll_interval'));
+       if(! $poll_interval)
+               $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
+
+       if($last) {
+               $next = $last + ($poll_interval * 60);
+               if($next > time()) {
+                       logger('twitter: poll intervall not reached');
+                       return;
+               }
+       }
+       logger('twitter: cron_start');
+
+       $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND() ");
+       if(count($r)) {
+               foreach($r as $rr) {
+                       logger('twitter: fetching for user '.$rr['uid']);
+                       twitter_fetchtimeline($a, $rr['uid']);
+               }
+       }
+
+       logger('twitter: cron_end');
+
+       set_config('twitter','last_poll', time());
+}
+
+function twitter_fetchtimeline($a, $uid) {
+       $ckey    = get_config('twitter', 'consumerkey');
+       $csecret = get_config('twitter', 'consumersecret');
+       $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
+       $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
+       $lastid  = get_pconfig($uid, 'twitter', 'lastid');
+
+       require_once('library/twitteroauth.php');
+       $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
+
+       $parameters = array("exclude_replies" => true, "trim_user" => true, "contributor_details" => false, "include_rts" => false);
+
+       if ($lastid <> "")
+               $parameters["since_id"] = $lastid;
+
+       $items = $connection->get('statuses/user_timeline', $parameters);
+       $posts = array_reverse($items);
+
+       foreach ($posts as $post) {
+               if ($post->id_str > $lastid)
+                       $lastid = $post->id_str;
+
+               if (!strpos($post->source, $a->get_hostname())) {
+                       $_SESSION["authenticated"] = true;
+                       $_SESSION["uid"] = $uid;
+
+                       $_REQUEST["type"] = "wall";
+                       $_REQUEST["api_source"] = true;
+                       $_REQUEST["profile_uid"] = $uid;
+                       $_REQUEST["source"] = "twitter.com";
+
+                       //$_REQUEST["date"] = $post->created_at;
+
+                       $_REQUEST["body"] = $post->text;
+                       if (is_string($post->place->name))
+                               $_REQUEST["location"] = $post->place->name;
+
+                       if (is_string($post->place->full_name))
+                               $_REQUEST["location"] = $post->place->full_name;
+
+                       if (is_array($post->geo->coordinates))
+                               $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
+
+                       if (is_array($post->coordinates->coordinates))
+                               $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
+
+                       //print_r($_REQUEST);
+                       logger('twitter: posting for user '.$uid);
+
+                       require_once('mod/item.php');
+                       item_post($a);
+
+               }
+       }
+       set_pconfig($uid, 'twitter', 'lastid', $lastid);
+}