/******************************************************************************
*
* Copyright 2016 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.sms;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import org.botlibre.api.knowledge.Network;
import org.botlibre.api.knowledge.Vertex;
import org.botlibre.knowledge.Primitive;
import org.botlibre.self.SelfCompiler;
import org.botlibre.sense.BasicSense;
import org.botlibre.thought.language.Language;
import org.botlibre.util.Utils;
/**
* Receive and respond to incoming email.
* Can use any pop mail server, such as gmail.
*/
public class Twilio extends BasicSense {
public static int MAX_WAIT = 60 * 1000; // 1 minute
protected String sid = "";
protected String secret = "";
protected String phone = "";
protected boolean initProperties;
protected SMSListener listener;
public Twilio() {
}
/**
* Start sensing.
*/
@Override
public void awake() {
}
/**
* Load settings.
*/
public void initProperties() {
if (this.initProperties) {
return;
}
synchronized (this) {
if (this.initProperties) {
return;
}
getBot().memory().loadProperties("Twilio");
String property = this.bot.memory().getProperty("Twilio.sid");
if (property != null) {
this.sid = property;
}
property = this.bot.memory().getProperty("Twilio.secret");
if ((property != null) && (this.sid != null)) {
this.secret = Utils.decrypt(Utils.KEY, property);
}
property = this.bot.memory().getProperty("Twilio.phone");
if (property != null) {
this.phone = property;
}
this.initProperties = true;
}
}
public void saveProperties() {
Network memory = getBot().memory().newMemory();
memory.saveProperty("Twilio.sid", this.sid, false);
memory.saveProperty("Twilio.secret", Utils.encrypt(Utils.KEY, this.secret), false);
memory.saveProperty("Twilio.phone", this.phone, false);
memory.save();
}
/**
* 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.SMS);
return input;
}
/**
* Process the text sentence.
*/
public void inputSentence(String text, String userName, String id, Network network) {
Vertex input = createInput(text.trim(), network);
Vertex user = network.createSpeaker(userName);
Vertex self = network.createVertex(Primitive.SELF);
input.addRelationship(Primitive.SPEAKER, user);
input.addRelationship(Primitive.TARGET, self);
user.addRelationship(Primitive.INPUT, input);
Vertex conversation = network.createVertex(id);
conversation.addRelationship(Primitive.INSTANTIATION, Primitive.CONVERSATION);
conversation.addRelationship(Primitive.TYPE, Primitive.SMS);
conversation.addRelationship(Primitive.ID, network.createVertex(id));
conversation.addRelationship(Primitive.SPEAKER, user);
conversation.addRelationship(Primitive.SPEAKER, self);
Language.addToConversation(input, conversation);
network.save();
getBot().memory().addActiveMemory(input);
}
/**
* Process to the message and reply synchronously.
*/
public String processMessage(String from, String message) {
log("Processing message", Level.INFO, from, message);
this.listener = new SMSListener();
Network memory = bot.memory().newMemory();
inputSentence(message, from, from, memory);
memory.save();
String reply = null;
synchronized (this.listener) {
if (this.listener.reply == null) {
try {
this.listener.wait(MAX_WAIT);
} catch (Exception exception) {
log(exception);
return "";
}
}
reply = this.listener.reply;
this.listener = null;
}
return reply;
}
public synchronized void notifyExceptionListeners(Exception exception) {
if (this.listener != null) {
this.listener.notifyAll();
}
super.notifyExceptionListeners(exception);
}
/**
* Output the SMS message.
*/
@Override
public void output(Vertex output) {
if (!isEnabled()) {
return;
}
Vertex sense = output.mostConscious(Primitive.SENSE);
// If not output to sms, ignore.
if ((sense == null) || (!getPrimitive().equals(sense.getData()))) {
return;
}
String text = printInput(output);
if (this.listener == null) {
return;
}
this.listener.reply = text;
Vertex conversation = output.getRelationship(Primitive.CONVERSATION);
if (conversation != null) {
this.listener.conversation = conversation.getDataValue();
}
synchronized (this.listener) {
this.listener.notifyAll();
}
}
public String getSid() {
initProperties();
return sid;
}
public void setSid(String sid) {
initProperties();
this.sid = sid;
}
public String getPhone() {
initProperties();
return phone;
}
public void setPhone(String phone) {
initProperties();
this.phone = phone;
}
public String getSecret() {
initProperties();
return secret;
}
public void setSecret(String secret) {
initProperties();
this.secret = secret;
}
public SMSListener getListener() {
return listener;
}
public void setListener(SMSListener listener) {
this.listener = listener;
}
public void sendSMS(String phone, String message) {
log("Sending SMS", Level.INFO, phone, message);
String url = "https://api.twilio.com/2010-04-01/Accounts/" + getSid() + "/Messages";
Map<String, String> formParams = new HashMap<String, String>();
formParams.put("From", getPhone());
formParams.put("To", phone);
formParams.put("Body", message);
try {
Utils.httpAuthPOST(url, getSid(), getSecret(), formParams);
} catch (Exception error) {
log(error);
}
}
// Self API
public void sms(Vertex source, Vertex phone, Vertex message) {
if (message.instanceOf(Primitive.FORMULA)) {
Map<Vertex, Vertex> variables = new HashMap<Vertex, Vertex>();
SelfCompiler.addGlobalVariables(message.getNetwork().createInstance(Primitive.INPUT), null, message.getNetwork(), variables);
message = getBot().mind().getThought(Language.class).evaluateFormula(message, variables, message.getNetwork());
if (message == null) {
log("Invalid template formula", Level.WARNING, message);
return;
}
}
String post = getBot().mind().getThought(Language.class).getWord(message, message.getNetwork()).printString();
getBot().stat("sms");
sendSMS(phone.printString(), post);
}
}