forked from Chaospott/site
234 lines
9.0 KiB
PHP
234 lines
9.0 KiB
PHP
|
<?php
|
||
|
|
||
|
// Create an "app" from the Twitter account you want to post to:
|
||
|
// https://dev.twitter.com/
|
||
|
//
|
||
|
// From your app's Details page, request an Access Token (bottom).
|
||
|
// Reload periodically until you have it.
|
||
|
// Paste in your values for these strings:
|
||
|
|
||
|
class TwitterPostHookCredentials
|
||
|
{
|
||
|
public static $consumer_key = '';
|
||
|
public static $consumer_secret = '';
|
||
|
public static $access_token = '';
|
||
|
public static $access_token_secret = '';
|
||
|
}
|
||
|
|
||
|
// Test your credentials by executing this file alone from the command line:
|
||
|
// php -f post_twitter.php
|
||
|
|
||
|
// Edit this function to customize the formatting of the posted tweets.
|
||
|
// The $post array is just like the $content['post'] arrays in the templates.
|
||
|
// You can even change the posted URL by overwriting $url_to_use.
|
||
|
//
|
||
|
// The string returned by this function will have a space and URL appended.
|
||
|
// $max_characters accounts for this, and is the most UTF-8 characters YOU can
|
||
|
// return from this function.
|
||
|
//
|
||
|
// The twitter_summarize($text, $max_characters) function is available. It
|
||
|
// will trim a string at the nearest word boundary and append an ellipsis to
|
||
|
// fit within the number of characters if it's too long.
|
||
|
//
|
||
|
function tweet_text_before_url_for_post(array $post, $max_characters, &$url_to_use)
|
||
|
{
|
||
|
$title = $post['post-title'];
|
||
|
if (isset($post['link'])) $title = "\xE2\x86\x92 " . $title; // right arrow
|
||
|
return twitter_summarize($title, $max_characters);
|
||
|
}
|
||
|
|
||
|
|
||
|
// ======== You probably don't want to edit anything below this line =========
|
||
|
|
||
|
class ExtremelyBasicSelfContainedOAuthConsumer
|
||
|
{
|
||
|
public $consumer_key;
|
||
|
public $consumer_secret;
|
||
|
public $token = '';
|
||
|
public $token_secret = '';
|
||
|
|
||
|
public $request_timeout_seconds = 10;
|
||
|
|
||
|
public static function hmac_sha1($data, $key)
|
||
|
{
|
||
|
if (strlen($key) > 64) $key = pack('H40', sha1($key));
|
||
|
if (strlen($key) < 64) $key = str_pad($key, 64, chr(0));
|
||
|
$ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
|
||
|
$opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
|
||
|
return sha1($opad . pack('H40', sha1($ipad . $data)), true);
|
||
|
}
|
||
|
|
||
|
public function signature($request_method, $target_url, $post_params = array())
|
||
|
{
|
||
|
if ( ($qs = @parse_url($target_url, PHP_URL_QUERY)) ) {
|
||
|
parse_str($qs, $get_params);
|
||
|
$post_params = array_merge($get_params, $post_params);
|
||
|
}
|
||
|
if (isset($post_params['oauth_signature'])) unset($post_params['oauth_signature']);
|
||
|
ksort($post_params);
|
||
|
|
||
|
if (false !== ($cutpos = strpos($target_url, '?')) ) $target_url = substr($target_url, 0, $cutpos);
|
||
|
$parts = array($request_method, rawurlencode($target_url));
|
||
|
$vars = array();
|
||
|
foreach ($post_params as $name => $val) $vars[] = rawurlencode($name) . '=' . rawurlencode($val);
|
||
|
$parts[] = rawurlencode(implode('&', $vars));
|
||
|
$signature_base_string = implode('&', $parts);
|
||
|
return base64_encode(self::hmac_sha1($signature_base_string, $this->consumer_secret . '&' . ($this->token_secret ? $this->token_secret : '')));
|
||
|
}
|
||
|
|
||
|
public function request_parameters($request_method, $target_url, $post_params = array())
|
||
|
{
|
||
|
$params = array(
|
||
|
'oauth_consumer_key' => $this->consumer_key,
|
||
|
'oauth_signature_method' => 'HMAC-SHA1',
|
||
|
'oauth_timestamp' => time(),
|
||
|
'oauth_nonce' => sha1($this->consumer_secret . microtime(true) . $this->token_secret),
|
||
|
'oauth_version' => '1.0'
|
||
|
);
|
||
|
if (strlen($this->token)) $params['oauth_token'] = $this->token;
|
||
|
|
||
|
$post_params = array_merge($post_params, $params);
|
||
|
$sig = $this->signature($request_method, $target_url, $post_params);
|
||
|
$params['oauth_signature'] = $sig;
|
||
|
return $params;
|
||
|
}
|
||
|
|
||
|
public function authorization_header_value($oauth_request_parameters)
|
||
|
{
|
||
|
$value = 'OAuth ';
|
||
|
foreach ($oauth_request_parameters as $key => $val) {
|
||
|
$value .= $key . '="' . rawurlencode($val) . '", ';
|
||
|
}
|
||
|
$value = rtrim($value, ', ');
|
||
|
return $value;
|
||
|
}
|
||
|
|
||
|
public function http_build_query_raw(array $arr)
|
||
|
{
|
||
|
$out = array();
|
||
|
foreach ($arr as $name => $val) $out[] = rawurlencode($name) . '=' . rawurlencode($val);
|
||
|
return implode('&', $out);
|
||
|
}
|
||
|
|
||
|
public function request($method, $endpoint, $params = array())
|
||
|
{
|
||
|
$oauth_parameters = $this->request_parameters($method, $endpoint, $params);
|
||
|
$auth = $this->authorization_header_value($oauth_parameters);
|
||
|
|
||
|
$curl = curl_init($endpoint);
|
||
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||
|
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $this->request_timeout_seconds);
|
||
|
curl_setopt($curl, CURLOPT_TIMEOUT, $this->request_timeout_seconds);
|
||
|
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||
|
curl_setopt($curl, CURLOPT_FAILONERROR, 0);
|
||
|
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Authorization: ' . $auth, 'Expect:'));
|
||
|
if ($method == 'POST') {
|
||
|
curl_setopt($curl, CURLOPT_POST, true);
|
||
|
curl_setopt($curl, CURLOPT_POSTFIELDS, self::http_build_query_raw($params));
|
||
|
}
|
||
|
|
||
|
$data = curl_exec($curl);
|
||
|
curl_close($curl);
|
||
|
return $data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function twitter_summarize($text, $length, $ellipsis = "\xE2\x80\xA6" /* UTF-8 … */)
|
||
|
{
|
||
|
if ($length == 0) return $text;
|
||
|
if (mb_strlen($text, 'UTF-8') <= $length) return $text;
|
||
|
|
||
|
$word_delimiter = ' ';
|
||
|
$word_delimiter_mblen = 1;
|
||
|
|
||
|
$length -= mb_strlen($ellipsis, 'UTF-8');
|
||
|
$cut_str = mb_substr($text, 0, $length + $word_delimiter_mblen, 'UTF-8');
|
||
|
if (mb_substr($cut_str, 0 - $word_delimiter_mblen) != $word_delimiter) {
|
||
|
$cut_str = mb_substr($cut_str, 0, 0 - $word_delimiter_mblen);
|
||
|
}
|
||
|
|
||
|
$split_pos = $cut_str ? mb_strrpos($cut_str, $word_delimiter, 'UTF-8') : false;
|
||
|
if ($split_pos) {
|
||
|
return mb_substr($text, 0, $split_pos, 'UTF-8') . $ellipsis;
|
||
|
} else {
|
||
|
return $cut_str . $ellipsis;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function tweet_link_to_post(array $post_array_for_template)
|
||
|
{
|
||
|
$oauth = new ExtremelyBasicSelfContainedOAuthConsumer();
|
||
|
$oauth->consumer_key = TwitterPostHookCredentials::$consumer_key;
|
||
|
$oauth->consumer_secret = TwitterPostHookCredentials::$consumer_secret;
|
||
|
$oauth->token = TwitterPostHookCredentials::$access_token;
|
||
|
$oauth->token_secret = TwitterPostHookCredentials::$access_token_secret;
|
||
|
|
||
|
$short_url_length = 24; // Big, safe default (at time of writing, Twitter's value is actually 20)
|
||
|
try {
|
||
|
$config = json_decode($oauth->request('GET', 'http://api.twitter.com/1/help/configuration.json'), true);
|
||
|
if (isset($config['short_url_length'])) $short_url_length = intval($config['short_url_length']);
|
||
|
if ($short_url_length < 16) $short_url_length = 24; // sanity check
|
||
|
} catch (Exception $e) { }
|
||
|
|
||
|
$url_to_use = $post_array_for_template['post-absolute-permalink'];
|
||
|
$tweet_text = tweet_text_before_url_for_post(
|
||
|
$post_array_for_template,
|
||
|
140 - ($short_url_length + 1 /* the space before the URL */),
|
||
|
$url_to_use
|
||
|
);
|
||
|
|
||
|
$tweet_text .= ' ' . $url_to_use;
|
||
|
$response = $oauth->request(
|
||
|
'POST', 'https://api.twitter.com/1/statuses/update.json',
|
||
|
array('trim_user' => 'true', 'status' => $tweet_text)
|
||
|
);
|
||
|
|
||
|
$post = @json_decode($response, true);
|
||
|
if ($post && isset($post['id_str']) && isset($post['text'])) {
|
||
|
error_log("Posted to Twitter: [{$post['text']}]");
|
||
|
} else {
|
||
|
error_log("Posting to Twitter failed: $response");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
$command_line_test_mode = isset($_SERVER['argv'][0]) && substr($_SERVER['argv'][0], -16) == 'post_twitter.php';
|
||
|
if ($command_line_test_mode) {
|
||
|
$oauth = new ExtremelyBasicSelfContainedOAuthConsumer();
|
||
|
$oauth->consumer_key = TwitterPostHookCredentials::$consumer_key;
|
||
|
$oauth->consumer_secret = TwitterPostHookCredentials::$consumer_secret;
|
||
|
$oauth->token = TwitterPostHookCredentials::$access_token;
|
||
|
$oauth->token_secret = TwitterPostHookCredentials::$access_token_secret;
|
||
|
|
||
|
$response = $oauth->request('GET', 'https://api.twitter.com/1/account/verify_credentials.json');
|
||
|
$user = @json_decode($response, true);
|
||
|
|
||
|
if ($user && isset($user['error'])) {
|
||
|
echo "\nTwitter returned an error: {$user['error']}\n\n";
|
||
|
exit(1);
|
||
|
} else if ($user && isset($user['name']) && isset($user['screen_name'])) {
|
||
|
echo "\nSuccessfully authenticated as @{$user['screen_name']} ({$user['name']})\n";
|
||
|
} else {
|
||
|
echo "\nGot unrecognized response from Twitter:\n$response\n\n";
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == 'post') {
|
||
|
tweet_link_to_post(array(
|
||
|
'post-title' => 'Test post title',
|
||
|
'post-absolute-permalink' => 'http://twitter.com/',
|
||
|
));
|
||
|
echo "\n";
|
||
|
} else {
|
||
|
echo "Re-run with argument 'post' to create a test post on Twitter.\n\n";
|
||
|
}
|
||
|
} else {
|
||
|
class Twitter extends Hook
|
||
|
{
|
||
|
public function doHook(Post $post)
|
||
|
{
|
||
|
tweet_link_to_post($post->array_for_template());
|
||
|
}
|
||
|
}
|
||
|
}
|