Generate WordPress Posts From Fan Mail

Whenever I dig around the WordPress source code, I try to come up with crazy functionality ideas and scenarios that at least one person in the world might find useful, write them down and come back to them and write some code whenever I feel like relaxing and having a bit of fun. Today is one of those days.

Generate WordPress Posts From Reader Fanmail

In one of my last week’s posts titled Another 7 Overlooked WordPress Helper Functions, I mentioned the POP3 library that is bundled with WordPress as something one can use.

Useful for all sorts of interesting things, like parsing reader fan mail to draft posts automatically, answering them and publishing them.

So this is what I’ll code up step-by-step as I write this post. It’s going to be raw and dirty, as usual. I assume that you know a bit about WordPress plugin development and are running WordPress 3.3+. Buckled up? Here we go.

The setup

You can code alongside in whichever structure you prefer – class-based, function-based or even inside theme files, I won’t care as long as it works. I’ll be skimming across many points outlining the important bits and how they interact together and provide the full source code at the end.

Fetching mail

The first thing that the plugin should do is learn how to fetch mail. Fortunately, the POP3 class that comes with WordPress is ready to be used. If it ever disappears from the core in the future, you can bundle it together with any plugin it relies on, since it’s decoupled from the core.

The POP3 library is used by the Post by mail functionality that comes with WordPress. “Aha! So why code something that can be just used?” Indeed, this core feature can be used, since posts from unknown senders are added with a pending status. However, we’re here to learn from the source, there’s no harm in using parts of and learning from wp-mail.php.

So, let’s create a fetching function that works with a real POP3 account and fetches e-mails first and work our way down from there.

Gmail provides POP3 access to its mailboxes, you can register a new account, visit the Settings/Forwarding and POP/IMAP tab and enable POP. Here are the instructions on what the server, port, username and password are. Do not forget to prepend the server with ssl:// if SSL is enforced like on Gmail.

public function fetch_mail( $server, $port, $login, $pass ) {
  /* require POP3 library */
  require_once( ABSPATH . WPINC . '/class-pop3.php' );

  $pop = new POP3();

  /* invalid credentials */
  if ( !$pop->connect( $server, $port ) || !$pop->user( $login ) ) {
    return new WP_Error( 0x00, "POP Error: {$pop->ERROR}" );
  }

  /* no emails or invalid credentials */
  $mailcount = $pop->pass( $pass );
  if ( $mailcount === false ) return new WP_Error( 0x01, "POP Error: {$pop->ERROR}" );

  $messages = array(); /* store messages here */
  
  for ( $current = 1; $current <= $mailcount; $current++ ) {
    
    $messages []= $pop->get( $current );

  }

  return $messages;
}

You can see I wrapped the code into a class. Remove public to use it outside of a class. It will end up being private afterwards for me, probably.

Parsing mail

A big problem, if you dumped the messages you’ll see that they’re a huge array of mail headers, most of which are not needed. We need to compact them down and map them to resemble a Post object that will be created.

Most of what is inside here can be used as is, it does a great job, too bad it’s not packaged to be reused; but feel free to reuse instead of rolling your own parser. I’m going to strip it down a little to the bare basics.

$body = false; $message = array();
foreach ( $pop->get( $current ) as $line ) {
  if ( strlen( $line ) < 3 ) $body = true; /* a single \r\n */
  if ( $body ) {
    $message['content'] .= $line;
  } else {
    if (preg_match('/Subject: /i', $line)) {
      $subject = trim($line);
      $subject = substr($subject, 9); /* strip out Subject: */
      $message['subject'] = $subject;
    } elseif (preg_match('/From: /i', $line) )  {
      $from = trim($line);
      $from = substr($from, 6); /* strip out From */
      $message['from'] = $from;
    }
  }
} $messages []= $message; /* stack up on messages */

Subject, from and message would be sufficient for a basic demo.

Creating a post

Creating posts in WordPress is done via the wp_insert_post function, and wrap it together in a function that first fetches the messages.

public function run() {
  require_once ABSPATH . 'wp-admin/includes/taxonomy.php'; /* wp_create_category */

  $messages = $this->fetch_mail( 'ssl://pop.gmail.com', 995, 'fans@codeseekah.com', 'nothnxbye' );
  if ( is_wp_error( $messages ) ) return;
  
  if ( !$category = get_category_by_slug( 'fanmail' ) ) {
    $category_id = wp_create_category( 'Fan mail' );
  } else $category_id = $category->id;

  foreach( $messages as $message ) {
    /* create the posts */
    $content = htmlentities( $message['from'] ) . ' writes: '."\n";
    $content .= '<blockquote>' . strip_tags( $message['content'] ) . '</blockquote>';

    $post = array(
        'post_category' => array( intval( $category_id ) ),
        'post_content' => $content,
        'post_status' => 'pending',
        'post_title' => strip_tags( $message['subject'] ),
        'post_type' => 'post'
      );
    $post_id = wp_insert_post( $post );
  }
}

Not too complicated, but needs more thought, of course. Don’t forget to always escape user input.

Streamlining fetching

Now that the basic functionality is in, let’s setup some code to schedule fetching and also remove the e-mails from the inbox so they won’t be re-read. I’ll check for the schedule every time to avoid plugin deactivation and activation housekeeping, even though this provides a little more overhead. Using the wp_schedule_event function of the WordPress Cron API we get something like:

$fanmail_posts = new FanmailPosts_Plugin;
add_action( 'fanmail_posts_run', array( $fanmail_posts, 'run' ) );

..and:

public function __construct() {
  add_action( 'init', array( $this, 'schedule_fetching' ) );
}
public function schedule_fetching() {
  if ( !wp_next_scheduled( 'fanmail_posts_run' ) ) {
    wp_schedule_event( time(), 'hourly', 'fanmail_posts_run' );
  }
}

…wherein the fanmail_posts_run hook will be pulled every hour to fetch the posts.

Also have $pop remove your message from the server by doing $pop->delete( $current ); wherever your messages are being read.

WordPress Fanmail Plugin

Full source

Here’s my version of the WordPress posts generator from fan mail: fanmail-generated-posts.php; as usual, comes with no guarantees. Bug reports and ideas are welcome.

More thoughts

  • Implement Akismet spam filtering, even though mail spam filters should work quite well
  • Hash message contents of last parsed message to pull in only new messages instead of removing them
  • Provide option screens for the plugin to setup one or more mail servers to pull messages from