Implement write auto-persist file storage
Closes https://git.autonomic.zone/decentral1se/xbotlib/issues/39.
This commit is contained in:
parent
b1f4d6112f
commit
2e8c9e739e
|
@ -1,5 +1,9 @@
|
||||||
# xbotlib x.x.x (UNRELEASED)
|
# xbotlib x.x.x (UNRELEASED)
|
||||||
|
|
||||||
|
# xbotlib 0.15.1 (2021-01-24)
|
||||||
|
|
||||||
|
- Save to file based storage on all writes ([#39](https://git.autonomic.zone/decentral1se/xbotlib/issues/39))
|
||||||
|
|
||||||
# xbotlib 0.15.0 (2021-01-23)
|
# xbotlib 0.15.0 (2021-01-23)
|
||||||
|
|
||||||
- Fix configuration generation to cover mandatory options ([#1](https://git.vvvvvvaria.org/decentral1se/xbotlib/issues/1))
|
- Fix configuration generation to cover mandatory options ([#1](https://git.vvvvvvaria.org/decentral1se/xbotlib/issues/1))
|
||||||
|
|
|
@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "xbotlib"
|
name = "xbotlib"
|
||||||
version = "0.15.0"
|
version = "0.15.1"
|
||||||
description = "XMPP bots for humans"
|
description = "XMPP bots for humans"
|
||||||
authors = ["decentral1se <lukewm@riseup.net>"]
|
authors = ["decentral1se <lukewm@riseup.net>"]
|
||||||
maintainers = ["decentral1se <lukewm@riseup.net>"]
|
maintainers = ["decentral1se <lukewm@riseup.net>"]
|
||||||
|
|
91
xbotlib.py
91
xbotlib.py
|
@ -18,6 +18,59 @@ from humanize import naturaldelta
|
||||||
from slixmpp import ClientXMPP
|
from slixmpp import ClientXMPP
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleDatabase(dict):
|
||||||
|
"""A simple database.
|
||||||
|
|
||||||
|
It is a dictionary which saves to disk on all writes. It is optimised for
|
||||||
|
ease of hacking and accessibility and not for performance or efficiency.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, filename, *args, **kwargs):
|
||||||
|
"""Initialise the object."""
|
||||||
|
self.filename = Path(filename).absolute()
|
||||||
|
self._loads()
|
||||||
|
self.update(*args, **kwargs)
|
||||||
|
|
||||||
|
def _loads(self):
|
||||||
|
"""Load the database."""
|
||||||
|
if not exists(self.filename):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(self.filename, "r") as handle:
|
||||||
|
self.update(loads(handle.read()))
|
||||||
|
except Exception as exception:
|
||||||
|
message = f"Loading file storage failed: {exception}"
|
||||||
|
self.log.debug(message)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def _dumps(self):
|
||||||
|
"""Save the databse to disk."""
|
||||||
|
try:
|
||||||
|
with open(self.filename, "w") as handle:
|
||||||
|
handle.write(dumps(self))
|
||||||
|
except Exception as exception:
|
||||||
|
message = f"Saving file storage failed: {exception}"
|
||||||
|
self.log.debug(message)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
def __setitem__(self, key, val):
|
||||||
|
"""Write data to the database."""
|
||||||
|
dict.__setitem__(self, key, val)
|
||||||
|
self._dumps()
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
"""Remove data from the database."""
|
||||||
|
dict.__delitem__(key)
|
||||||
|
self._dumps()
|
||||||
|
|
||||||
|
def update(self, *args, **kwargs):
|
||||||
|
"""Update the database."""
|
||||||
|
for k, v in dict(*args, **kwargs).items():
|
||||||
|
self[k] = v
|
||||||
|
self._dumps()
|
||||||
|
|
||||||
|
|
||||||
class SimpleMessage:
|
class SimpleMessage:
|
||||||
"""A simple message interface."""
|
"""A simple message interface."""
|
||||||
|
|
||||||
|
@ -478,10 +531,17 @@ class Bot(ClientXMPP):
|
||||||
if self.command(message, to=message.sender):
|
if self.command(message, to=message.sender):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not hasattr(self, "direct"):
|
||||||
|
self.log.info(f"Bot.direct not implemented for {self.nick}")
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.direct(message)
|
self.direct(message)
|
||||||
except AttributeError:
|
except Exception as exception:
|
||||||
self.log.info(f"Bot.direct not implemented for {self.nick}")
|
self.log.info(f"Bot.direct threw exception {exception}")
|
||||||
|
|
||||||
|
if self.storage == "file":
|
||||||
|
self.db._dumps()
|
||||||
|
|
||||||
def session_start(self, message):
|
def session_start(self, message):
|
||||||
"""Handle session_start event."""
|
"""Handle session_start event."""
|
||||||
|
@ -548,10 +608,17 @@ class Bot(ClientXMPP):
|
||||||
if self.command(message, room=message.room):
|
if self.command(message, room=message.room):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not hasattr(self, "group"):
|
||||||
|
self.log.info(f"Bot.group not implemented for {self.nick}")
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.group(message)
|
self.group(message)
|
||||||
except AttributeError:
|
except Exception as exception:
|
||||||
self.log.info(f"Bot.group not implemented for {self.nick}")
|
self.log.info(f"Bot.group threw exception: {exception}")
|
||||||
|
|
||||||
|
if self.storage == "file":
|
||||||
|
self.db._dumps()
|
||||||
|
|
||||||
def register_xmpp_plugins(self):
|
def register_xmpp_plugins(self):
|
||||||
"""Register XMPP plugins that the bot supports."""
|
"""Register XMPP plugins that the bot supports."""
|
||||||
|
@ -572,9 +639,7 @@ class Bot(ClientXMPP):
|
||||||
"""Initialise the storage back-end."""
|
"""Initialise the storage back-end."""
|
||||||
if self.storage == "file":
|
if self.storage == "file":
|
||||||
try:
|
try:
|
||||||
self.db = {}
|
self.db = SimpleDatabase(self.storage_file)
|
||||||
if exists(self.storage_file):
|
|
||||||
self.db = loads(open(self.storage_file, "r").read())
|
|
||||||
self.log.info("Successfully loaded file storage")
|
self.log.info("Successfully loaded file storage")
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
message = f"Failed to load {self.storage_file}: {exception}"
|
message = f"Failed to load {self.storage_file}: {exception}"
|
||||||
|
@ -605,17 +670,7 @@ class Bot(ClientXMPP):
|
||||||
self.serve_web()
|
self.serve_web()
|
||||||
self.process(forever=False)
|
self.process(forever=False)
|
||||||
except (KeyboardInterrupt, RuntimeError):
|
except (KeyboardInterrupt, RuntimeError):
|
||||||
if self.storage != "file":
|
pass
|
||||||
exit(0)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(self.storage_file, "w") as handle:
|
|
||||||
handle.write(dumps(self.db))
|
|
||||||
self.log.info("Successfully saved file storage")
|
|
||||||
except Exception as exception:
|
|
||||||
message = f"Failed to save file storage: {exception}"
|
|
||||||
self.log.info(message)
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
def serve_web(self):
|
def serve_web(self):
|
||||||
"""Serve the web."""
|
"""Serve the web."""
|
||||||
|
|
Loading…
Reference in New Issue