class ResetLinkModel: """Database model to store generated reset links.""" def __init__(self): self.db = Database(variables['db']) def generate(self): random_string = ''.join( random.choice('abcdefghijklmnopqrstuvwxyz1234567890') for _ in range(30)) dt = datetime.now() + timedelta(days=+1) dt_string = dt.strftime("%Y%m%d%H%M%S") reset_string = f'{random_string}-{dt_string}' return reset_string def _check_expire(self, resetlink): now = datetime.now() dt_expire_string = resetlink.split('-')[1] dt_expire = datetime.strptime(dt_expire_string, "%Y%m%d%H%M%S") return dt_expire > now def add(self, resetlink, email): self.db.data(key=resetlink, value=email) def get(self, resetlink): return self.db.data(key=resetlink) def exists(self, resetlink): if resetlink in self.db: return self._check_expire(resetlink) return False def delete(self, resetlink): self.db.delete(resetlink)
class Cache: """User credentials data Cache. Note: Stores user's username and password in JSON database to decrease requests count to authentication backend. All users password values are stored encrypted only. User password stores because of it can be changed anytime at authentication backend side. And this should be handled. Thus, credentials should be stored and validated in authentication backend every cache expiration period. """ def __init__(self, expirationMinutes): self.expirationMinutes = expirationMinutes self.cache = {} self.validUntil = datetime.now( timezone('UTC')) + timedelta(minutes=self.expirationMinutes) self.logger = logging.getLogger(__class__.__name__) self.db = Database(variables['db']) self.cryptor = Fernet(Fernet.generate_key()) def add(self, username, password, password_encrypted): if password_encrypted: pswd = password else: pswd = self.cryptor.encrypt(password.encode()) self.db.data(key=username, value={ 'password': pswd, 'ttl': datetime.now(timezone('UTC')) + timedelta(minutes=self.expirationMinutes) }) self.logger.debug( f'User cache record for username {username} has been added.') def get(self, username): return self.db.data(key=username) def validate(self, username, password): if username in self.db: if self.db.data(key=username)['password'] == password: ttl = self.db.data(key=username)['ttl'] if ttl > datetime.now(timezone('UTC')): return True self.logger.debug( f'User cache record for username {username} was not found or expired.' ) self.db.delete(username) return False def decrypt(self, value): return self.cryptor.decrypt(value).decode()
class JsonFileStorageAdapter(StorageAdapter): """ This adapter allows ChatterBot to store conversation data in a file in JSON format. """ def __init__(self, **kwargs): super(JsonFileStorageAdapter, self).__init__(**kwargs) if not kwargs.get('silence_performance_warning', False): warnings.warn( 'The JsonFileStorageAdapter is not recommended for production application environments.', self.UnsuitableForProductionWarning ) database_path = self.kwargs.get('database', 'database.db') self.database = Database(database_path) self.adapter_supports_queries = False def _keys(self): # The value has to be cast as a list for Python 3 compatibility return list(self.database[0].keys()) def count(self): return len(self._keys()) def find(self, statement_text): values = self.database.data(key=statement_text) if not values: return None values['text'] = statement_text return self.json_to_object(values) def remove(self, statement_text): """ Removes the statement that matches the input text. Removes any responses from statements if the response text matches the input text. """ for statement in self.filter(in_response_to__contains=statement_text): statement.remove_response(statement_text) self.update(statement) self.database.delete(statement_text) def deserialize_responses(self, response_list): """ Takes the list of response items and returns the list converted to Response objects. """ proxy_statement = Statement('') for response in response_list: data = response.copy() text = data['text'] del(data['text']) proxy_statement.add_response( Response(text, **data) ) return proxy_statement.in_response_to def json_to_object(self, statement_data): # Don't modify the referenced object statement_data = statement_data.copy() # Build the objects for the response list statement_data['in_response_to'] = self.deserialize_responses( statement_data['in_response_to'] ) # Remove the text attribute from the values text = statement_data.pop('text') return Statement(text, **statement_data) def _all_kwargs_match_values(self, kwarguments, values): for kwarg in kwarguments: if '__' in kwarg: kwarg_parts = kwarg.split('__') key = kwarg_parts[0] identifier = kwarg_parts[1] if identifier == 'contains': text_values = [] for val in values[key]: text_values.append(val['text']) if (kwarguments[kwarg] not in text_values) and ( kwarguments[kwarg] not in values[key]): return False if kwarg in values: if values[kwarg] != kwarguments[kwarg]: return False return True def filter(self, **kwargs): """ Returns a list of statements in the database that match the parameters specified. """ results = [] for key in self._keys(): values = self.database.data(key=key) # Add the text attribute to the values values['text'] = key if self._all_kwargs_match_values(kwargs, values): results.append(self.json_to_object(values)) return results def update(self, statement, **kwargs): # Do not alter the database unless writing is enabled if not self.read_only: data = statement.serialize() # Remove the text key from the data del(data['text']) self.database.data(key=statement.text, value=data) # Make sure that an entry for each response exists for response_statement in statement.in_response_to: response = self.find(response_statement.text) if not response: response = Statement(response_statement.text) self.update(response) return statement def get_random(self): from random import choice if self.count() < 1: raise self.EmptyDatabaseException() statement = choice(self._keys()) return self.find(statement) def drop(self): """ Remove the json file database completely. """ import os if os.path.exists(self.database.path): os.remove(self.database.path) class UnsuitableForProductionWarning(Warning): pass
class JsonFileStorageAdapter(StorageAdapter): """ This adapter allows ChatterBot to store conversation data in a file in JSON format. """ def __init__(self, **kwargs): super(JsonFileStorageAdapter, self).__init__(**kwargs) database_path = self.kwargs.get('database', 'database.db') self.database = Database(database_path) def _keys(self): # The value has to be cast as a list for Python 3 compatibility return list(self.database[0].keys()) def count(self): return len(self._keys()) def find(self, statement_text): values = self.database.data(key=statement_text) if not values: return None values['text'] = statement_text return self.json_to_object(values) def remove(self, statement_text): """ Removes the statement that matches the input text. Removes any responses from statements if the response text matches the input text. """ for statement in self.filter(in_response_to__contains=statement_text): statement.remove_response(statement_text) self.update(statement) self.database.delete(statement_text) def deserialize_responses(self, response_list): """ Takes the list of response items and returns the list converted to Response objects. """ proxy_statement = Statement('') for response in response_list: data = response.copy() text = data['text'] del (data['text']) proxy_statement.add_response(Response(text, **data)) return proxy_statement.in_response_to def json_to_object(self, statement_data): # Don't modify the referenced object statement_data = statement_data.copy() # Build the objects for the response list statement_data['in_response_to'] = self.deserialize_responses( statement_data['in_response_to']) # Remove the text attribute from the values text = statement_data.pop('text') return Statement(text, **statement_data) def _all_kwargs_match_values(self, kwarguments, values): for kwarg in kwarguments: if '__' in kwarg: kwarg_parts = kwarg.split('__') key = kwarg_parts[0] identifier = kwarg_parts[1] if identifier == 'contains': text_values = [] for val in values[key]: text_values.append(val['text']) if (kwarguments[kwarg] not in text_values) and (kwarguments[kwarg] not in values[key]): return False if kwarg in values: if values[kwarg] != kwarguments[kwarg]: return False return True def filter(self, **kwargs): """ Returns a list of statements in the database that match the parameters specified. """ results = [] for key in self._keys(): values = self.database.data(key=key) # Add the text attribute to the values values['text'] = key if self._all_kwargs_match_values(kwargs, values): results.append(self.json_to_object(values)) return results def update(self, statement): # Do not alter the database unless writing is enabled if not self.read_only: data = statement.serialize() # Remove the text key from the data del (data['text']) self.database.data(key=statement.text, value=data) # Make sure that an entry for each response exists for response_statement in statement.in_response_to: response = self.find(response_statement.text) if not response: response = Statement(response_statement.text) self.update(response) return statement def get_random(self): from random import choice if self.count() < 1: raise self.EmptyDatabaseException() statement = choice(self._keys()) return self.find(statement) def drop(self): """ Remove the json file database completely. """ import os if os.path.exists(self.database.path): os.remove(self.database.path) class UnsuitableForProductionWarning(Warning): pass
class JsonDatabaseAdapter(StorageAdapter): def __init__(self, **kwargs): super(JsonDatabaseAdapter, self).__init__(**kwargs) database_path = self.kwargs.get("database", "database.db") self.database = Database(database_path) def _keys(self): # The value has to be cast as a list for Python 3 compatibility return list(self.database[0].keys()) def count(self): return len(self._keys()) def find(self, statement_text): values = self.database.data(key=statement_text) if not values: return None # Build the objects for the response list response_list = self._objectify_response_list(values["in_response_to"]) values["in_response_to"] = response_list return Statement(statement_text, **values) def _objectify_response_list(self, response_list): """ Takes the list of response items and returns the list converted to object versions of the responses. """ in_response_to = [] for item in response_list: text = item[0] occurrence = item[1] in_response_to.append( Response(text, occurrence=occurrence) ) return in_response_to def _all_kwargs_match_values(self, kwarguments, values): for kwarg in kwarguments: if "__" in kwarg: kwarg_parts = kwarg.split("__") if kwarg_parts[1] == "contains": text_values = [] for val in values[kwarg_parts[0]]: text_values.append(val[0]) if (kwarguments[kwarg] not in text_values) and (kwarguments[kwarg] not in values[kwarg_parts[0]]): return False if kwarg in values: if values[kwarg] != kwarguments[kwarg]: return False return True def filter(self, **kwargs): """ Returns a list of statements in the database that match the parameters specified. """ results = [] for key in self._keys(): values = self.database.data(key=key) if self._all_kwargs_match_values(kwargs, values): # Build the objects for the response list response_list = self._objectify_response_list(values["in_response_to"]) values["in_response_to"] = response_list results.append( Statement(key, **values) ) return results def update(self, statement): # Do not alter the database unless writing is enabled if not self.read_only: data = statement.serialize() # Remove the text key from the data del(data['text']) self.database.data(key=statement.text, value=data) # Make sure that an entry for each response is saved for response_statement in statement.in_response_to: response = self.find(response_statement.text) if not response: response = Statement(response_statement.text) self.update(response) return statement def get_random(self): from random import choice if self.count() < 1: raise EmptyDatabaseException() statement = choice(self._keys()) return self.find(statement) def drop(self): """ Remove the json file database completely. """ import os os.remove(self.database.path)
class JsonFileStorageAdapter(StorageAdapter): """ This adapter allows ChatterBot to store conversation data in a file in JSON format. :keyword database: The path to the json file you wish to store data in. :type database: str :keyword silence_performance_warning: If set to True, the :code:`UnsuitableForProductionWarning` will not be displayed. :type silence_performance_warning: bool """ def __init__(self, **kwargs): super(JsonFileStorageAdapter, self).__init__(**kwargs) from jsondb import Database if not kwargs.get('silence_performance_warning', False): warnings.warn( 'The JsonFileStorageAdapter is not recommended for production environments.', self.UnsuitableForProductionWarning ) warnings.warn( 'The JsonFileStorageAdapter is deprecated and ' 'will be removed in ChatterBot version 0.8.', DeprecationWarning ) database_path = self.kwargs.get('database', 'database.db') self.database = Database(database_path) self.adapter_supports_queries = False def _keys(self): # The value has to be cast as a list for Python 3 compatibility return list(self.database[0].keys()) def count(self): return len(self._keys()) def find(self, statement_text): values = self.database.data(key=statement_text) if not values: return None values['text'] = statement_text return self.json_to_object(values) def remove(self, statement_text): """ Removes the statement that matches the input text. Removes any responses from statements if the response text matches the input text. """ for statement in self.filter(in_response_to__contains=statement_text): statement.remove_response(statement_text) self.update(statement) self.database.delete(statement_text) def deserialize_responses(self, response_list): """ Takes the list of response items and returns the list converted to Response objects. """ proxy_statement = self.Statement('') for response in response_list: data = response.copy() text = data['text'] del data['text'] proxy_statement.add_response( Response(text, **data) ) return proxy_statement.in_response_to def json_to_object(self, statement_data): """ Converts a dictionary-like object to a Statement object. """ # Don't modify the referenced object statement_data = statement_data.copy() # Build the objects for the response list statement_data['in_response_to'] = self.deserialize_responses( statement_data['in_response_to'] ) # Remove the text attribute from the values text = statement_data.pop('text') return self.Statement(text, **statement_data) def _all_kwargs_match_values(self, kwarguments, values): for kwarg in kwarguments: if '__' in kwarg: kwarg_parts = kwarg.split('__') key = kwarg_parts[0] identifier = kwarg_parts[1] if identifier == 'contains': text_values = [] for val in values[key]: text_values.append(val['text']) if (kwarguments[kwarg] not in text_values) and ( kwarguments[kwarg] not in values[key]): return False if kwarg in values: if values[kwarg] != kwarguments[kwarg]: return False return True def filter(self, **kwargs): """ Returns a list of statements in the database that match the parameters specified. """ from operator import attrgetter results = [] order_by = kwargs.pop('order_by', None) for key in self._keys(): values = self.database.data(key=key) # Add the text attribute to the values values['text'] = key if self._all_kwargs_match_values(kwargs, values): results.append(self.json_to_object(values)) if order_by: # Sort so that newer datetimes appear first is_reverse = order_by == 'created_at' # Do an in place sort of the results results.sort(key=attrgetter(order_by), reverse=is_reverse) return results def update(self, statement): """ Update a statement in the database. """ data = statement.serialize() # Remove the text key from the data del data['text'] self.database.data(key=statement.text, value=data) # Make sure that an entry for each response exists for response_statement in statement.in_response_to: response = self.find(response_statement.text) if not response: response = self.Statement(response_statement.text) self.update(response) return statement def get_random(self): from random import choice if self.count() < 1: raise self.EmptyDatabaseException() statement = choice(self._keys()) return self.find(statement) def drop(self): """ Remove the json file database completely. """ self.database.drop() class UnsuitableForProductionWarning(Warning): """ The json file storage adapter will display an :code:`UnsuitableForProductionWarning` when it is initialized because it is not intended for use in large scale production applications. You can silence this warning by setting :code:`silence_performance_warning=True` when initializing the adapter. """ pass
class JsonDatabaseAdapter(StorageAdapter): """ The JsonDatabaseAdapter is an interface that allows ChatterBot to store the conversation as a Json-encoded file. """ def __init__(self, **kwargs): super(JsonDatabaseAdapter, self).__init__(**kwargs) database_path = self.kwargs.get("database", "database.db") self.database = Database(database_path) def _keys(self): # The value has to be cast as a list for Python 3 compatibility return list(self.database[0].keys()) def count(self): return len(self._keys()) def find(self, statement_text): values = self.database.data(key=statement_text) if not values: return None # Build the objects for the response list response_list = self.deserialize_responses(values["in_response_to"]) values["in_response_to"] = response_list return Statement(statement_text, **values) def remove(self, statement_text): """ Removes the statement that matches the input text. Removes any responses from statements if the response text matches the input text. """ for statement in self.filter(in_response_to__contains=statement_text): statement.remove_response(statement_text) self.update(statement) self.database.delete(statement_text) def deserialize_responses(self, response_list): """ Takes the list of response items and returns the list converted to object versions of the responses. """ in_response_to = [] for response in response_list: text = response["text"] del (response["text"]) in_response_to.append(Response(text, **response)) return in_response_to def _all_kwargs_match_values(self, kwarguments, values): for kwarg in kwarguments: if "__" in kwarg: kwarg_parts = kwarg.split("__") key = kwarg_parts[0] identifier = kwarg_parts[1] if identifier == "contains": text_values = [] for val in values[key]: text_values.append(val["text"]) if (kwarguments[kwarg] not in text_values) and (kwarguments[kwarg] not in values[key]): return False if kwarg in values: if values[kwarg] != kwarguments[kwarg]: return False return True def filter(self, **kwargs): """ Returns a list of statements in the database that match the parameters specified. """ results = [] for key in self._keys(): values = self.database.data(key=key) # Add the text attribute to the values values["text"] = key if self._all_kwargs_match_values(kwargs, values): # Build the objects for the response list in_response_to = values["in_response_to"] response_list = self.deserialize_responses(in_response_to) values["in_response_to"] = response_list # Remove the text attribute from the values text = values.pop("text") results.append(Statement(text, **values)) return results def update(self, statement): # Do not alter the database unless writing is enabled if not self.read_only: data = statement.serialize() # Remove the text key from the data del (data['text']) self.database.data(key=statement.text, value=data) # Make sure that an entry for each response exists for response_statement in statement.in_response_to: response = self.find(response_statement.text) if not response: response = Statement(response_statement.text) self.update(response) return statement def get_random(self): from random import choice if self.count() < 1: raise EmptyDatabaseException() statement = choice(self._keys()) return self.find(statement) def drop(self): """ Remove the json file database completely. """ import os if os.path.exists(self.database.path): os.remove(self.database.path)
class JsonDatabaseAdapter(StorageAdapter): """ The JsonDatabaseAdapter is an interface that allows ChatterBot to store the conversation as a Json-encoded file. """ def __init__(self, **kwargs): super(JsonDatabaseAdapter, self).__init__(**kwargs) database_path = self.kwargs.get("database", "database.db") self.database = Database(database_path) def _keys(self): # The value has to be cast as a list for Python 3 compatibility return list(self.database[0].keys()) def count(self): return len(self._keys()) def find(self, statement_text): values = self.database.data(key=statement_text) if not values: return None # Build the objects for the response list response_list = self.deserialize_responses(values["in_response_to"]) values["in_response_to"] = response_list return Statement(statement_text, **values) def remove(self, statement_text): """ Removes the statement that matches the input text. Removes any responses from statements if the response text matches the input text. """ for statement in self.filter(in_response_to__contains=statement_text): statement.remove_response(statement_text) self.update(statement) self.database.delete(statement_text) def deserialize_responses(self, response_list): """ Takes the list of response items and returns the list converted to object versions of the responses. """ in_response_to = [] for response in response_list: text = response["text"] del(response["text"]) in_response_to.append( Response(text, **response) ) return in_response_to def _all_kwargs_match_values(self, kwarguments, values): for kwarg in kwarguments: if "__" in kwarg: kwarg_parts = kwarg.split("__") key = kwarg_parts[0] identifier = kwarg_parts[1] if identifier == "contains": text_values = [] for val in values[key]: text_values.append(val["text"]) if (kwarguments[kwarg] not in text_values) and ( kwarguments[kwarg] not in values[key]): return False if kwarg in values: if values[kwarg] != kwarguments[kwarg]: return False return True def filter(self, **kwargs): """ Returns a list of statements in the database that match the parameters specified. """ results = [] for key in self._keys(): values = self.database.data(key=key) # Add the text attribute to the values values["text"] = key if self._all_kwargs_match_values(kwargs, values): # Build the objects for the response list in_response_to = values["in_response_to"] response_list = self.deserialize_responses(in_response_to) values["in_response_to"] = response_list # Remove the text attribute from the values text = values.pop("text") results.append( Statement(text, **values) ) return results def update(self, statement): # Do not alter the database unless writing is enabled if not self.read_only: data = statement.serialize() # Remove the text key from the data del(data['text']) self.database.data(key=statement.text, value=data) # Make sure that an entry for each response exists for response_statement in statement.in_response_to: response = self.find(response_statement.text) if not response: response = Statement(response_statement.text) self.update(response) return statement def get_random(self): from random import choice if self.count() < 1: raise self.EmptyDatabaseException() statement = choice(self._keys()) return self.find(statement) def drop(self): """ Remove the json file database completely. """ import os if os.path.exists(self.database.path): os.remove(self.database.path)
class JsonDatabaseAdapter(StorageAdapter): """ The JsonDatabaseAdapter is an interface that allows ChatterBot to store the conversation as a Json-encoded file. """ def create_user_storage(self, **kwargs): _kwargs = self.kwargs.copy() _kwargs.update(kwargs) return UserJsonDatabaseAdapter(**_kwargs) def __init__(self, **kwargs): super(JsonDatabaseAdapter, self).__init__(**kwargs) if "database_path" not in self.kwargs: raise ValueError(_("You need to specify database path")) database_path = self.kwargs.get("database_path") from jsondb import Database self.database = Database(database_path) def _keys(self, filter_func=None): keys = self.database[0].keys() return list(filter(filter_func, keys) if callable(filter_func) else keys) def count(self, filter_func=None): return len(self._keys(filter_func)) def get(self, statement_text, default=None): values = self.database.data(key=statement_text) if not values: return default return values def remove(self, statement_text): self.database.delete(statement_text) def _all_kwargs_match_values(self, kwarguments, values): for kwarg in kwarguments: if "__" in kwarg: kwarg_parts = kwarg.split("__") key = kwarg_parts[0] identifier = kwarg_parts[1] if identifier == "contains": text_values = [] for val in values[key]: text_values.append(val["text"]) if (kwarguments[kwarg] not in text_values) and ( kwarguments[kwarg] not in values[key]): return False if kwarg in values: if values[kwarg] != kwarguments[kwarg]: return False return True def filter(self, filter_func=None, **kwargs): """ Returns a list of statements in the database that match the parameters specified. """ results = [] for key in self._keys(filter_func): values = self.database.data(key=key) if self._all_kwargs_match_values(kwargs, values): results.append(values) if not results: return None return results def update(self, key, value): # Do not alter the database unless writing is enabled if not self.read_only: self.database.data(key=key, value=value) return True return False def get_random(self, key): from random import choice if self.count() < 1: raise self.EmptyDatabaseException() statement = choice(self._keys()) return self.get(statement) def drop(self): """ Remove the json file database completely. """ import os if os.path.exists(self.database.path): os.remove(self.database.path)