/******************************************************************************
*
* Copyright 2014 Paphus Solutions Inc.
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.botlibre.sense.facebook;
import java.net.URL;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.botlibre.Bot;
import org.botlibre.BotException;
import org.botlibre.api.knowledge.Network;
import org.botlibre.api.knowledge.Relationship;
import org.botlibre.api.knowledge.Vertex;
import org.botlibre.knowledge.Primitive;
import org.botlibre.self.SelfCompiler;
import org.botlibre.sense.BasicSense;
import org.botlibre.sense.http.Http;
import org.botlibre.thought.language.Language;
import org.botlibre.thought.language.Language.LanguageState;
import org.botlibre.util.TextStream;
import org.botlibre.util.Utils;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import facebook4j.Account;
import facebook4j.Comment;
import facebook4j.FacebookException;
import facebook4j.FacebookFactory;
import facebook4j.Friend;
import facebook4j.PagableList;
import facebook4j.Post;
import facebook4j.PostUpdate;
import facebook4j.Reading;
import facebook4j.ResponseList;
import facebook4j.User;
import facebook4j.auth.AccessToken;
import facebook4j.conf.ConfigurationBuilder;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
/**
* Enables receiving a sending messages through Facebook.
*/
public class Facebook extends BasicSense {
public static int MAX_LOOKUP = 100;
public static String oauthKey = "key";
public static String oauthSecret = "secret";
protected String userName = "";
protected String token = "";
protected Date tokenExpiry;
public String appOauthKey = "";
public String appOauthSecret = "";
protected boolean initProperties;
protected boolean autoFriend = false;
protected String welcomeMessage = "";
protected int maxFriends = 100;
protected int maxFriendsPerCycle = 5;
protected int maxPage = 5;
protected int maxLike = 20;
protected int maxPost = 20;
protected int maxFeed = 20;
protected int maxErrors = 5;
protected int errors;
protected boolean processPost = false;
protected boolean processAllPosts = false;
protected boolean processNewsFeed = false;
protected boolean processAllNewsFeed = false;
protected boolean likeAllPosts = false;
protected boolean replyToMessages = false;
protected boolean facebookMessenger = false;
protected String facebookMessengerAccessToken = "";
protected boolean autoPost = false;
protected int autoPostHours = 24;
protected String page = "";
protected String pageId = "";
protected List<String> pages = new ArrayList<String>();
protected String profileName = "";
protected List<String> likeKeywords = new ArrayList<String>();
protected List<String> postRSS = new ArrayList<String>();
protected List<String> rssKeywords = new ArrayList<String>();
protected List<String> statusKeywords = new ArrayList<String>();
protected List<String> newsFeedKeywords = new ArrayList<String>();
protected List<String> autoFriendKeywords = new ArrayList<String>();
protected Set<String> processedPosts = new HashSet<String>();
protected Set<String> wallPosts = new HashSet<String>();
protected int posts;
protected int postsProcessed;
protected int messagesProcessed;
protected int likes;
protected facebook4j.Facebook connection;
public Facebook(boolean enabled) {
this.isEnabled = enabled;
this.languageState = LanguageState.Discussion;
}
public Facebook() {
this(false);
}
public String getAppOauthKey() {
return appOauthKey;
}
public void setAppOauthKey(String appOauthKey) {
this.appOauthKey = appOauthKey;
}
public String getAppOauthSecret() {
return appOauthSecret;
}
public void setAppOauthSecret(String appOauthSecret) {
this.appOauthSecret = appOauthSecret;
}
public List<String> getPages() {
return pages;
}
public void setPages(List<String> pages) {
this.pages = pages;
}
public int getPosts() {
return posts;
}
public void setPosts(int posts) {
this.posts = posts;
}
public int getPostsProcessed() {
return postsProcessed;
}
public void setPostsProcessed(int postsProcessed) {
this.postsProcessed = postsProcessed;
}
public int getMessagesProcessed() {
return messagesProcessed;
}
public void setMessagesProcessed(int messagesProcessed) {
this.messagesProcessed = messagesProcessed;
}
public int getLikes() {
return likes;
}
public void setLikes(int likes) {
this.likes = likes;
}
public String getPage() {
initProperties();
return page;
}
public String getPageId() {
initProperties();
if ((pageId == null || pageId.isEmpty()) && isPage()) {
try {
connect();
this.pageId = getConnection().getPage().getId();
} catch (Exception exception) {
log(exception);
}
}
return pageId;
}
public void setPageId(String pageId) {
initProperties();
this.pageId = pageId;
}
public String getProfileName() {
return profileName;
}
public void setProfileName(String profileName) {
this.profileName = profileName;
}
public void setPage(String page) {
initProperties();
this.page = page;
}
public String getFacebookMessengerAccessToken() {
initProperties();
return facebookMessengerAccessToken;
}
public void setFacebookMessengerAccessToken(String facebookMessengerAccessToken) {
this.facebookMessengerAccessToken = facebookMessengerAccessToken;
}
public String getWelcomeMessage() {
initProperties();
return welcomeMessage;
}
public void setWelcomeMessage(String welcomeMessage) {
initProperties();
this.welcomeMessage = welcomeMessage;
}
public boolean getProcessAllPosts() {
initProperties();
return processAllPosts;
}
public void setProcessAllPosts(boolean processAllPosts) {
initProperties();
this.processAllPosts = processAllPosts;
}
public boolean getProcessAllNewsFeed() {
initProperties();
return processAllNewsFeed;
}
public void setProcessAllNewsFeed(boolean processAllNewsFeed) {
initProperties();
this.processAllNewsFeed = processAllNewsFeed;
}
public boolean getLikeAllPosts() {
initProperties();
return likeAllPosts;
}
public void setLikeAllPosts(boolean likesAllPosts) {
initProperties();
this.likeAllPosts = likesAllPosts;
}
public List<String> getRssKeywords() {
initProperties();
return rssKeywords;
}
public void setRssKeywords(List<String> rssKeywords) {
initProperties();
this.rssKeywords = rssKeywords;
}
public boolean getProcessNewsFeed() {
initProperties();
return processNewsFeed;
}
public void setProcessNewsFeed(boolean processNewsFeed) {
initProperties();
this.processNewsFeed = processNewsFeed;
}
public List<String> getNewsFeedKeywords() {
initProperties();
return newsFeedKeywords;
}
public void setNewsFeedKeywords(List<String> newsFeedKeywords) {
initProperties();
this.newsFeedKeywords = newsFeedKeywords;
}
public int getMaxFeed() {
initProperties();
return maxFeed;
}
public void setMaxFeed(int maxFeed) {
initProperties();
this.maxFeed = maxFeed;
}
public boolean getAutoPost() {
initProperties();
return autoPost;
}
public void setAutoPost(boolean autoPost) {
initProperties();
this.autoPost = autoPost;
}
public int getAutoPostHours() {
initProperties();
return autoPostHours;
}
public void setAutoPostHours(int autoPostHours) {
initProperties();
this.autoPostHours = autoPostHours;
}
public List<Vertex> getAutoPosts(Network network) {
return network.createVertex(getPrimitive()).orderedRelations(Primitive.AUTOPOSTS);
}
public List<String> getPostKeywords() {
initProperties();
return statusKeywords;
}
public void setPostKeywords(List<String> statusKeywords) {
initProperties();
this.statusKeywords = statusKeywords;
}
public List<String> getAutoFriendKeywords() {
initProperties();
return autoFriendKeywords;
}
public void setAutoFriendKeywords(List<String> autoFriendKeywords) {
initProperties();
this.autoFriendKeywords = autoFriendKeywords;
}
/**
* Authorise a new account to be accessible by Bot.
* Return the request token that contains the URL that the user must use to authorise facebook.
*/
public String authorizeAccount(String callbackURL) throws FacebookException {
this.connection = new FacebookFactory().getInstance();
String key = getOauthKey();
String secret = getOauthSecret();
if (this.appOauthKey != null && !this.appOauthKey.isEmpty()) {
key = this.appOauthKey;
}
if (this.appOauthSecret != null && !this.appOauthSecret.isEmpty()) {
secret = this.appOauthSecret;
}
this.connection.setOAuthAppId(key, secret);
if (this.appOauthKey != null && !this.appOauthKey.isEmpty()) {
this.connection.setOAuthPermissions("user_posts,manage_pages,publish_pages,publish_actions,read_page_mailboxes");
} else {
this.connection.setOAuthPermissions("user_posts,manage_pages,publish_pages,publish_actions,read_page_mailboxes");
}
//this.connection.setOAuthPermissions("read_stream, manage_pages, publish_pages, publish_actions, read_mailbox, read_page_mailboxes");
return this.connection.getOAuthAuthorizationURL(callbackURL);
}
/**
* Authorise a new account to be accessible by Bot.
*/
public void authorizeComplete(String pin) throws FacebookException {
AccessToken token = this.connection.getOAuthAccessToken(pin);
setToken(token.getToken());
User user = this.connection.getMe();
this.userName = user.getId();
if (token.getExpires() != null) {
this.tokenExpiry = new Date(System.currentTimeMillis() + (token.getExpires() * 1000));
}
this.profileName = user.getName();
try {
this.page = "";
ResponseList<Account> accounts = this.connection.getAccounts();
this.pages = new ArrayList<String>();
if (accounts != null) {
for (Account account : accounts) {
this.page = account.getName();
this.pages.add(account.getName());
}
}
} catch (Exception exception) {
log(exception);
}
/* Map<String, String> params = new HashMap<String, String>();
params.put("client_id", this.oauthKey);
params.put("client_secret", this.oauthSecret);
params.put("grant_type", "fb_exchange_token");
params.put("fb_exchange_token", token.getToken());
RawAPIResponse apiResponse = this.connection.callGetAPI("/oauth/access_token", params);
String response = apiResponse.asString();
AccessToken newAccessToken = new AccessToken(response);
this.connection.setOAuthAccessToken(newAccessToken);
setToken(newAccessToken.getToken());
this.tokenExpiry = new Date(System.currentTimeMillis() + (newAccessToken.getExpires() * 1000));
System.out.println(this.tokenExpiry);*/
}
/**
* Start sensing.
*/
@Override
public void awake() {
this.userName = this.bot.memory().getProperty("Facebook.user");
if (this.userName == null) {
this.userName = "";
}
this.token = this.bot.memory().getProperty("Facebook.token");
if (this.token == null) {
this.token = "";
}
if (!this.token.isEmpty()) {
setIsEnabled(true);
}
this.appOauthKey = this.bot.memory().getProperty("Facebook.appOauthKey");
if (this.appOauthKey != null && !this.appOauthKey.isEmpty()) {
this.appOauthKey = Utils.decrypt(Utils.KEY, this.appOauthKey);
}
if (this.appOauthKey == null) {
this.appOauthKey = "";
}
this.appOauthSecret = this.bot.memory().getProperty("Facebook.appOauthSecret");
if (this.appOauthSecret != null && !this.appOauthSecret.isEmpty()) {
this.appOauthSecret = Utils.decrypt(Utils.KEY, this.appOauthSecret);
}
if (this.appOauthSecret == null) {
this.appOauthSecret = "";
}
}
/**
* Load settings.
*/
public void initProperties() {
if (this.initProperties) {
return;
}
synchronized (this) {
if (this.initProperties) {
return;
}
getBot().memory().loadProperties("Facebook");
Network memory = getBot().memory().newMemory();
Vertex facebook = memory.createVertex(getPrimitive());
String property = this.bot.memory().getProperty("Facebook.tokenExpiry");
if (property != null) {
this.tokenExpiry = new Date(Long.valueOf(property));
}
property = this.bot.memory().getProperty("Facebook.welcomeMessage");
if (property != null) {
this.welcomeMessage = property;
}
property = this.bot.memory().getProperty("Facebook.profileName");
if (property != null) {
this.profileName = property;
}
property = this.bot.memory().getProperty("Facebook.page");
if (property != null) {
this.page = property;
}
property = this.bot.memory().getProperty("Facebook.pageId");
if (property != null) {
this.pageId = property;
}
property = this.bot.memory().getProperty("Facebook.autoFriend");
if (property != null) {
this.autoFriend = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.maxFriends");
if (property != null) {
this.maxFriends = Integer.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.maxPost");
if (property != null) {
this.maxPost = Integer.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.maxLike");
if (property != null) {
this.maxLike = Integer.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.processPost");
if (property != null) {
this.processPost = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.processAllPosts");
if (property != null) {
this.processAllPosts = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.processNewsFeed");
if (property != null) {
this.processNewsFeed = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.processAllNewsFeed");
if (property != null) {
this.processAllNewsFeed = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.likeAllPosts");
if (property != null) {
this.likeAllPosts = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.replyToMessages");
if (property != null) {
this.replyToMessages = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.facebookMessenger");
if (property != null) {
this.facebookMessenger = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.facebookMessengerAccessToken");
if (property != null) {
this.facebookMessengerAccessToken = property;
}
property = this.bot.memory().getProperty("Facebook.autoPost");
if (property != null) {
this.autoPost = Boolean.valueOf(property);
}
property = this.bot.memory().getProperty("Facebook.autoPostHours");
if (property != null) {
this.autoPostHours = Integer.valueOf(property);
}
this.statusKeywords = new ArrayList<String>();
List<Relationship> keywords = facebook.orderedRelationships(Primitive.STATUSKEYWORDS);
if (keywords != null) {
for (Relationship relationship : keywords) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.statusKeywords.add(text);
}
}
}
this.newsFeedKeywords = new ArrayList<String>();
keywords = facebook.orderedRelationships(Primitive.NEWSFEEDKEYWORDS);
if (keywords != null) {
for (Relationship relationship : keywords) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.newsFeedKeywords.add(text);
}
}
}
this.likeKeywords = new ArrayList<String>();
keywords = facebook.orderedRelationships(Primitive.LIKEKEYWORDS);
if (keywords != null) {
for (Relationship relationship : keywords) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.likeKeywords.add(text);
}
}
}
this.autoFriendKeywords = new ArrayList<String>();
List<Relationship> search = facebook.orderedRelationships(Primitive.AUTOFRIENDKEYWORDS);
if (search != null) {
for (Relationship relationship : search) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.autoFriendKeywords.add(text);
}
}
}
this.postRSS = new ArrayList<String>();
List<Relationship> rss = facebook.orderedRelationships(Primitive.RSS);
if (rss != null) {
for (Relationship relationship : rss) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.postRSS.add(text);
}
}
}
this.rssKeywords = new ArrayList<String>();
keywords = facebook.orderedRelationships(Primitive.RSSKEYWORDS);
if (keywords != null) {
for (Relationship relationship : keywords) {
String text = ((String)relationship.getTarget().getData()).trim();
this.rssKeywords.add(text);
}
}
this.initProperties = true;
}
}
/**
* Migrate to new properties system.
*/
public void migrateProperties() {
Network memory = getBot().memory().newMemory();
Vertex facebook = memory.createVertex(getPrimitive());
// Load old properties.
Vertex user = facebook.getRelationship(Primitive.USER);
if (user != null) {
this.userName = (String)user.getData();
}
Vertex token = facebook.getRelationship(Primitive.TOKEN);
if (token != null) {
this.token = (String)token.getData();
setIsEnabled(true);
}
Vertex tokenExpiry = facebook.getRelationship(Primitive.TOKENEXPIRY);
if (tokenExpiry != null) {
this.tokenExpiry = new Date((Long)tokenExpiry.getData());
}
Vertex property = facebook.getRelationship(Primitive.WELCOME);
if (property != null) {
this.welcomeMessage = (String)property.getData();
}
property = facebook.getRelationship(Primitive.NAME);
if (property != null) {
this.profileName = (String)property.getData();
}
property = facebook.getRelationship(Primitive.PAGE);
if (property != null) {
this.page = (String)property.getData();
}
property = facebook.getRelationship(Primitive.AUTOFRIEND);
if (property != null) {
this.autoFriend = (Boolean)property.getData();
}
property = facebook.getRelationship(Primitive.MAXFRIENDS);
if (property != null) {
this.maxFriends = ((Number)property.getData()).intValue();
}
property = facebook.getRelationship(Primitive.MAXSTATUSCHECKS);
if (property != null) {
this.maxPost = ((Number)property.getData()).intValue();
}
property = facebook.getRelationship(Primitive.PROCESSSTATUS);
if (property != null) {
this.processPost = (Boolean)property.getData();
}
this.statusKeywords = new ArrayList<String>();
List<Relationship> keywords = facebook.orderedRelationships(Primitive.STATUSKEYWORDS);
if (keywords != null) {
for (Relationship relationship : keywords) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.statusKeywords.add(text);
}
}
}
this.likeKeywords = new ArrayList<String>();
keywords = facebook.orderedRelationships(Primitive.LIKEKEYWORDS);
if (keywords != null) {
for (Relationship relationship : keywords) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.likeKeywords.add(text);
}
}
}
this.autoFriendKeywords = new ArrayList<String>();
List<Relationship> search = facebook.orderedRelationships(Primitive.AUTOFRIENDKEYWORDS);
if (search != null) {
for (Relationship relationship : search) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.autoFriendKeywords.add(text);
}
}
}
this.postRSS = new ArrayList<String>();
List<Relationship> rss = facebook.orderedRelationships(Primitive.RSS);
if (rss != null) {
for (Relationship relationship : rss) {
String text = ((String)relationship.getTarget().getData()).trim();
if (!text.isEmpty()) {
this.postRSS.add(text);
}
}
}
this.rssKeywords = new ArrayList<String>();
keywords = facebook.orderedRelationships(Primitive.RSSKEYWORDS);
if (keywords != null) {
for (Relationship relationship : keywords) {
String text = ((String)relationship.getTarget().getData()).trim();
this.rssKeywords.add(text);
}
}
property = facebook.getRelationship(Primitive.REPLYTOMESSAGES);
if (property != null) {
this.replyToMessages = (Boolean)property.getData();
}
property = facebook.getRelationship(Primitive.AUTOPOST);
if (property != null) {
this.autoPost = (Boolean)property.getData();
}
property = facebook.getRelationship(Primitive.AUTOPOSTHOURS);
if (property != null) {
this.autoPostHours = ((Number)property.getData()).intValue();
}
// Remove old properties.
facebook.unpinChildren();
facebook.internalRemoveRelationships(Primitive.USER);
facebook.internalRemoveRelationships(Primitive.TOKEN);
facebook.internalRemoveRelationships(Primitive.TOKENEXPIRY);
facebook.internalRemoveRelationships(Primitive.PAGE);
facebook.internalRemoveRelationships(Primitive.NAME);
facebook.internalRemoveRelationships(Primitive.WELCOME);
facebook.internalRemoveRelationships(Primitive.AUTOFRIEND);
facebook.internalRemoveRelationships(Primitive.MAXFRIENDS);
facebook.internalRemoveRelationships(Primitive.MAXSTATUSCHECKS);
facebook.internalRemoveRelationships(Primitive.PROCESSSTATUS);
facebook.internalRemoveRelationships(Primitive.REPLYTOMESSAGES);
facebook.internalRemoveRelationships(Primitive.AUTOPOST);
facebook.internalRemoveRelationships(Primitive.AUTOPOSTHOURS);
memory.save();
saveProperties(null);
}
public void saveProperties(List<String> autoPosts) {
Network memory = getBot().memory().newMemory();
memory.saveProperty("Facebook.user", this.userName, true);
memory.saveProperty("Facebook.token", this.token, true);
if (this.appOauthKey == null || this.appOauthKey.isEmpty()) {
memory.saveProperty("Facebook.appOauthKey", "", true);
} else {
memory.saveProperty("Facebook.appOauthKey", Utils.encrypt(Utils.KEY, this.appOauthKey), true);
}
if (this.appOauthKey == null || this.appOauthKey.isEmpty()) {
memory.saveProperty("Facebook.appOauthSecret", "", true);
} else {
memory.saveProperty("Facebook.appOauthSecret", Utils.encrypt(Utils.KEY, this.appOauthSecret), true);
}
if (this.tokenExpiry == null) {
memory.removeProperty("Facebook.tokenExpiry");
} else {
memory.saveProperty("Facebook.tokenExpiry", String.valueOf(this.tokenExpiry.getTime()), false);
}
memory.saveProperty("Facebook.page", this.page, false);
memory.saveProperty("Facebook.pageId", this.pageId, false);
memory.saveProperty("Facebook.profileName", this.profileName, false);
memory.saveProperty("Facebook.welcomeMessage", this.welcomeMessage, false);
memory.saveProperty("Facebook.autoFriend", String.valueOf(this.autoFriend), false);
memory.saveProperty("Facebook.maxFriends", String.valueOf(this.maxFriends), false);
memory.saveProperty("Facebook.maxPost", String.valueOf(this.maxPost), false);
memory.saveProperty("Facebook.maxLike", String.valueOf(this.maxLike), false);
memory.saveProperty("Facebook.processPost", String.valueOf(this.processPost), false);
memory.saveProperty("Facebook.processAllPosts", String.valueOf(this.processAllPosts), false);
memory.saveProperty("Facebook.processNewsFeed", String.valueOf(this.processNewsFeed), false);
memory.saveProperty("Facebook.processAllNewsFeed", String.valueOf(this.processAllNewsFeed), false);
memory.saveProperty("Facebook.likeAllPosts", String.valueOf(this.likeAllPosts), false);
memory.saveProperty("Facebook.autoFriend", String.valueOf(this.autoFriend), false);
memory.saveProperty("Facebook.replyToMessages", String.valueOf(this.replyToMessages), false);
memory.saveProperty("Facebook.facebookMessenger", String.valueOf(this.facebookMessenger), false);
memory.saveProperty("Facebook.facebookMessengerAccessToken", String.valueOf(this.facebookMessengerAccessToken), false);
memory.saveProperty("Facebook.autoPost", String.valueOf(this.autoPost), false);
memory.saveProperty("Facebook.autoPostHours", String.valueOf(this.autoPostHours), false);
Vertex facebook = memory.createVertex(getPrimitive());
facebook.unpinChildren();
facebook.internalRemoveRelationships(Primitive.STATUSKEYWORDS);
for (String text : this.statusKeywords) {
Vertex keywords = memory.createVertex(text);
facebook.addRelationship(Primitive.STATUSKEYWORDS, keywords);
}
facebook.internalRemoveRelationships(Primitive.NEWSFEEDKEYWORDS);
for (String text : this.newsFeedKeywords) {
Vertex keywords = memory.createVertex(text);
facebook.addRelationship(Primitive.NEWSFEEDKEYWORDS, keywords);
}
facebook.internalRemoveRelationships(Primitive.LIKEKEYWORDS);
for (String text : this.likeKeywords) {
Vertex keywords = memory.createVertex(text);
facebook.addRelationship(Primitive.LIKEKEYWORDS, keywords);
}
facebook.internalRemoveRelationships(Primitive.RSS);
for (String text : this.postRSS) {
Vertex rss = memory.createVertex(text);
facebook.addRelationship(Primitive.RSS, rss);
}
facebook.internalRemoveRelationships(Primitive.RSSKEYWORDS);
for (String text : this.rssKeywords) {
Vertex keywords = memory.createVertex(text);
facebook.addRelationship(Primitive.RSSKEYWORDS, keywords);
}
if (autoPosts != null) {
Collection<Relationship> old = facebook.getRelationships(Primitive.AUTOPOSTS);
if (old != null) {
for (Relationship post : old) {
if (post.getTarget().instanceOf(Primitive.FORMULA)) {
SelfCompiler.getCompiler().unpin(post.getTarget());
}
}
}
facebook.internalRemoveRelationships(Primitive.AUTOPOSTS);
for (String text : autoPosts) {
Vertex post = memory.createSentence(text);
if (post.instanceOf(Primitive.FORMULA)) {
SelfCompiler.getCompiler().pin(post);
}
post.addRelationship(Primitive.INSTANTIATION, Primitive.TWEET);
facebook.addRelationship(Primitive.AUTOPOSTS, post);
}
}
facebook.pinChildren();
memory.save();
}
public void connect() throws FacebookException {
initProperties();
ConfigurationBuilder config = new ConfigurationBuilder();
String key = getOauthKey();
String secret = getOauthSecret();
if (this.appOauthKey != null && !this.appOauthKey.isEmpty()) {
key = this.appOauthKey;
}
if (this.appOauthSecret != null && !this.appOauthSecret.isEmpty()) {
secret = this.appOauthSecret;
}
config.setOAuthAppId(key);
config.setOAuthAppSecret(secret);
config.setOAuthAccessToken(getToken());
facebook4j.Facebook facebook = new FacebookFactory(config.build()).getInstance();
setConnection(facebook);
}
public void connectAccount() throws FacebookException {
connect();
facebook4j.Facebook facebook = getConnection();
User user = facebook.getMe();
if (this.userName == null || !this.userName.equals(user.getId())) {
this.userName = user.getId();
this.profileName = user.getName();
}
this.pageId = "";
if (this.page != null && !this.page.isEmpty()) {
if (facebook.getPage() == null || !facebook.getPage().getName().equals(this.page)) {
// Reset page access token.
boolean found = false;
ResponseList<Account> accounts = this.connection.getAccounts();
if (accounts != null) {
for (Account account : accounts) {
if (this.page.equals(account.getName())) {
found = true;
this.token = account.getAccessToken();
this.userName = account.getId();
this.profileName = account.getName();
String key = getOauthKey();
String secret = getOauthSecret();
if (this.appOauthKey != null && !this.appOauthKey.isEmpty()) {
key = this.appOauthKey;
}
if (this.appOauthSecret != null && !this.appOauthSecret.isEmpty()) {
secret = this.appOauthSecret;
}
ConfigurationBuilder config = new ConfigurationBuilder();
config.setOAuthAppId(key);
config.setOAuthAppSecret(secret);
config.setOAuthAccessToken(getToken());
facebook = new FacebookFactory(config.build()).getInstance();
setConnection(facebook);
this.pageId = facebook.getPage().getId();
log("Connected to Facebook page", Level.INFO, facebook.getPage().getId(), facebook.getPage().getName());
}
}
}
if (!found) {
throw new BotException("Page missing");
}
}
}
saveProperties(null);
}
public void subscribeToMessenger() throws FacebookException {
if (getFacebookMessenger()) {
if (this.page != null && !this.page.isEmpty() && (getConnection().getPage() != null)) {
try {
String url = "https://graph.facebook.com/v2.6/" + getConnection().getPage().getId() + "/subscribed_apps";
log("Subscribing to Facebook Messenger", Level.INFO);
Map<String, String> params = new HashMap<String, String>();
params.put("access_token", getFacebookMessengerAccessToken());
String result = Utils.httpPOST(url, params);
log("Facebook Messenger subscription success", Level.INFO, result);
} catch (Exception exception) {
log(exception);
}
} else {
log("Facebook Messenger only allowed for page messaging", Level.WARNING);
}
}
}
/**
* Check profile for messages.
*/
public void checkProfile() {
log("Checking profile.", Level.INFO);
this.processedPosts = new HashSet<String>();
this.wallPosts = new HashSet<String>();
try {
//checkFriends();
checkWall();
//checkNewsFeed();
checkRSS();
checkAutoPost();
} catch (Exception exception) {
log(exception);
}
log("Done checking profile.", Level.INFO);
}
/**
* Check wall posts.
*/
public void checkWall() {
if (!getProcessPost()) {
return;
}
log("Checking wall posts", Level.FINE);
try {
Network memory = getBot().memory().newMemory();
Vertex facebook = memory.createVertex(getPrimitive());
Vertex vertex = facebook.getRelationship(Primitive.LASTTIMELINE);
long last = 0;
if (vertex != null) {
last = ((Number)vertex.getData()).longValue();
}
long max = 0;
ResponseList<Post> timeline = null;
boolean more = true;
int page = 1;
int count = 0;
int like = 0;
this.errors = 0;
while (more && (count <= this.maxPost) && page <= this.maxPage) {
if (last == 0) {
timeline = getConnection().getFeed(new Reading().fields("id", "message", "caption", "description", "created_time", "from"));
more = false;
} else {
Reading paging = new Reading();
paging.fields("id", "message", "caption", "description", "created_time", "from");
max = last;
paging.since(new Date(last));
timeline = getConnection().getFeed(paging);
if ((timeline == null) || (timeline.size() < 20)) {
more = false;
}
page++;
}
if ((timeline == null) || timeline.isEmpty()) {
log("Empty wall", Level.FINE);
break;
}
log("Processing posts", Level.INFO, timeline.size());
for (int index = timeline.size() - 1; index >= 0; index--) {
if (count >= this.maxPost) {
log("Max posts", Level.FINE, count);
break;
}
if (this.errors > this.maxErrors) {
log("Max errors", Level.WARNING, this.errors);
break;
}
Post post = timeline.get(index);
String userId = post.getFrom() == null ? "anonymous" : post.getFrom().getId();
String userName = post.getFrom() == null ? "anonymous" : post.getFrom().getName();
log("Processing post", Level.FINE, post.getId(), userName, post.getCaption());
long postTime = post.getCreatedTime().getTime();
String postId = post.getId();
if (postTime > max) {
max = postTime;
}
if (!userId.equals(this.userName)) {
if ((System.currentTimeMillis() - postTime) > DAY) {
log("Day old post", Level.INFO, postId, postTime);
more = false;
continue;
}
if (postTime > last) {
boolean match = false;
String message = post.getMessage();
if (message == null || message.isEmpty()) {
message = post.getCaption();
}
if (getLikeAllPosts()) {
if (like >= this.maxLike) {
log("Max like", Level.FINE, like);
} else {
like++;
like(post);
Utils.sleep(500);
}
}
if (message != null && !message.isEmpty()) {
List<String> postWords = new TextStream(message.toLowerCase()).allWords();
// Like
if (!getLikeAllPosts()) {
for (String keywords : getLikeKeywords()) {
List<String> keyWords = new TextStream(keywords.toLowerCase()).allWords();
if (!keyWords.isEmpty()) {
if (postWords.containsAll(keyWords)) {
if (like >= this.maxLike) {
log("Max like", Level.FINE, like);
} else {
like++;
like(post);
Utils.sleep(500);
}
break;
}
}
}
}
// Reply.
for (String text : getPostKeywords()) {
List<String> keywords = new TextStream(text.toLowerCase()).allWords();
if (!keywords.isEmpty() && postWords.containsAll(keywords)) {
match = true;
break;
}
}
if (match || getProcessAllPosts()) {
count++;
log("Processing post", Level.FINE, post.getCaption(), post.getDescription(), post.getMessage(), userId, userName);
this.wallPosts.add(post.getId());
input(post);
Utils.sleep(500);
} else {
log("Skipping post, missing keywords.", Level.FINE, post.getCaption(), post.getDescription(), post.getMessage());
}
} else {
log("Empty message", Level.FINE, post);
}
} else {
log("Old post", Level.INFO, postId, postTime);
}
} else {
log("Ignoring own post", Level.INFO, postId);
}
}
}
// Process comments.
if (count <= this.maxPost) {
timeline = getConnection().getFeed(new Reading().fields("id", "from", "created_time", "comments"));
if ((timeline != null) && !timeline.isEmpty()) {
log("Processing post comments", Level.INFO, timeline.size());
for (int index = timeline.size() - 1; index >= 0; index--) {
if (count >= this.maxPost) {
log("Max posts", Level.FINE, count);
break;
}
if (this.errors > this.maxErrors) {
log("Max errors", Level.WARNING, this.errors);
break;
}
Post post = timeline.get(index);
log("Processing post comments", Level.FINE, post.getId(), post.getCaption());
PagableList<Comment> comments = post.getComments();
if (comments != null && !comments.isEmpty()) {
for (Comment comment : comments) {
long[] values = processComment(comment, null, memory, count, max, last);
if (values == null) {
break;
}
count = (int)values[0];
max = values[1];
if (count == -1) {
break;
}
ResponseList<Comment> replies = getConnection().getCommentReplies(comment.getId());
if ((replies != null) && !replies.isEmpty()) {
for (int index2 = replies.size() - 1; index2 >= 0; index2--) {
Comment reply = replies.get(index2);
values = processComment(reply, comment, memory, count, max, last);
if (values == null) {
break;
}
count = (int)values[0];
max = values[1];
}
}
}
} else {
log("No comments", Level.FINE, post.getId());
}
}
}
} else {
log("Max posts", Level.FINE, count);
}
if (max != 0) {
facebook.setRelationship(Primitive.LASTTIMELINE, memory.createVertex(max));
memory.save();
}
} catch (Exception exception) {
log(exception);
}
}
public long[] processComment(Comment comment, Comment parent, Network memory, int count, long max, long last) {
String userId = comment.getFrom() == null ? "anonymous" : comment.getFrom().getId();
String userName = comment.getFrom() == null ? "anonymous" : comment.getFrom().getName();
log("Processing post comment", Level.FINE, comment.getId(), userName, comment.getMessage());
if (count >= this.maxPost) {
log("Max posts", Level.FINE, count);
return null;
}
if (this.errors > this.maxErrors) {
log("Max errors", Level.WARNING, this.errors);
return null;
}
long postTime = comment.getCreatedTime().getTime();
String postId = comment.getId();
if (postTime > max) {
max = postTime;
}
if (!userId.equals(this.userName)) {
if ((System.currentTimeMillis() - postTime) > DAY) {
log("Day old post comment", Level.FINE, postId, postTime);
return null;
}
if (postTime > last) {
boolean match = false;
List<String> postWords = new TextStream(comment.getMessage().toLowerCase()).allWords();
for (String text : getPostKeywords()) {
List<String> keywords = new TextStream(text.toLowerCase()).allWords();
if (!keywords.isEmpty() && postWords.containsAll(keywords)) {
match = true;
break;
}
}
if (match || getProcessAllPosts()) {
count++;
log("Processing post comment", Level.FINE, comment.getMessage(), userId, userName);
input(comment, parent, memory);
Utils.sleep(500);
} else {
log("Skipping post comment, missing keywords.", Level.FINE, comment.getMessage());
}
} else {
log("Old post comment", Level.INFO, postId, postTime);
}
} else {
log("Ignoring own comment", Level.INFO, postId);
}
long[] values = new long[2];
values[0] = count;
values[1] = max;
return values;
}
/**
* Check news feed posts.
*/
public void checkNewsFeed() {
if (isPage() || !getProcessNewsFeed()) {
return;
}
log("Checking news feed posts", Level.FINE);
try {
Network memory = getBot().memory().newMemory();
Vertex facebook = memory.createVertex(getPrimitive());
Vertex vertex = facebook.getRelationship(Primitive.LASTNEWSFEED);
long last = 0;
if (vertex != null) {
last = ((Number)vertex.getData()).longValue();
}
long max = 0;
ResponseList<Post> timeline = null;
boolean more = true;
int page = 1;
int count = 0;
int like = 0;
this.errors = 0;
while (more && (count <= this.maxPost) && page <= this.maxPage) {
if (last == 0) {
timeline = getConnection().getHome(new Reading().fields("id", "message", "caption", "description", "created_time", "from"));
more = false;
} else {
Reading paging = new Reading();
paging.fields("id", "message", "caption", "description", "created_time", "from");
max = last;
paging.since(new Date(last));
timeline = getConnection().getHome(paging);
if ((timeline == null) || (timeline.size() < 20)) {
more = false;
}
page++;
}
if ((timeline == null) || timeline.isEmpty()) {
log("Empty news feed", Level.FINE);
break;
}
log("Processing posts", Level.INFO, timeline.size());
for (int index = timeline.size() - 1; index >= 0; index--) {
if (count >= this.maxPost) {
log("Max posts", Level.FINE, count);
break;
}
if (this.errors > this.maxErrors) {
log("Max errors", Level.WARNING, this.errors);
break;
}
Post post = timeline.get(index);
String userId = post.getFrom() == null ? "anonymous" : post.getFrom().getId();
String userName = post.getFrom() == null ? "anonymous" : post.getFrom().getName();
log("Processing post", Level.FINE, post.getId(), userName, post.getCaption());
long postTime = post.getCreatedTime().getTime();
String postId = post.getId();
if (postTime > max) {
max = postTime;
}
if (!userId.equals(this.userName)) {
if ((System.currentTimeMillis() - postTime) > DAY) {
log("Day old post", Level.INFO, postId, postTime);
more = false;
continue;
}
if (postTime > last) {
boolean match = false;
String message = post.getMessage();
if (message == null || message.isEmpty()) {
message = post.getCaption();
}
if (getLikeAllPosts()) {
if (like >= this.maxLike) {
log("Max like", Level.FINE, like);
} else {
like++;
like(post);
Utils.sleep(500);
}
}
if (message != null && !message.isEmpty()) {
List<String> postWords = new TextStream(message.toLowerCase()).allWords();
// Like
if (getLikeAllPosts()) {
for (String keywords : getLikeKeywords()) {
List<String> keyWords = new TextStream(keywords.toLowerCase()).allWords();
if (!keyWords.isEmpty()) {
if (postWords.containsAll(keyWords)) {
if (like >= this.maxLike) {
log("Max like", Level.FINE, like);
} else {
like++;
like(post);
Utils.sleep(500);
}
break;
}
}
}
}
// Reply.
for (String text : getNewsFeedKeywords()) {
List<String> keywords = new TextStream(text.toLowerCase()).allWords();
if (!keywords.isEmpty() && postWords.containsAll(keywords)) {
match = true;
break;
}
}
if (match || getProcessAllNewsFeed()) {
count++;
log("Processing post", Level.FINE, post.getCaption(), post.getDescription(), post.getMessage(), userId, userName);
input(post);
Utils.sleep(500);
} else {
log("Skipping post, missing keywords.", Level.FINE, post.getCaption(), post.getDescription(), post.getMessage());
}
} else {
log("Empty message", Level.FINE, post);
}
} else {
log("Old post", Level.INFO, postId, postTime);
}
}
}
}
if (max != 0) {
facebook.setRelationship(Primitive.LASTNEWSFEED, memory.createVertex(max));
memory.save();
}
} catch (Exception exception) {
log(exception);
}
}
/**
* Check RSS feed.
*/
public void checkRSS() {
if (getPostRSS().isEmpty()) {
return;
}
log("Processing RSS", Level.FINE, getPostRSS());
try {
Network memory = getBot().memory().newMemory();
Vertex facebook = memory.createVertex(getPrimitive());
Vertex vertex = facebook.getRelationship(Primitive.LASTRSS);
long last = 0;
if (vertex != null) {
last = ((Number)vertex.getData()).longValue();
}
for (String rss : getPostRSS()) {
TextStream stream = new TextStream(rss);
String prefix = stream.upToAll("http").trim();
if (prefix.isEmpty()) {
prefix = "";
}
prefix = prefix + " ";
String url = stream.nextWord();
String postfix = " " + stream.upToEnd().trim();
List<Map<String, Object>> feed = getBot().awareness().getSense(Http.class).parseRSSFeed(new URL(url), last);
if (feed != null) {
long max = 0;
int count = 0;
this.errors = 0;
for (int index = feed.size() - 1; index >= 0; index--) {
Map<String, Object> entry = feed.get(index);
long time = (Long)entry.get("published");
if ((System.currentTimeMillis() - time) > DAY) {
continue;
}
if (time > last) {
if (count > this.maxFeed) {
break;
}
if (this.errors > this.maxErrors) {
break;
}
String text = (String)entry.get("title");
if (!getRssKeywords().isEmpty()) {
boolean match = false;
List<String> words = new TextStream(text.toLowerCase()).allWords();
for (String keywords : getRssKeywords()) {
List<String> keyWords = new TextStream(keywords.toLowerCase()).allWords();
if (!keyWords.isEmpty()) {
if (words.containsAll(keyWords)) {
match = true;
break;
}
}
}
if (!match) {
log("Skipping RSS, missing keywords", Level.FINE, text);
continue;
}
}
log("Posting RSS", Level.FINE, entry.get("title"));
text = prefix + text + postfix;
if (text.length() > 120) {
text = text.substring(0, 120);
}
post(text + " " + entry.get("link"), null);
Utils.sleep(500);
count++;
if (time > max) {
max = time;
}
}
}
if (max != 0) {
facebook.setRelationship(Primitive.LASTRSS, memory.createVertex(max));
memory.save();
}
}
}
} catch (Exception exception) {
log(exception);
}
}
/**
* Auto post.
*/
public void checkAutoPost() {
if (!getAutoPost()) {
return;
}
log("Autoposting", Level.FINE);
try {
Network memory = getBot().memory().newMemory();
Vertex facebook = memory.createVertex(getPrimitive());
Vertex vertex = facebook.getRelationship(Primitive.LASTPOST);
long last = 0;
if (vertex != null) {
last = ((Timestamp)vertex.getData()).getTime();
}
long millis = getAutoPostHours() * 60 * 60 * 1000;
if ((System.currentTimeMillis() - last) < millis) {
log("Autoposting hours not reached", Level.FINE, getAutoPostHours());
return;
}
List<Vertex> autoposts = getAutoPosts(memory);
if (autoposts != null && !autoposts.isEmpty()) {
int index = Utils.random().nextInt(autoposts.size());
Vertex post = autoposts.get(index);
String text = null;
// Check for labels and formulas
if (post.instanceOf(Primitive.LABEL)) {
post = post.mostConscious(Primitive.RESPONSE);
}
if (post.instanceOf(Primitive.FORMULA)) {
Map<Vertex, Vertex> variables = new HashMap<Vertex, Vertex>();
SelfCompiler.addGlobalVariables(memory.createInstance(Primitive.INPUT), null, memory, variables);
Vertex result = getBot().mind().getThought(Language.class).evaluateFormula(post, variables, memory);
if (result != null) {
text = getBot().mind().getThought(Language.class).getWord(result, memory).getDataValue();
} else {
log("Invalid autopost template formula", Level.WARNING, post);
text = null;
}
} else {
text = post.printString();
}
if (text != null) {
log("Autoposting", Level.INFO, post);
post(text, null);
Utils.sleep(100);
facebook.setRelationship(Primitive.LASTPOST, memory.createTimestamp());
memory.save();
}
}
} catch (Exception exception) {
log(exception);
}
}
public boolean isPage() {
return this.page != null && !this.page.isEmpty();
}
/**
* Return the list of friends names.
*/
public List<String> getFriends() {
List<String> friends = new ArrayList<String>();
try {
if (isPage()) {
return friends;
}
ResponseList<Friend> list = getConnection().getFriends();
for (Friend user : list) {
friends.add(user.getName());
if (friends.size() >= 100) {
// Only return first 100.
break;
}
}
} catch (Exception exception) {
log(exception);
}
return friends;
}
/**
* Return the time-line.
*/
public List<String> getTimeline() {
List<String> timeline = new ArrayList<String>();
try {
ResponseList<Post> statuses = getConnection().getFeed();
if (statuses != null) {
for (Post status : statuses) {
timeline.add(String.valueOf(status.getCreatedTime()) + " - <b>"
+ (status.getFrom() == null ? "" : String.valueOf(status.getFrom().getName())) + "</b>: "
+ String.valueOf(status.getMessage()));
}
}
} catch (Exception exception) {
log(exception);
throw new BotException(exception);
}
return timeline;
}
/**
* Check friends.
*/
public void checkFriends() {
}
/*
public boolean checkFriendship(long friend) throws FacebookException {
long[] lookup = new long[1];
lookup[0] = friend;
ResponseList<User> users = getConnection().lookupUsers(lookup);
User user = users.get(0);
if (user.getName().equals(getUserName())) {
return false;
}
if (!getAutoFriendKeywords().isEmpty()) {
StringWriter writer = new StringWriter();
writer.write(user.getName().toLowerCase());
writer.write(" ");
writer.write(user.getDescription().toLowerCase());
writer.write(" ");
writer.write(user.getLocation().toLowerCase());
writer.write(" ");
writer.write(user.getLang().toLowerCase());
writer.write(" ");
writer.write(user.getName().toLowerCase());
boolean match = false;
for (String text : getAutoFriendKeywords()) {
List<String> keywords = new TextStream(text.toLowerCase()).allWords();
if (new TextStream(writer.toString()).allWords().containsAll(keywords)) {
match = true;
break;
}
}
if (!match) {
log("Autofollow skipping friend, does not match keywords", Level.FINE, user.getName());
return false;
}
}
Network memory = getBot().memory().newMemory();
Vertex speaker = memory.createSpeaker(user.getName());
// Only try to follow a user once.
if (!speaker.hasRelationship(Primitive.FOLLOWED)) {
log("Adding autofollow friend.", Level.INFO, user.getName());
speaker.addRelationship(Primitive.FOLLOWED, memory.createTimestamp());
memory.save();
getConnection().createFriendship(friend);
Utils.sleep(1000);
if (!getWelcomeMessage().isEmpty()) {
sendMessage(getWelcomeMessage(), user.getName());
}
return true;
}
log("Autofollow skipping friend, already followed once", Level.FINE, user.getName());
return false;
}*/
public void log(FacebookException exception) {
log(new TextStream(exception.toString()).nextLine(), Bot.WARNING);
}
/**
* Post.
*/
public void post(String text, String reply) {
this.posts++;
log("Posting:", Level.INFO, text, reply);
try {
if (getConnection() == null) {
connect();
}
if (reply != null) {
getConnection().commentPost(reply, format(text));
} else {
PostUpdate update = new PostUpdate(format(text));
getConnection().postFeed(update);
}
} catch (Exception exception) {
this.errors++;
log(exception.getMessage(), Level.WARNING, text);
}
}
/**
* Prepare and format the text for Facebook.
*/
public String format(String text) {
text = text.replace("\n", "");
text = text.replace("\r", "");
if ((text.indexOf('<') == -1) || (text.indexOf('>') == -1)) {
return text;
}
text = text.replace("<br/>", "\\n");
text = text.replace("<br>", "\\n");
text = text.replace("</br>", "");
text = text.replace("<li>", "\\n");
text = text.replace("</li>", "");
text = Utils.stripTags(text);
return text;
}
/**
* Send a message to the user.
*/
public void sendMessage(String text, String replyUser, String id) {
log("Sending message:", Level.INFO, text, replyUser);
try {
Map<String, String> params = new HashMap<String, String>();
params.put("message", format(text));
getConnection().callPostAPI("/" + id + "/messages", params);
} catch (Exception exception) {
this.errors++;
log(exception);
}
}
/**
* Send a message to the user.
*/
public void sendFacebookMessengerMessage(String text, String replyUser, String id) {
log("Sending messenger message:", Level.INFO, text, replyUser);
try {
if (isPage() && (!getPageId().isEmpty())) {
try {
String url = "https://graph.facebook.com/v2.6/" + getPageId() + "/messages?access_token=" + getFacebookMessengerAccessToken();
String strippedText = format(text);
String postText = null;
// Max size limit
if (strippedText.length() >= 320) {
TextStream stream = new TextStream(strippedText);
while (!stream.atEnd()) {
String message = stream.nextParagraph(320);
String json = null;
if (stream.atEnd()) {
json = "{recipient:{id:\"" + id + "\"}, message:{ text:\"" + Utils.escapeQuotesJS(message) + "\"}}";
} else {
json = "{recipient:{id:\"" + id + "\"}, message:{ text:\"" + Utils.escapeQuotesJS(message) + "\"}}";
}
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
}
} else {
postText = strippedText;
}
Element root = null;
boolean linkFound = false;
// Check for HTML content - first check for link to convert, otherwise images (only one of the other).
// This is complicated as link use button which require the text, and images can't have text.
if ((text.indexOf('<') != -1) && (text.indexOf('>') != -1)) {
try {
root = getBot().awareness().getSense(Http.class).parseHTML(text);
// Find image for template, fb only allows one image.
NodeList imageNodes = root.getElementsByTagName("img");
String image = null;
String imageTitle = "...";
if (imageNodes.getLength() > 0) {
String src = ((Element)imageNodes.item(0)).getAttribute("src");
if (src != null && !src.isEmpty()) {
image = src;
String title = ((Element)imageNodes.item(0)).getAttribute("title");
if (title != null && !title.isEmpty()) {
imageTitle = title;
}
}
}
NodeList nodes = root.getElementsByTagName("a");
List<String> extraButtons = new ArrayList<String>();
if (nodes.getLength() > 0) {
String buttonJSON = "";
String postText2 = postText;
String href = "";
int count = 0;
for (int index = 0; index < nodes.getLength(); index++) {
Element node = (Element)nodes.item(index);
String button = null;
NodeList buttonNodes = node.getElementsByTagName("button");
if (buttonNodes.getLength() > 0) {
String buttonText = buttonNodes.item(0).getTextContent().trim();
if (buttonText != null && !buttonText.isEmpty()) {
button = buttonText;
}
}
href = node.getAttribute("href");
String link = node.getTextContent().trim();
if (link.isEmpty()) {
link = "Link";
}
if (button != null) {
link = button;
}
if (link.length() > 80) {
link = link.substring(0, 80);
}
if (postText2 == null) {
postText2 = link;
}
if (href != null && !href.isEmpty()) {
if (count < 3) {
count++;
if (!buttonJSON.isEmpty()) {
buttonJSON = buttonJSON + ", ";
}
if (href.startsWith("chat:")) {
String chat = href.substring("chat:".length(), href.length()).trim();
buttonJSON = buttonJSON + "{ type: \"postback\", payload: \"" + chat + "\", title: \"" + Utils.escapeQuotesJS(link) + "\"}";
} else {
buttonJSON = buttonJSON + "{ type: \"web_url\", url: \"" + href + "\", title: \"" + Utils.escapeQuotesJS(link) + "\"}";
}
linkFound = true;
} else {
extraButtons.add(button);
}
}
}
if (linkFound) {
String json = null;
if (image == null) {
json = "{recipient:{id:\""
+ id + "\"}, message:{ attachment: { type: \"template\", payload: { template_type: \"button\", text: \""
+ Utils.escapeQuotesJS(postText2) + "\", buttons: [ " + buttonJSON + " ]}}}}";
} else {
// Generic template does not have text.
json = "{recipient:{id:\"" + id + "\"}, message:{ text:\"" + Utils.escapeQuotesJS(postText2) + "\"}}";
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
postText = null;
json = "{recipient:{id:\""
+ id + "\"}, message:{ attachment: { type: \"template\", payload: { template_type: \"generic\", elements:[{ image_url: \""
+ image + "\", title: \"" + imageTitle + "\", item_url: \"" + href + "\", buttons: [ " + buttonJSON + " ]}]}}}}";
}
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
postText = null;
}
} else {
nodes = root.getElementsByTagName("button");
if (nodes.getLength() > 0) {
String buttonJSON = "";
String postText2 = postText;
int count = 0;
for (int index = 0; index < nodes.getLength(); index++) {
Element node = (Element)nodes.item(index);
String button = node.getTextContent().trim();
if (button != null && !button.isEmpty()) {
if (postText2 == null) {
postText2 = button;
}
if (count < 3) {
if (!buttonJSON.isEmpty()) {
buttonJSON = buttonJSON + ", ";
}
button = Utils.escapeQuotesJS(button);
buttonJSON = buttonJSON + "{ type: \"postback\", payload: \"" + button + "\", title: \"" + button + "\"}";
linkFound = true;
} else {
extraButtons.add(button);
}
count++;
}
}
if (linkFound) {
String json = null;
if (image == null) {
json = "{recipient:{id:\""
+ id + "\"}, message:{ attachment: { type: \"template\", payload: { template_type: \"button\", text: \""
+ Utils.escapeQuotesJS(postText2) + "\", buttons: [ " + buttonJSON + " ]}}}}";
} else {
// Generic template does not have text.
json = "{recipient:{id:\"" + id + "\"}, message:{ text:\"" + Utils.escapeQuotesJS(postText) + "\"}}";
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
postText = null;
json = "{recipient:{id:\""
+ id + "\"}, message:{ attachment: { type: \"template\", payload: { template_type: \"generic\", elements:[{ image_url: \""
+ image + "\", title: \"" + imageTitle+ "\", buttons: [ " + buttonJSON + " ]}]}}}}";
}
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
postText = null;
}
}
}
int count = 0;
String buttonJSON = "";
for (String button : extraButtons) {
button = Utils.escapeQuotesJS(button);
if (!buttonJSON.isEmpty()) {
buttonJSON = buttonJSON + ", ";
}
buttonJSON = buttonJSON + "{ type: \"postback\", payload: \"" + button + "\", title: \"" + button + "\"}";
count++;
if (count == 3 || count == extraButtons.size()) {
String json = "{recipient:{id:\""
+ id + "\"}, message:{ attachment: { type: \"template\", payload: { template_type: \"button\", text: \""
+ "..." + "\", buttons: [ " + buttonJSON + " ]}}}}";
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
buttonJSON = "";
count = 0;
}
}
} catch (Exception exception) {
log(exception);
}
}
if (postText != null) {
String json = "{recipient:{id:\"" + id + "\"}, message:{ text:\"" + Utils.escapeQuotesJS(postText) + "\"}}";
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
}
if (!linkFound && (text.indexOf('<') != -1) && (text.indexOf('>') != -1)) {
try {
NodeList nodes = root.getElementsByTagName("img");
for (int index = 0; index < nodes.getLength(); index++) {
Element node = (Element)nodes.item(index);
String src = node.getAttribute("src");
if (src != null && !src.isEmpty()) {
String json = "{recipient:{id:\"" + id + "\"}, message:{ attachment:{ type: \"image\", payload: { url: \"" + src + "\"}}}}";
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
}
}
nodes = root.getElementsByTagName("audio");
for (int index = 0; index < nodes.getLength(); index++) {
Element node = (Element)nodes.item(index);
String src = node.getAttribute("src");
if (src != null && !src.isEmpty()) {
String json = "{recipient:{id:\"" + id + "\"}, message:{ attachment:{ type: \"audio\", payload: { url: \"" + src + "\"}}}}";
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
}
}
nodes = root.getElementsByTagName("video");
for (int index = 0; index < nodes.getLength(); index++) {
Element node = (Element)nodes.item(index);
String src = node.getAttribute("src");
if (src != null && !src.isEmpty()) {
String json = "{recipient:{id:\"" + id + "\"}, message:{ attachment:{ type: \"video\", payload: { url: \"" + src + "\"}}}}";
log("POST", Level.INFO, json);
Utils.httpPOST(url, "application/json", json);
}
}
} catch (Exception exception) {
log(exception);
}
}
} catch (Exception exception) {
this.errors++;
log(exception);
}
} else {
log("Facebook Messenger only allowed for page messaging", Level.WARNING);
}
} catch (Exception exception) {
log(exception);
}
}
/**
* Send a button template message to the user.
*/
public void sendFacebookMessengerButtonMessage(String text, String command, String replyUser, String id) {
log("Sending messenger message:", Level.INFO, text, replyUser);
try {
if (isPage() && (!getPageId().isEmpty())) {
try {
String url = "https://graph.facebook.com/v2.6/" + getPageId() + "/messages?access_token=" + getFacebookMessengerAccessToken();
String strippedText = format(text);
// Max size limit
if (strippedText.length() >= 320) {
TextStream stream = new TextStream(strippedText);
while (!stream.atEnd()) {
String json = null;
String message = stream.nextParagraph(320);
if (stream.atEnd()) {
json = createButtonJSON(command, id, strippedText);
} else {
json = "{recipient:{id:\"" + id + "\"}, message:{ text:\"" + Utils.escapeQuotesJS(message) + "\"}}";
}
Utils.httpPOST(url, "application/json", json);
}
} else {
String json = createButtonJSON(command, id, strippedText);
Utils.httpPOST(url, "application/json", json);
}
//String json = "{recipient:{id:\"" + id + "\"}, message:{ \"attachment\":{\"type\":\"template\",\"payload\":{ \"template_type\":\"button\", \"text\":"+ Utils.escapeQuotesJS(text) + ",\"buttons\":[" + "\"}}}}}";
//Utils.httpPOST(url, "application/json", json);
} catch (Exception exception) {
this.errors++;
log(exception);
}
} else {
log("Facebook Messenger only allowed for page messaging", Level.WARNING);
}
} catch (Exception exception) {
log(exception);
}
}
/**
* Like the post.
*/
public void like(Post post) {
this.likes++;
String userName = post.getFrom() == null ? "anonymous" : post.getFrom().getName();
log("Liking:", Level.INFO, post.getCaption(), userName);
try {
if (getConnection() == null) {
connect();
}
getConnection().likePost(post.getId());
} catch (Exception exception) {
if (exception.getMessage() != null && exception.getMessage().contains("authorized") && exception.getMessage().contains("endpoint")) {
this.errors = this.errors + 5;
}
this.errors++;
log(exception.toString(), Level.WARNING, post.getCaption());
}
}
/**
* Output the post if facebook is connected.
*/
public void outputPost(String post) {
if (!isEnabled()) {
return;
}
Network network = getBot().memory().newMemory();
Vertex setence = network.createSentence(post);
Vertex output = network.createInstance(Primitive.INPUT);
output.setName(post);
output.addRelationship(Primitive.INPUT, setence);
output.addRelationship(Primitive.SENSE, getPrimitive());
output.addRelationship(Primitive.SPEAKER, Primitive.SELF);
output.addRelationship(Primitive.INSTANTIATION, Primitive.POST);
network.createVertex(Primitive.SELF).addRelationship(Primitive.POST, output);
Vertex target = output.mostConscious(Primitive.TARGET);
if (target != null) {
String replyTo = target.mostConscious(Primitive.WORD).getData().toString();
post = "@" + replyTo + " " + post;
}
network.save();
post(post, null);
}
/**
* Process the post.
*/
@Override
public void input(Object input, Network network) {
if (!isEnabled()) {
return;
}
try {
if (input instanceof Post) {
Post post = (Post)input;
log("Processing post", Bot.FINE, post.getCaption(), post.getId());
if ((System.currentTimeMillis() - post.getCreatedTime().getTime()) > DAY) {
log("Day old post", Bot.FINE, post.getId(), post.getCreatedTime().getTime());
return;
}
if (this.processedPosts.contains(post.getId())) {
log("Already processed post", Bot.FINE, post.getCaption(), post.getMessage(), post.getId());
return;
}
this.processedPosts.add(post.getId());
String name = post.getFrom() == null ? "anonymous" : post.getFrom().getName();
String message = post.getMessage();
if (message == null || message.isEmpty()) {
message = post.getCaption();
}
if (message == null || message.isEmpty()) {
log("Empty post", Bot.FINE, post.getCaption(), post.getMessage(), post.getId());
return;
}
String text = message.trim();
log("Input post", Level.FINE, post.getMessage(), name);
this.postsProcessed++;
String target = null;
if (this.wallPosts.contains(post.getId())) {
target = this.userName;
}
inputSentence(text, name, target, post.getId(), post.getCreatedTime().getTime(), network);
} else if (input instanceof Comment) {
Comment comment = (Comment)input;
log("Processing post comment", Bot.FINE, comment.getMessage(), comment.getId());
if ((System.currentTimeMillis() - comment.getCreatedTime().getTime()) > DAY) {
log("Day old post commentt", Bot.FINE, comment.getId(), comment.getCreatedTime().getTime());
return;
}
if (this.processedPosts.contains(comment)) {
log("Already processed post comment", Bot.FINE, comment.getMessage(), comment.getId());
return;
}
this.processedPosts.add(comment.getId());
String name = comment.getFrom() == null ? "anonymous" : comment.getFrom().getName();
String text = comment.getMessage().trim();
log("Input post comment", Level.FINE, comment.getMessage(), name);
this.postsProcessed++;
String id = comment.getId();
if (comment.getParent() != null) {
id = comment.getParent().getId();
}
inputSentence(text, name, this.userName, id, comment.getCreatedTime().getTime(), network);
}
} catch (Exception exception) {
log(exception);
}
}
/**
* Process the post comment.
*/
public void input(Comment comment, Comment parent, Network network) {
if (!isEnabled()) {
return;
}
try {
log("Processing post comment", Bot.FINE, comment.getMessage(), comment.getId());
if ((System.currentTimeMillis() - comment.getCreatedTime().getTime()) > DAY) {
log("Day old post commentt", Bot.FINE, comment.getId(), comment.getCreatedTime().getTime());
return;
}
if (this.processedPosts.contains(comment)) {
log("Already processed post comment", Bot.FINE, comment.getMessage(), comment.getId());
return;
}
this.processedPosts.add(comment.getId());
String name = comment.getFrom() == null ? "anonymous" : comment.getFrom().getName();
String text = comment.getMessage().trim();
log("Input post comment", Level.FINE, comment.getMessage(), name);
this.postsProcessed++;
String id = comment.getId();
if (parent != null) {
id = parent.getId();
}
inputSentence(text, name, this.userName, id, comment.getCreatedTime().getTime(), network);
} catch (Exception exception) {
log(exception);
}
}
/**
* Output the post or direct message reply.
*/
public void output(Vertex output) {
if (!isEnabled()) {
return;
}
Vertex sense = output.mostConscious(Primitive.SENSE);
// If not output to facebook, ignore.
if ((sense == null) || (!getPrimitive().equals(sense.getData()))) {
return;
}
output.addRelationship(Primitive.INSTANTIATION, Primitive.POST);
output.getNetwork().createVertex(Primitive.SELF).addRelationship(Primitive.POST, output);
String text = printInput(output);
Vertex question = output.getRelationship(Primitive.QUESTION);
String reply = null;
if (question != null) {
Vertex id = question.getRelationship(Primitive.ID);
if (id != null) {
reply = (String)id.getData();
}
}
post(text, reply);
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
/**
* Process the text sentence.
*/
public void inputSentence(String text, String userName, String targetUserName, String messageId, long time, Network network) {
Vertex input = createInput(text.trim(), network);
Vertex sentence = input.getRelationship(Primitive.INPUT);
Vertex id = network.createVertex(messageId);
if (sentence.hasRelationship(Primitive.POST, id)) {
log("Post already processed", Bot.FINE, id, time);
return;
}
sentence.addRelationship(Primitive.POST, id);
input.addRelationship(Primitive.INSTANTIATION, Primitive.POST);
input.addRelationship(Primitive.CREATEDAT, network.createVertex(time));
input.addRelationship(Primitive.ID, id);
Vertex conversation = network.createInstance(Primitive.CONVERSATION);
conversation.addRelationship(Primitive.TYPE, Primitive.POST);
Language.addToConversation(input, conversation);
Vertex user = network.createSpeaker(userName);
conversation.addRelationship(Primitive.SPEAKER, user);
input.addRelationship(Primitive.SPEAKER, user);
if (targetUserName != null) {
Vertex targetUser = null;
if (targetUserName.equals(getUserName())) {
targetUser = network.createVertex(Primitive.SELF);
} else {
targetUser = network.createSpeaker(targetUserName);
}
input.addRelationship(Primitive.TARGET, targetUser);
conversation.addRelationship(Primitive.SPEAKER, targetUser);
}
user.addRelationship(Primitive.POST, input);
network.save();
getBot().memory().addActiveMemory(input);
}
/**
* Create an input based on the sentence.
*/
protected Vertex createInput(String text, Network network) {
Vertex sentence = network.createSentence(text);
Vertex input = network.createInstance(Primitive.INPUT);
input.setName(text);
input.addRelationship(Primitive.SENSE, getPrimitive());
input.addRelationship(Primitive.INPUT, sentence);
sentence.addRelationship(Primitive.INSTANTIATION, Primitive.POST);
return input;
}
public String getOauthKey() {
return oauthKey;
}
public void setOauthKey(String oauthKey) {
Facebook.oauthKey = oauthKey;
}
public String getOauthSecret() {
return oauthSecret;
}
public void setOauthSecret(String oauthSecret) {
Facebook.oauthSecret = oauthSecret;
}
public String getToken() {
return token;
}
public Date getTokenExpiry() {
return tokenExpiry;
}
public void setToken(String token) {
this.token = token;
}
public facebook4j.Facebook getConnection() throws FacebookException {
if (connection == null) {
connect();
}
return connection;
}
public void setConnection(facebook4j.Facebook connection) {
this.connection = connection;
}
public boolean getAutoFriend() {
initProperties();
return autoFriend;
}
public void setAutoFriend(boolean autoFriend) {
initProperties();
this.autoFriend = autoFriend;
}
public int getMaxFriends() {
initProperties();
return maxFriends;
}
public void setMaxFriends(int maxFriends) {
initProperties();
this.maxFriends = maxFriends;
}
public boolean getProcessPost() {
initProperties();
return processPost;
}
public void setProcessPost(boolean processPost) {
initProperties();
this.processPost = processPost;
}
public boolean getReplyToMessages() {
initProperties();
return replyToMessages;
}
public boolean getFacebookMessenger() {
initProperties();
return facebookMessenger;
}
public void setFacebookMessenger(boolean facebookMessenger) {
this.facebookMessenger = facebookMessenger;
}
public void setReplyToMessages(boolean replyToMessages) {
this.replyToMessages = replyToMessages;
}
public List<String> getLikeKeywords() {
initProperties();
return likeKeywords;
}
public void setLikeKeywords(List<String> likeKeywords) {
initProperties();
this.likeKeywords = likeKeywords;
}
public List<String> getPostRSS() {
initProperties();
return postRSS;
}
public void setPostRSS(List<String> postRSS) {
initProperties();
this.postRSS = postRSS;
}
public int getMaxPost() {
initProperties();
return maxPost;
}
public void setMaxPost(int maxPost) {
initProperties();
this.maxPost = maxPost;
}
public int getMaxLike() {
initProperties();
return maxLike;
}
public void setMaxLike(int maxLike) {
initProperties();
this.maxLike = maxLike;
}
// Self API
public void post(Vertex source, Vertex sentence) {
if (sentence.instanceOf(Primitive.FORMULA)) {
Map<Vertex, Vertex> variables = new HashMap<Vertex, Vertex>();
SelfCompiler.addGlobalVariables(sentence.getNetwork().createInstance(Primitive.INPUT), null, sentence.getNetwork(), variables);
sentence = getBot().mind().getThought(Language.class).evaluateFormula(sentence, variables, sentence.getNetwork());
if (sentence == null) {
log("Invalid template formula", Level.WARNING, sentence);
return;
}
}
String post = getBot().mind().getThought(Language.class).getWord(sentence, sentence.getNetwork()).getDataValue();
getBot().stat("facebook.post");
post(post, null);
}
private String createButtonJSON(String command, String id, String text) {
command = command.substring(command.indexOf("{"), command.lastIndexOf("}"));
command = command + "}";
JSONObject root = (JSONObject)JSONSerializer.toJSON(command);
if(!root.getString("type").equals("button"))
return "";
JSONObject json = new JSONObject();
JSONObject recipient = new JSONObject();
recipient.put("id", id);
JSONObject message = new JSONObject();
JSONObject attachement = new JSONObject();
attachement.put("type", "template");
JSONObject payload = new JSONObject();
payload.put("template_type", "button");
payload.put("text", text);
JSONArray buttons = new JSONArray();
int buttonIndex = 0;
while(root.optJSONObject("button" + buttonIndex) != null) {
JSONObject buttonJson = root.getJSONObject("button" + buttonIndex);
JSONObject newButton = new JSONObject();
newButton.put("title", buttonJson.getString("caption"));
if(buttonJson.opt("type")!=null) {
newButton.put("type", buttonJson.getString("type"));
} else {
newButton.put("type", "postback");
}
//newButton.put("type", "postback");
if(newButton.getString("type").equals("postback") || newButton.getString("type").equals("phone_number") )
newButton.put("payload", buttonJson.optString("chat"));
else if(newButton.getString("type").equals("web_url"))
newButton.put("url", buttonJson.optString("chat"));
buttons.add(newButton);
buttonIndex++;
}
/*JSONObject button0 = new JSONObject();
button0.put("type", "web_url");
button0.put("url", "http://www.botlibre.com");
button0.put("title", "Bot Libre Website");
JSONObject button1 = new JSONObject();
button1.put("type", "postback");
button1.put("title", "Chat");
button1.put("payload", "chat");
buttons.add(button0);
buttons.add(button1);*/
payload.put("buttons", buttons);
attachement.put("payload", payload);
message.put("attachment", attachement);
json.put("recipient", recipient);
json.put("message", message);
return json.toString();
}
}