/****************************************************************************** * * 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; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; 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.api.sense.ExceptionEventListener; import org.botlibre.api.sense.Sense; import org.botlibre.emotion.EmotionalState; import org.botlibre.knowledge.Primitive; import org.botlibre.thought.language.Language.LanguageState; import org.botlibre.util.Utils; /** * Defines an external interface. * i.e. * - text * - voice * - hearing * - vision */ public class BasicSense implements Sense { public static long MINUTE = 1000 * 60; public static long HOUR = 1000 * 60 * 60; public static long DAY = 1000 * 60 * 60 * 24; /** Number of attempt to retry sensory input on failure. */ public static int RETRY = 3; public static int MAX_FILE_SIZE = 10000000; // 10 meg /** Default user if none specified. */ public static String DEFAULT_SPEAKER = "Anonymous"; /** This default name is the class name. */ protected String name; /** Allow the voice to be disabled. */ protected boolean isEnabled; /** * Defines the current language conversational state for the sense. * This allows each sense to have its own conversational state. */ protected LanguageState languageState; /** * Defines the current emotional state for the sense. * This allows each sense to have its own emotional state. */ protected EmotionalState emotionalState; protected String action; /** Back reference to Bot instance. **/ protected Bot bot; protected List<ExceptionEventListener> listeners; public BasicSense() { this.name = getClass().getName(); this.isEnabled = true; this.languageState = LanguageState.Answering; this.emotionalState = EmotionalState.NONE; this.listeners = new ArrayList<ExceptionEventListener>(); } /** * Return if the sense is enabled. */ @Override public boolean isEnabled() { return isEnabled; } /** * Allow the sense to disabled/enabled. */ @Override public void setIsEnabled(boolean isEnabled) { this.isEnabled = isEnabled; } /** * Return the current conversational state. */ public LanguageState getLanguageState() { return this.languageState; } /** * Set the current conversational state. */ public void setLanguageState(LanguageState languageState) { this.languageState = languageState; } /** * Return the current conversational mood. */ public EmotionalState getEmotionalState() { return this.emotionalState; } /** * Set the current conversational mood. */ public void setEmotionalState(EmotionalState emotionalState) { if (emotionalState == null) { this.emotionalState = EmotionalState.NONE; } else { this.emotionalState = emotionalState; } } public String getAction() { return this.action; } /** * Set the current action. */ public void setAction(String action) { if (action == null || action.isEmpty()) { this.action = null; } else { this.action = action; } } /** * Start sensing. */ @Override public void awake() { getBot().log(this, "Awake", Bot.FINE); } /** * Migrate to new properties system. */ public void migrateProperties() { } /** * Stop sensing. */ @Override public void shutdown() { getBot().log(this, "Shutdown", Bot.FINE); setIsEnabled(false); } /** * Reset state when instance is pooled. */ @Override public void pool() { } /** * Receive any input from the sense. */ @Override public void input(Object input) { int attempt = 0; Exception failure = null; while (attempt < RETRY) { if (!isEnabled()) { return; } attempt++; try { Network network = getBot().memory().newMemory(); input(input, network); network.save(); return; } catch (BotException error) { log(error.toString(), Bot.WARNING); throw error; } catch (Exception failed) { failure = failed; log(failed.toString(), Bot.WARNING); log("Retrying", Bot.WARNING); } } log("Retry failed", Bot.WARNING); log(failure); notifyExceptionListeners(failure); } /** * Receive any input from the sense. */ @Override public void input(Object input, Network network) throws Exception { } /** * Output the active network to the sense. */ @Override public void output(Vertex output) { } /** * Return the name that identifies the sense. */ @Override public String getName() { return name; } /** * Set the name that identifies the sense. */ @Override public void setName(String name) { this.name = name; } /** * Return the short term memory. */ public Network getShortTermMemory() { return getBot().memory().getShortTermMemory(); } /** * Log the message if the debug level is greater or equal to the level. */ public void log(String message, Level level, Object... arguments) { getBot().log(this, message, level, arguments); } /** * Log the message if the debug level is greater or equal to the level. */ public void log(String message, Level level) { getBot().log(this, message, level); } /** * Log the exception. */ public void log(Throwable error) { getBot().log(this, error); } public Primitive getPrimitive() { return new Primitive(getName()); } @Override public String toString() { return getClass().getSimpleName(); } /** * Create an input based on the sentence. */ protected Vertex createInputParagraph(String text, Network network) { if (getBot().getFilterProfanity()) { if (Utils.checkProfanity(text)) { throw BotException.offensive(); } } Utils.checkScript(text); Vertex sentence = network.createParagraph(text); if (sentence.hasRelationship(Primitive.ASSOCIATED, Primitive.OFFENSIVE)) { throw BotException.offensive(); } if (sentence.instanceOf(Primitive.PARAGRAPH)) { Collection<Relationship> relationships = sentence.getRelationships(Primitive.SENTENCE); for (Relationship relationship : relationships) { if (relationship.getTarget().hasRelationship(Primitive.ASSOCIATED, Primitive.OFFENSIVE)) { throw BotException.offensive(); } } } Vertex input = network.createInstance(Primitive.INPUT); input.setName(text); input.addRelationship(Primitive.SENSE, getPrimitive()); input.addRelationship(Primitive.INPUT, sentence); return input; } /** * Create an input based on the sentence. */ protected Vertex createInputSentence(String text, Network network) { if (getBot().getFilterProfanity()) { if (Utils.checkProfanity(text)) { throw BotException.offensive(); } } Utils.checkScript(text); Vertex sentence = network.createSentence(text); if (sentence.hasRelationship(Primitive.ASSOCIATED, Primitive.OFFENSIVE)) { throw BotException.offensive(); } Vertex input = network.createInstance(Primitive.INPUT); input.setName(text); input.addRelationship(Primitive.SENSE, getPrimitive()); input.addRelationship(Primitive.INPUT, sentence); return input; } /** * Return the associated Bot instance. */ @Override public Bot getBot() { return bot; } /** * Set the associated Bot instance. */ @Override public void setBot(Bot Bot) { this.bot = Bot; } /** * Initialize any configurable settings from the properties. */ @Override public void initialize(Map<String, Object> properties) { return; } /** * Attempt to discover information on the vertex. */ public boolean discover(Vertex input, Network network, Vertex currentTime) { Vertex sentence = input.getRelationship(Primitive.INPUT); if (sentence != null) { if (sentence.instanceOf(Primitive.PARAGRAPH)) { Collection<Relationship> sentences = sentence.getRelationships(Primitive.SENTENCE); if (sentences != null) { for (Relationship relationship : sentences) { return checkSentence(relationship.getTarget(), network, currentTime); } } } else if (sentence.instanceOf(Primitive.SENTENCE)) { return checkSentence(sentence, network, currentTime); } } return false; } /** * Check if the sentence has been discovered. */ public boolean checkSentence(Vertex sentence, Network network, Vertex currentTime) { Vertex lastChecked = sentence.getRelationship(getPrimitive()); if (lastChecked == null) { log("Discovering sentence:", Bot.FINE, sentence); discoverSentence(sentence, network, currentTime); sentence.addRelationship(getPrimitive(), currentTime); return true; } return false; } /** * Attempt to discover information on the sentence words. */ public void discoverSentence(Vertex sentence, Network network, Vertex currentTime) { } /** * Convert the input into text. */ public String printInput(Vertex input) { Vertex sentence = input.getRelationship(Primitive.INPUT); if (sentence != null) { return sentence.printString(); } return ""; } public synchronized void notifyExceptionListeners(Exception exception) { for (ExceptionEventListener listener : getListeners()) { listener.notify(exception); } } public List<ExceptionEventListener> getListeners() { return listeners; } protected void setListeners(List<ExceptionEventListener> listeners) { this.listeners = listeners; } /** * Add the exception listener. * It will be notified of any exceptions. */ public synchronized void addListener(ExceptionEventListener listener) { if (!this.listeners.contains(listener)) { this.listeners.add(listener); } } public synchronized void removeListener(ExceptionEventListener listener) { this.listeners.remove(listener); } public void saveProperties() { } }