twitter4j streaming + @mentions

Hi all, I currently have a program that connects to twitter, opens a stream and the tweets coming flooding in based on keywords in the filter. This data is then visualised through shapes, colour and sound.
What I am trying to do is add the ability for users to tweet the program and have it find their tweet and visualise it on screen. Just searching through the flood doesn't find these tweets as the streaming api only returns up to 40% of the incoming tweets. I have been informed that @mentions may allow me to stream all available tweets and filter for tweets that mention my account specifically.

I have found this example and have tried to adapt it without success due to the builder having already been used.

Error Message: Cannot use this builder any longer, build() has already been called.

Does anyone have an idea on how to incorporate the @mentions ability to filter through the data from the streaming api?

Thanks.

Twitter code below.

String userDefined;
GetMentions men = new GetMentions();

// Stream it
void openTwitterStream(String user) {

  // OAuth stuff
  ConfigurationBuilder cb = new ConfigurationBuilder();  

  cb.setOAuthConsumerKey("*****");
  cb.setOAuthConsumerSecret("*****");
  cb.setOAuthAccessToken("*****");
  cb.setOAuthAccessTokenSecret("*****"); 

  //the stream object
  twitterStream = new TwitterStreamFactory(cb.build()).getInstance();

  //@mentions modification, doesn't work due to the cb.build already having been used. ---------------------------
  Twitter twitter = new TwitterFactory(cb.build()).getInstance();
  try {
    User u = twitter.verifyCredentials();
    List<Status> statuses = twitter.getMentionsTimeline();
    System.out.println("Showing @" + u.getScreenName() + "'s mentions.");
    for (Status status : statuses) {
      System.out.println("@" + status.getUser().getScreenName() + " - " + status.getText());
    }
  } 
  catch (TwitterException te) {
    te.printStackTrace();
    System.out.println("Failed to get timeline: " + te.getMessage());
    System.exit(-1);
  }
  //end of @mentions modification ------------------------------------------------------------------------

  // filter is used to pass querys to the stream
  // see twitter4j's java doc
  FilterQuery filtered = new FilterQuery();

  // if you enter keywords here it will filter, otherwise it will sample
  String keywords[] = {userDefined};

  //track is like "search"... well kind of
  // for a better explanation go 
  // dev.twitter.com/streaming/overview/request-parameters"
  filtered.track(keywords);

  // the StatusListener interface is where the magic happens
  // code there will be executed upon tweets arriving
  // so we want to attach one to our stream
  twitterStream.addListener(listener);

  if (keywords.length == 0) {
    // sample() method internally creates a thread which manipulates TwitterStream 
    // and calls these adequate listener methods continuously.
    // ref //dev.twitter.com/streaming/reference/get/statuses/sample
    // "Returns a small random sample of all public statuses"
    twitterStream.sample();
  } else { 
    twitterStream.filter(filtered);
  }
  println("connected");
} 

// Implementing StatusListener interface
// the methods are pretty much self explantory
// they will be called according to the messages arrived
// onStatus is probably what you are lookikng for...
StatusListener listener = new StatusListener() {

  //@Override
  public void onStatus(Status status) {
    //println("@" + status.getUser().getScreenName() + " - " + status.getText());
    String s = status.getText().toLowerCase();
    String[] tweetWords = s.split(" ");
    for (int i = 0; i < tweetWords.length-1; i++) words.add(tweetWords[i]);
    //println("tweetWords: " + tweetWords.length);
    //tweetWords = null;
  }

  //@Override
  public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
    println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId());
  }

  //@Override
  public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
    //println("Got track limitation notice:" + numberOfLimitedStatuses);
  }

  //@Override
  public void onScrubGeo(long userId, long upToStatusId) {
    println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId);
  }

  //@Override
  public void onStallWarning(StallWarning warning) {
    println("Got stall warning:" + warning);
  }

  //@Override
  public void onException(Exception ex) {
    ex.printStackTrace();
  }
};

Answers

  • edited January 2018

    I managed to get around the builder issue by creating another speficically for the @mentions function. Now I have no idea how to filter for these.

    I have found this in the documentation which in no uncertain terms says that @mentions are excluded from the streaming api.

    I hope there's some sort of workaround to this.

    Anyone have any ideas?

    // Stream it
    void openTwitterStream(String user) {
      userDefined = user.toLowerCase();
      println("userDefined: " + userDefined);
      twitterInitialised = true;
      // OAuth stuff
      ConfigurationBuilder cb = new ConfigurationBuilder();  //stream builder
      ConfigurationBuilder b = new ConfigurationBuilder();   //@mentions builder
      //@poostickPython
    
      //stream api OAuth
      cb.setOAuthConsumerKey("xxxx");
      cb.setOAuthConsumerSecret("xxxx");
      cb.setOAuthAccessToken("xxxx");
      cb.setOAuthAccessTokenSecret("xxxx"); 
    
      //@mentions builder OAuth
      b.setOAuthConsumerKey("xxxx");
      b.setOAuthConsumerSecret("xxxx");
      b.setOAuthAccessToken("xxxx");
      b.setOAuthAccessTokenSecret("xxxx"); 
    
      //the stream object
      twitterStream = new TwitterStreamFactory(cb.build()).getInstance();
    
      //@mentions begins here
      Twitter twitter = new TwitterFactory(b.build()).getInstance();
      try {
        User u = twitter.verifyCredentials();
        List<Status> statuses = twitter.getMentionsTimeline();
        println("Showing @" + u.getScreenName() + "'s mentions.////////////////////////////////////////////////////");
        for (Status status : statuses) {
          System.out.println("@" + status.getUser().getScreenName() + " - " + status.getText());
        }
      } 
      catch (TwitterException te) {
        te.printStackTrace();
        System.out.println("Failed to get timeline: " + te.getMessage());
        System.exit(-1);
      }
      //@mentions ends here
    
      // filter is used to pass querys to the stream
      // see twitter4j's java doc
      FilterQuery filtered = new FilterQuery();
    
      // if you enter keywords here it will filter, otherwise it will sample
      String keywords[] = {
        "white", "black", "red", "orange", "yellow", "green", "blue", "indigo", "violet", "purple", userDefined
      };
    
      //track is like "search"... well kind of
      // for a better explanation go 
      // dev.twitter.com/streaming/overview/request-parameters"
      filtered.track(keywords);
    
      // the StatusListener interface is where the magic happens
      // code there will be executed upon tweets arriving
      // so we want to attach one to our stream
      twitterStream.addListener(listener);
    
      if (keywords.length == 0) {
        // sample() method internally creates a thread which manipulates TwitterStream 
        // and calls these adequate listener methods continuously.
        // ref //dev.twitter.com/streaming/reference/get/statuses/sample
        // "Returns a small random sample of all public statuses"
        twitterStream.sample();
      } else { 
        twitterStream.filter(filtered);
      }
      println("connected");
    } 
    
    // Implementing StatusListener interface
    // the methods are pretty much self explantory
    // they will be called according to the messages arrived
    // onStatus is probably what you are lookikng for...
    StatusListener listener = new StatusListener() {
    
      //@Override
      public void onStatus(Status status) {
        //println("@" + status.getUser().getScreenName() + " - " + status.getText());
        String s = status.getText().toLowerCase();
        String[] tweetWords = s.split(" ");
        for (int i = 0; i < tweetWords.length-1; i++) words.add(tweetWords[i]);
        //println("tweetWords: " + tweetWords.length);
        //tweetWords = null;
      }
    
      //@Override
      public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {
        println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId());
      }
    
      //@Override
      public void onTrackLimitationNotice(int numberOfLimitedStatuses) {
        //println("Got track limitation notice:" + numberOfLimitedStatuses);
      }
    
      //@Override
      public void onScrubGeo(long userId, long upToStatusId) {
        println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId);
      }
    
      //@Override
      public void onStallWarning(StallWarning warning) {
        println("Got stall warning:" + warning);
      }
    
      //@Override
      public void onException(Exception ex) {
        ex.printStackTrace();
      }
    };
    
  • edited January 2018

    The above works. If the users account is mentioned eg: tweet content @exampleaccount then this will filter it from the stream and it can be used on screen.

    Solved.

  • Posting api keys to a public forum is asking for trouble...

  • In general: if someone issues you a variable with "secret" or "private" or "PIN" etc. in the name, don't post it to a public forum, and don't check it in to a public open source repository (e.g. github).

    Examples:

    • ConsumerSecret
    • PrivateSecret
    • SecretKey
    • PrivateKey
    • AccessTokenSecret
    • etc.

    Treat it like your bank PIN number. You can still ask questions about banking in public, you just shouldn't provide your personal PIN number as part of an example.

  • edited January 2018

    I didn't think I had. The keys and secrets have been changed to x's or asterisk's in place of the actual key, token or secret.

      cb.setOAuthConsumerKey("xxxx");         //not an actual key
      cb.setOAuthConsumerSecret("xxxx");      //not an actual secret key
      cb.setOAuthAccessToken("xxxx");         //not an actual token
      cb.setOAuthAccessTokenSecret("xxxx");   //not an actual secret token
    

    Please either @jeremydouglass or @koogs inbox me and tell me on which line I have gone wrong.

    On the topic of the @mentions code, it appears that it is not required at all. Just tweet and @mention the account and it should filter it. Useful.

  • edited January 2018

    @SnailPropulsionLabs -- not sure -- I had just assumed @koogs had edited those out for you, as he made his comment at 5:53 and your post was edited at 5:53.

    The @ mention does sound useful!

  • Thanks for the response jeremy.

Sign In or Register to comment.