TwitterAPIExchange: Tweeting images not working

php

#1

This topic seems to be discussed a lot, but there appears to be no definitive answer.

I have developed a wraparound class for the TwitterAPIExchange API for Twitter. It works, in that I can post a text status, but if I try and post a status with an image, the status appears, but not the image.

I note that, I first upload the image, get an id and then add this to the ‘postfield’.

The class I have developed is shown at the end of this post, along with a small wraparound function to instatiate it, and call the various CTwitter functions.

The output that is produced (along with var_dump’s) is shown below.

From what I can see the ‘media_ids’ paramater is just being ignored and I don’t know why.

=== Output from test run ===

Soundbite harness: Start CTwitter (1.00): uploadImage: At start of function CTwitter (1.00): json: string(169) “{“media_id”:1035388812191399936,“media_id_string”:“1035388812191399936”,“size”:1660220,“expires_after_secs”:86400,“image”:{“image_type”:“image/jpeg”,“w”:4961,“h”:3307}}”

CTwitter (1.00): res: object(stdClass)#9 (5) { [“media_id”]=> int(1035388812191399936) [“media_id_string”]=> string(19) “1035388812191399936” [“size”]=> int(1660220) [“expires_after_secs”]=> int(86400) [“image”]=> object(stdClass)#10 (3) { [“image_type”]=> string(10) “image/jpeg” [“w”]=> int(4961) [“h”]=> int(3307) } }

ID at the end of uploadImage: 1035388812191399936

CTwitter (1.00): uploadImage: At end of function ID returned from uploadImage: 1035388812191399936

CTwitter (1.00): postImage: At start of function CTwitter (1.00): post: At start of function CTwitter (1.00): postfields: array(2) { [“status”]=> string(17) “Delete this tweet” [“media_ids”]=> string(19) “1035388812191399936” }

CTwitter (1.00): json: string(2457) “{“created_at”:“Fri Aug 31 04:48:25 +0000 2018”,“id”:1035388817312628737,“id_str”:“1035388817312628737”,“text”:“Delete this tweet”,“truncated”:false,“entities”:{“hashtags”:[],“symbols”:[],“user_mentions”:[],“urls”:[]},“source”:”\u003ca href=“http://www.xxxxxx.com/twitter” rel=“nofollow”\u003exxxxx\u003c/a\u003e",“in_reply_to_status_id”:null,“in_reply_to_status_id_str”:null,“in_reply_to_user_id”:null,“in_reply_to_user_id_str”:null,“in_reply_to_screen_name”:null,“user”:{“id”:180287955,“id_str”:“180287955”,“name”:“xxxxxx”,“screen_name”:“xxxxxx”,“location”:“xxxxx”,“description”:“xxxxx @xxxxx/@xxxxx. xxxxx”,“url”:“https://t.co/xxxxx",“entities”:{“url”:{“urls”:[{“url”:“https://t.co/ybqkAuOSbL”,“expanded_url”:“http://www.xxxxx.com”,“display_url”:“xxxxx.com”,“indices”:[0,23]}]},“description”:{“urls”:[]}},“protected”:false,“followers_count”:1529,“friends_count”:842,“listed_count”:159,“created_at”:"Thu Aug 19 07:36:32 +0000 2010”,“favourites_count”:1437,“utc_offset”:null,“time_zone”:null,“geo_enabled”:true,“verified”:false,“statuses_count”:43754,“lang”:“en”,“contributors_enabled”:false,“is_translator”:false,“is_translation_enabled”:false,“profile_background_color”:“FFFFFF”,“profile_background_image_url”:"http://abs.twimg.com/images/themes/theme1/bg.png",“profile_background_image_url_https”:“https://abs.twimg.com/images/themes/theme1/bg.png”,“profile_background_tile”:false,“profile_image_url”:“http://pbs.twimg.com/profile_images/775725280002355200/q7dt7vQB_normal.jpg”,“profile_image_url_https”:“https://pbs.twimg.com/profile_images/775725280002355200/q7dt7vQB_normal.jpg”,“profile_banner_url”:“https://pbs.twimg.com/profile_banners/180287955/1484967410”,“profile_link_color”:“0084B4”,“profile_sidebar_border_color”:“000203”,“profile_sidebar_fill_color”:“CFDFE6”,“profile_text_color”:“333333”,“profile_use_background_image”:true,“has_extended_profile”:false,“default_profile”:false,“default_profile_image”:false,“following”:false,“follow_request_sent”:false,“notifications”:false,“translator_type”:“none”},“geo”:null,“coordinates”:null,“place”:null,“contributors”:null,“is_quote_status”:false,“retweet_count”:0,“favorite_count”:0,“favorited”:false,“retweeted”:false,“lang”:“en”}"

CTwitter (1.00): postImage: At end of function Soundbite harness: after instatiating CTwitterSoundbite

== end ==

Any help/advice appreciated

G

=== CTwitter Class ===


class CTwitter {

// Member Variables
private $username; // Username of the main user
private $settings; // this holds the access tokens and the consumer keys
//private $runFromCron; // This indicates if the job was run from a cron (so $ROOT can’t be (easily) established

// Twitter URLs
private $baseTwitterURL;
private $statusTwitterURL;
private $uploadTwitterURL;

private $version; // Holds the current version of this class
private $className; // Holds the name of this class
private $newline = “
”; // Provides easy access to a new line
private $trace; // Defines whether we want to display (possibly debugging) information

// Paths
private $ROOT;
private $paths;

// === Constructor ===
function _construct($uname, $settings) {

$this->version = "1.00";
$this->className = "CTwitter";
$this->setTrace( true );

$this->includes();
$this->twitterURLs();

$this->username = $uname;

$this->settings = $settings_;

} // constructor

// === Destuctor ====
function __destruct() {
}
// Destuctor

private
function getNameVersion() {
return $this->getName() . " (" . $this->getVersion() . “)”;
}

private
function getName() {
return $this->className;
}

private
function getVersion() {
return $this->version;
}

// Includes the various files that are required
private function includes() {
    $this->ROOT = $_SERVER['DOCUMENT_ROOT'];
    require_once( $this->ROOT . '/php/classes/' . 'Cpaths.php' );
    $this->paths = new Cpaths();

    $this->requireClass("TwitterAPIExchange");

}

private function twitterURLs() {
    $this->baseTwitterURL = 'https://api.twitter.com/1.1/';
    $this->statusTwitterURL = $this->baseTwitterURL . 'statuses/update.json';
    $this->uploadTwitterURL = "https://upload.twitter.com/1.1/media/upload.json";
}

private function requireClass($file) {
    require_once($this->paths->getPHPClasses() . $file . ".php");
}

public function setTrace($t) {
    $this->trace = $t;
}

private function post($postfields) {
if ( $this->trace ) echo $this->getNameVersion() . ": post: At start of function" . $this->newline;

echo $this->getNameVersion() . ": postfields: " . $this->newline;
var_dump($postfields);
echo $this->newline . $this->newline;

    $requestMethod = 'POST';

    try{        
       $twitter = new TwitterAPIExchange($this->settings);
       $json =  $twitter->buildOauth($this->statusTwitterURL, $requestMethod)
                    ->setPostfields($postfields)
                    ->performRequest();
       echo $this->getNameVersion() . ": json: " . $this->newline;
       var_dump($json);
       echo $this->newline . $this->newline;
        return $json;

    } catch (Exception $ex) {
       echo $ex->getMessage();
    }
if ( $this->trace ) echo $this->getNameVersion() . ": post: At end of function" . $this->newline;
}

public function postStatus($tweet) {

   $postfields = array(
      'status' => $tweet
   );

   $this->post($postfields);

} // post

public function postImage($tweet, $id) {
if ( $this->trace ) echo $this->getNameVersion() . “: postImage: At start of function” . $this->newline;

$postfields = array('status' => $tweet, 'media_ids' => $id);

$json = $this->post($postfields);

if ( $this->trace ) echo $this->getNameVersion() . ": postImage: At end of function" . $this->newline;

} // postWithImage

public function uploadImage() {
if ( $this->trace ) echo $this->getNameVersion() . “: uploadImage: At start of function” . $this->newline;
$twitter = new TwitterAPIExchange($this->settings);

$file = file_get_contents($this->ROOT . '/publications/Images/abk2011.jpg');
$data = base64_encode($file);

$method = "POST";
$params = array(
    "media_data" => $data
);

$json = $twitter->setPostfields( $params )->buildOauth( $this->uploadTwitterURL, $method )->performRequest();

echo $this->getNameVersion() . ": json: " . $this->newline;
var_dump($json);
echo $this->newline . $this->newline;

// Result is a json string
$res = json_decode( $json );
echo $this->getNameVersion() . ": res: " . $this->newline;
var_dump($res);
echo $this->newline . $this->newline;

  // Extract media id
$id = $res->media_id_string;
echo "ID at the end of uploadImage: " . $id . $this->newline . $this->newline;
if ( $this->trace ) echo $this->getNameVersion() . ": uploadImage: At end of function" . $this->newline;
return $id;

}

} // CTwitter class



=== End: CTwitter Class ===

=== Calling CTwitter Class ===

 

function __construct($uname) {

$this->version = "1.00";
$this->className = "CTwitterSoundbite";
$this->setTrace( false );

$this->includes();

if($this->trace) echo $this->getNameVersion() . ": __construct" . $this->newline;

$this->username = $uname;

$this->twitterDets = new CTwitterUserDetails($this->getUser());
$this->twitter = new CTwitter($this->getUser(), $this->twitterDets->getSettings());
$id = $this->twitter->uploadImage();
echo "ID returned from uploadImage: " . $id . $this->newline . $this->newline;
$this->twitter->postImage("Delete this tweet", $id);

} // constructor


#2

Hello,

I ran your class and it worked fine for me.

I found a few minor syntax errors with your CTwitter constructor and fixed those.

// === Constructor ===
public function __construct($uname, $settings) {

$this->version = "1.00";
$this->className = "CTwitter";
$this->setTrace( true );

//$this->includes();
$this->twitterURLs();

$this->username = $uname;

$this->settings = $settings;

} // constructor

Then I just ran

$twitter = new CTwitter('RivitKopf',array());

$id = $twitter->uploadImage();

$twitter->postImage('This is a test.',$id);

This succeeds.

I’m not sure how you were able to connect to the API with those typos in your constructor since the TwitterAPIExchange object is instantiated with the CTwitter->settings property.

My guess is that you have additional typos in your code that is preventing this from working correclty.


#3

Thnak you - I’ll try your code - really apprecaite you doing this


#4

Still struggling :-(. The “$this->includes();” that you commented out, includes the TwitterAPIExchange class, so without that, the class is not available, so the API cannot be called.

… and I am not sure what the “array()” is being passed to teh CTwitter constructor. I assume this is the “settings” array data.

Sorry if these are naive questions/details. Quite new to PHP


#5

I included the class manually because my paths are different. You need to uncomment that.

The array() passed to the CTwitter constructor should contain your OAuth credentials.


#6

Thank you


#7

I have written a minimal, self-contained PHP script (shown at the end end) and this does not display the image, only the tweet text.

Any further ideas?

require_once($_SERVER['DOCUMENT_ROOT'] . '/php/classes/' . 'TwitterAPIExchange.php');
 
$settings = array(
    'oauth_access_token' => "XXX",
    'oauth_access_token_secret' => "XXX",
    'consumer_key' => "XXX",
    'consumer_secret' => "XXX"
);

$url = 'https://upload.twitter.com/1.1/media/upload.json';
$requestMethod = 'POST';
	
$image = $_SERVER['DOCUMENT_ROOT'] . '/soundbites/images/gxk.jpg';
display($image);

$postfields = array('media_data' => base64_encode(file_get_contents($image)));
$twitter = new TwitterAPIExchange($settings);

$response = $twitter->buildOauth($url, $requestMethod)
  ->setPostfields($postfields)
  ->performRequest();

// get the media_id from the API return
$media_id = json_decode($response)->media_id;
display("Media_id: " . $media_id);

// then send the Tweet along with the media ID
$url = 'https://api.twitter.com/1.1/statuses/update.json';
$requestMethod = 'POST';

$postfields = array(
  'status' => 'To delete',
  'media_ids' => $media_id,
);

$response = $twitter->buildOauth($url, $requestMethod)
  ->setPostfields($postfields)
  ->performRequest();
	

function display ($msg = "No custom message", $fn = "No fn defined"){
	echo $msg . "<br />";
}