Below is some code to programatically post a custom message to Twitter using the current user's configured Twitter account. Sometimes you want to tweet something other than a node and I couldn't find an easy way of doing this, so last night I hacked up this piece of code. If you know better way to do it, please tell me. To make it work in your module, change "mymodule" to whatever your module is called. Because of the otherwise excellent Twitter module's notorious lack of return value checks, I modified the twitter_set_status function in twitter.inc to actually collect the returned status. Since the return value is never collected in the original function, it won't effect any existing code. In twitter.inc, just change the line
$twitter->status_update($status);
to
return($twitter->status_update($status));
and my error checking code will work ok. There are still some checks that are missing, e.g. the commented parts, so if anyone has an idea, please share.

<?php
function mymodule_publish_on_twitter($message) {
	global $user;
	module_load_include('inc', 'twitter');

	// Load and connect user's twitter account
	$account = twitter_get_user_accounts($user->uid);
	if (!$account) {
		$err_message = t("Unable to find your Twitter account. ") .  l(t('Click here to a Twitter account to your profile.'),'user/' . $user->uid . '/edit/twitter');
		drupal_set_message($err_message, 'warning');
		return FALSE;
	}

	// Check message length
	if (strlen($message) > 140) {
		$err_message = t("Unable to set your Twitter status. Your status text is too long. The maximum message size on Twitter is 140 charaters.");
		drupal_set_message($err_message, 'warning');
		return FALSE;
	}

	$twitter_account = twitter_account_load($account[0]->id);
	// TODO: Connected ok check should go in here

	// Try to set twitter status
	$status = twitter_set_status($twitter_account,$message);

	// Status is always returned but empty if setting the status didn't succeed
	// Check for errors
	if ($status && $status->id < 1) {
		/* This should work but doesn't
		
		$twitter = twitter_connect($twitter_account);
		$params = array();
		if ($since) {
			$params['since_id'] = $since;
		}
		$err_message = t("Couldn't set your status on Twitter.");
		$statuses = $twitter->user_timeline($account->id, $params, $account->protected);
		foreach ($statuses as $status) {
			if ($status->text == $message) {
				$err_message = t("Status text is a duplicate of a recent tweet and Twitter didn't accept it. Try modifying the text a bit.");
				break;
			}
		} */
		$err_message = t("Couldn't set your status on Twitter. If the status text is a duplicate of a recent tweet, Twitter doesn't accept it. Try modifying the text a bit.");
		drupal_set_message($err_message, 'warning');
	} else {
		$message = t("Twitter status updated.");
		drupal_set_message($message, 'status');
		// Save status if all is ok
		twitter_status_save($status);
		return TRUE;
	}
	return FALSE;
}
?>

Comments

escore’s picture

Finally got around to fixing the non-working parts. In the above code, replace lines 27-47 with the following code:

	// Status is always returned but empty if setting didn't succeed
	// Check for errors
	if ($status && $status->id < 1) {
		module_load_include('lib.php', 'twitter');		
		$twitter = twitter_connect($twitter_account);
		$params = array();
		$err_message = t("Couldn't set your status on Twitter.");
		$statuses = $twitter->user_timeline($twitter_account->id, $params, $twitter_account->protected);
		foreach ($statuses as $status) {
			if ($status->text == $message) {
				$err_message = t("Your status text is a duplicate of a recent tweet and Twitter didn't accept it. Try modifying the text a bit.");
				break;
			}
		}
		drupal_set_message($err_message, 'warning');
	} else {
nbailly’s picture

Awesome code! I've been trying to figure out how to do exactly the same thing, but I am not super saavy at building Drupal modules so this came in VERY HANDY!

Thanks for the contribution.

escore’s picture

Glad to be of help! I've been missing several useful API calls in the Twitter module, so any day soon I'll try to bake my first patch to make them easily available.

ayathullah’s picture

Title: Post custom message to Twitter using logged in user's account » Post comment to Twitter using logged in user's account using advanced actions
Version: 6.x-3.x-dev » 6.x-3.0-beta3

Hi,

I've been looking for something like this for quite a while. Just wanted to say thanks for the contribution.
But I've got a specific request...
How do you set up a rule to post a message to the current user's Twitter account? At the moment, the form in the "Post a message to Twitter" action only permits a absolute value. I want to use a token for the currently logged in user's Twitter account.

Perhaps you can point me in the right direction as to what I should change in the code below.
I'm pretty certain that someparts of your code above will come in handy just not sure exactly what.

Any Help would be greatly appreciated.

Many Thanks,
ayathullah

Heres the code I found in the twitter_actions module which is comes packaged with the twitter module

// $Id: twitter_actions.module,v 1.2.2.3 2010/10/20 15:59:11 walkah Exp $

/**
 * @file
 * Exposes Drupal actions for sending Twitter messages.
 */

/**
 * Implementation of hook_action_info().
 */
function twitter_actions_action_info() {
  return array(
    'twitter_actions_set_status_action' => array(
      'type' => 'system',
      'description' => t('Post a message to Twitter'),
      'configurable' => TRUE,
      'hooks' => array(
        'nodeapi' => array('view', 'insert', 'update', 'delete'),
        'comment' => array('view', 'insert', 'update', 'delete'),
        'user' => array('view', 'insert', 'update', 'delete', 'login'),
        'cron' => array('run'),
      ),
    ),
  );
}

/**
 * Return a form definition so the Send email action can be configured.
 *
 * @param $context
 *   Default values (if we are editing an existing action instance).
 * @return
 *   Form definition.
 */
function twitter_actions_set_status_action_form($context = array()) {
  // Set default values for form.
  $context += array(
    'account_id' => -1,
    'screen_name' => '',
    'message' => '',
  );

  $form['screen_name'] = array(
    '#type'          => 'textfield',
    '#title'         => t('Twitter account name'),
    '#default_value' => $context['screen_name'],
    '#size'          => 25,
    '#required'      => TRUE,
  );

  $form['message'] = array(
    '#type' => 'textarea',
    '#title' => t('Message'),
    '#default_value' => $context['message'],
    '#cols' => '80',
    '#rows' => '3',
    '#description' => t('The message that should be sent. You may include the following variables: %site_name, %username, %node_url, %node_type, %title, %teaser, %body, %tinyurl. Not all variables will be available in all contexts.'),
    '#required'      => TRUE,
  );
  return $form;
}

function twitter_actions_set_status_action_validate($form, $form_state) {
  if (!_twitter_use_oauth()) {
    form_set_error('screen_name', t('Oath has not yet been setup.'));
  }
  if (!db_result(db_query("SELECT twitter_uid FROM {twitter_account} WHERE screen_name = '%s'", $form_state['values']['screen_name']))) {
    form_set_error('screen_name', t('Twitter authentication failed. Please check your account name and try again.'));
  }
}

function twitter_actions_set_status_action_submit($form, $form_state) {
  $form_values = $form_state['values'];
  $twitter_uid = db_result(db_query("SELECT twitter_uid FROM {twitter_account} WHERE screen_name = '%s'", $form_values['screen_name']));
  // Process the HTML form to store configuration. The keyed array that
  // we return will be serialized to the database.
  $params = array(
    'twitter_uid' => $twitter_uid,
    'screen_name' => $form_values['screen_name'],
    'message'   => $form_values['message'],
  );
  return $params;
}

/**
 * Implementation of a configurable Drupal action.
 * Sends an email.
 */
function twitter_actions_set_status_action($object, $context) {
  global $user;
  $variables['%site_name'] = variable_get('site_name', 'Drupal');

  switch ($context['hook']) {
    case 'nodeapi':
    case 'workflow':
      // Because this is not an action of type 'node' the node
      // will not be passed as $object, but it will still be available
      // in $context.
      $node = $context['node'];
      break;
    // The comment hook provides nid, in $context.
    case 'comment':
      $comment = $context['comment'];
      $node = node_load($comment->nid);
    case 'user':
      // Because this is not an action of type 'user' the user
      // object is not passed as $object, but it will still be available
      // in $context.
      $account = $context['account'];
      if (isset($context['node'])) {
        $node = $context['node'];
      }
      elseif ($context['recipient'] == '%author') {
        // If we don't have a node, we don't have a node author.
        watchdog('error', 'Cannot use %author token in this context.');
        return;
      }
      break;
    case 'taxonomy':
      $account = $user;
      $vocabulary = taxonomy_vocabulary_load($object->vid);
      $variables = array_merge($variables, array(
        '%term_name' => $object->name,
        '%term_description' => $object->description,
        '%term_id' => $object->tid,
        '%vocabulary_name' => $vocabulary->name,
        '%vocabulary_description' => $vocabulary->description,
        '%vocabulary_id' => $vocabulary->vid,
        )
      );
      break;
    default:
      // We are being called directly.
      $node = $object;
  }

  if (isset($node)) {
    if (!isset($account)) {
      $account = user_load(array('uid' => $node->uid));
    }
    if ($recipient == '%author') {
      $recipient = $account->mail;
    }
  }

  $variables['%username'] = $account->name;

  // Node-based variable translation is only available if we have a node.
  if (isset($node) && is_object($node)) {
    $variables = array_merge($variables, array(
        '%uid' => $node->uid,
        '%node_url' => url('node/'. $node->nid, array('absolute' => TRUE)),
        '%node_type' => node_get_types('name', $node),
        '%title' => $node->title,
        '%teaser' => $node->teaser,
        '%body' => $node->body
      )
    );
  }

  // Only make a tinyurl if it was asked for.
  if (strstr($context['message'], '%tinyurl') !== FALSE) {
    $variables = array_merge($variables, array(
      '%tinyurl' => twitter_shorten_url(url('node/'. $node->nid, array('absolute' => TRUE))),
    ));
  }

  $message = strtr($context['message'], $variables);

  module_load_include('inc', 'twitter');
  $twitter_account = twitter_account_load($context['twitter_uid']);
  twitter_set_status($twitter_account, $message);
}
escore’s picture

Title: Post comment to Twitter using logged in user's account using advanced actions » Post comment to Twitter using logged in user's account

@ayathullah: Happy if my code is of help to you. As for your question, since it's really a different question, please submit it as a new issue and I'll add my two cents, although I don't think I have much to offer.

ayathullah’s picture

@escore
I have posted it as a new issue please visit the link http://drupal.org/node/1135026

escore’s picture

Just posted my reply there.

BManuel’s picture

If make this custom module, is it enough to update user's status or more coding is required?
BM

michaek’s picture

Hi, folks. Is the use case for this something like: "as a module developer, I want to be able to implement a hook to post an arbitrary message to Twitter, as the current user"?

It's definitely one of the goals of this module to provide an API for other modules to use, and I think there's good reason to make sure this functionality is present in the module and provide documentation for it.

BManuel’s picture

Yes and to also allow some automated status update as the current user does certain activities on the site.
This what I personally need it to do.
1. update status upon successful registration
2. Update status upon adding some defined content type
3. Update status upon flagging a particular content type or user
4. update status periodically advertising the current user post (user can enable/disable this)
5. (Optional) tracking update status, in order to identify what users are the super stars.

Buddy can you implement this module, if the cost is reasonable, I pay for it.
Thanks
BM
You can send me private mail to discuss pricing

ayesh’s picture

Use Rules module, with custom token module.
You will need some db_fetch php code to get the token though.

BManuel’s picture

@Ayesh I am no programmer hence I am willing to pay to get this done :)
Thanks again for the responses

ayesh’s picture

Not yet tested!

  global $user;
  module_load_include('inc', 'twitter');
  $values = db_fetch_array(db_query("SELECT twitter_uid FROM {twitter_account} WHERE uid = %d", $user->uid)); 
   $twitter_account = twitter_account_load($values[twitter_uid]);
steinmb’s picture

Version: 6.x-3.0-beta3 » 6.x-3.x-dev
Component: Miscellaneous » Twitter Actions
Category: support » feature
phelix’s picture

I don't know if I am just completely over looking something but I have the twitter and twitter actions modules installed and have tried using functions from your bit of code. Every function I try to call says that it can not find it. I have also added the module include at the top. But I am getting errors like this.Call to undefined function twitter_get_user_accounts() Any idea on what I could be missing here?

13rac1’s picture

Status: Active » Fixed

This functionality is supported now using Rules due to #1045304: Rules: Post to current user's Twitter account.

juampynr’s picture

Status: Fixed » Closed (duplicate)

Marking as duplicate.

AMMAR ALDUMAINI’s picture