$Id:$
Messaging and exclusions:

Entities:
- Account (Myspace account)
- Message (A unique subject and message)
- Friend list (i.e. friends of a band, members of a group, to
              which messages are sent)
- Campaign  (Message grouping, or a "purpose" for sending a message).

Relationships:
- Accounts send messages.  (many to many)
- Messages have a list of friends to whom they'll be sent. (many to many)
- Messages are sent for a purpose (campaign). (many to one)
- A friend is contacted (by anyone) only once per campaign.


Tables to represent this:
campaigns:
campaign_id
campaign_desc

messages:
message_id
campaign_id (optional if only one message in the campaign?)
subject
message

message_to_friend  (maps messages to the friends they'll be sent to)
message_id
friend_id

account_to_message  (maps messages to the accounts they'll be sent from)
account_id,
message_id


Example:
Julia and Sue are sending messages to promote Julia's Myspace page.

    campaigns:
        campaign_id: 1
        campaign_desc: Introduce potential fans to Julia

Julia's Message to Evanescence fan group:
    messages:
        message_id: 1
        campaign_id: 1
        Subject: Evanescence
        Message: Hi, I saw that you like Evanescence and I thought you like
            my music too. Add me if you do!
            
            - Julia

    message_to_friend:  # Contains the list of friends to which message#1 will
                        # be sent
        message_id  friend_id
        1           12345
        1           12346
        1           12347
        ...         ...

Sue's message to Ashlee Simpson fan group:
    messages:
        message_id: 2
        campaign_id: 1
        subject: Ashlee Simpson
        message: Hi, I saw that you like Ashlee Simpson.  I'm on the Street
            Team for "Julia" - I thought you might like her stuff.
            <a href="http://www.myspace.com/juliasrock">myspace.com/juliasrock</a>.
            
            - Sue
    
    message_to_friend:  (This is basically Olaf's cache table)
        message_id  friend_id
        2           12345
        2           23456
        2           23457
        ...         ...

Julia and Sue are sending different messages for the same purpose, so they
want to make sure they don't message the same person.
Person # 12345 is on both lists.

Julia runs and posts to 12345, 12346, 12347.
Sue runs, excludes 12345 because somebody already successully posted to
that friend for the same campaign, then posts to 12346 and 12347.
Don't send a message to the same friend for the same campaign.

    Or in Perl/SQL psuedo-code:
    exclude if exists select from post_log, messages where
        post_log.message_id = messages.message_id and
        messages.campaign_id = $self->campaign_id and
        post_log.friend_id = $friend_id and
        result_code != "F" and result_code != "FN"

When posting, log that this account sent this message to this friend.
insert into post_log:
    account_id => $self->myspace->my_friend_id,  # I think it should be
    message_id => $self->message_id,
    post_type => $self->post_type,
    result_code => $status,
    last_post => time
    
    
Posting:
- insert to post_log account_id, message_id, post_type, result_code "W", last_post
  - $post_id = post_id assigned by insert statement
- $exclude = 1 if exists select from post_log, messages where
        post_log.message_id = messages.message_id and
        messages.campaign_id = $self->campaign_id and
        post_log.friend_id = $friend_id and
        ( ( result_code != "F" and result_code != "FN" ) or
          ( result_code = "W" and post_id < $my_post_id ) )
- If not excluded:
    - Post
    - update post_log set result_code = $result_code where post_id = $post_id
  - else
    - delete from post_log where post_id = $my_post_id
      - or to clean up old locks too (but see cron job below):
        delete from post_log where post_id = $my_post_id or
                ( result_code = "W" and last_post < ( time - 3600 ) )

This inserts a "lock request" into the log, then checks to see if there was a
previous real post or a previous lock request. Earliest lock request wins.

- Cron job to clean up possible abandoned locks:
  delete from post_log where result_code = "W" and last_post < ( time - 3600 );
  (Cleans up locks inserted over 1 hr ago).




----------------------------------------------------------

Campaign modules:

WWW::Myspace::FriendFinder
    - Takes search criteria as options:
        profile => $profile_friend_id,
        group => $group_id,
        browse => $browse_criteria_hashref,
        search_music => $search_music_criteria_hashref

    - Returns friend list via "get_friends" method.
    - Handles any necessary caching (internally and/or in DB).
    - Doesn't do lookups until necessary.

WWW::Myspace::Poster:
    - Provides all core methods to post a message (or not) from one
      account to another:
      - Logging methods
      - Method to determine if a friend should be excluded.
      - Primary Iterator method "post", so any Poster subclass can
        be run by calling the "post" method.
      - options setting via new (inherited from MyBase)
      - Accessors for most standard options (myspace, subject, message,
        max_count, etc.)
        (Not all subclasses will use all of those, obviously)
      - friend_ids method to store list of friends to whom message
        will be sent (list of IDs or FriendFinder object).
        - Method will also return the friend_ids (calling the FriendFinder
          object's "get_friends" method if necessary).
      - "post" method will iterate to max_count and call "send_post"
        method, which subclass must override, to send each post.

WWW::Myspace::Poster subclasses (Comment, Message, FriendAdder)
    - Take a message and a list of friends OR a FriendFinder object.
      (Inherited from Poster)
    - Post the message to the list of friends (by overriding "send_post").
      Note that for each subclass the message varies:
        Comment: comment to post
        FriendAdder: no message
        Message: Subject and Message
      send_post takes one argument, a friendID, and posts to it.
      It then "reacts" if necessary to the post. send_post might usually
      be as simple as calling the right method in the myspace object. Hmm.

WWW::Myspace::Campaign (was Promoter):
    - Is a WWW::Myspace::Poster itself
    - Takes a list of Poster objects
    - Uses the algorithm described in Promoter.pm to post
      the messages.  (Subclasses must provide a "send_post" method
      that sends an individual post. Poster will provide logging methods).

Campaign can be run by multiple people (accounts) at once in realtime if
using a centralized database. Each person runs a Campaign object with
their messages.

Example:
    Julia is going to message groups 123456, 1234567, and profile 55551
        - Message to group 123456:  "Hi, buy my CD!"
        - Message to group 1234567: "You'll really like my CD"
        - Message to profile 55551: "You like them, you'll like me"
    
    - Looks something like this:
        # Get friend criteria
        my $ff123456 = new WWW::Myspace::FriendFinder( group => 123456 );
        my $ff1234567 = new WWW::Myspace::FriendFinder( group => 1234567 );
        my $ff55551 = new WWW::Myspace::FriendFinder( profile => 55551 );
        
        # What do we send to each criteria?
        my $msg123456 = new WWW::Myspace::Message(
                message => "Hi, buy my CD!",
                friend_ids => $ff12345
            );
        my $msg1234567 = new WWW::Myspace::Message(
                message => "You'll really like my CD!",
                friend_ids => $ff1234567
            );
        my $msg55551 = new WWW::Myspace::Message(
                message => "You'll really like my CD!",
                friend_ids => $ff1234567
            );
        
        # Set up the Campaign
        my $myspace = new WWW::Myspace( $julias_account, $julias_password );
        $julia_campaign = new WWW::Myspace::Campaign(
            posters => [ $msg123456, $msg1234567, $msg55551 ]
        );
        
        # Oh wait, let's add a comment
        push @{$julia_campaign->posters},
            new WWW::Myspace::Comment(
                new WWW::Myspace::FriendAdder(
                    profile => $myspace->my_friend_id
                )
            );
        
        # Save it
        $julia_campaign->save;
        
        # Save it ourselves instead
        use YAML 'DumpFile';
        DumpFile( "julia_campaign", $julia_campaign );
        
        # Ok, enough showing off, run the campaign (in a different
        # script, because we're still showing off):

        #!/usr/bin/perl -w
        use YAML 'LoadFile';

        my $julia_campaign = LoadFile( "julia_campaign" );

        $julia_campaign->post;
        

Notes:

- FriendAdder makes it so we "know" the criteria instead of just a "dumb"
  list of friends, so we can cache and load as needed automatically to
  keep the list "fresh". Note that we might not get to a particular message
  for months, so we don't want our list to get stale (nor do we even need
  to read it) until then.

- Campaign's "posters" method should take an arrayref or hasref to allow
  specific message referencing.
  
  For example, we might want to change a message:
  
      $julia_campaign->posters->{'msg55551'}->message(
          "If you like Joe Strummer, you'll *really* like my CD!"
      );
      
 Or we might want to change that to a friend add instead
 
      $julia_campaign->posters->{'msg55551'} = new WWW::Myspace::FriendAdder(
            friend_ids => new WWW::Myspace::FriendFinder( profile => 55551 )
      );





----------------------------------------------

Exclusions checking:

Post 