diff --git a/README.md b/README.md index cd9a06a..85ffb0d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ +> **Migrated to [code.randogoth.com/randogoth/lxmf-bot](https://code.randogoth.com/randogoth/lxmf-bot)** + # LXMFBot Python class to easily develop a simple Telethon style chatbot for the LXMF protocol. +![screenshot](screenshot.png) + ## Installation ``` @@ -10,12 +14,13 @@ $ pip install -r requirements.txt ## Usage -- Instantiate the class with a name for the bot as parameter +- Instantiate the class with a `name` for the bot as parameter. + - Two additional options the class takes as parameters are `announce` to define the announce interval in seconds (defaults to `360`) and an `announce_immediately` boolean to define whether the bot should announce itself immediately after instantiation or not (defaults to `False`) - Use the `received` decorator to define functions for parsing received messages - Use the `.send(recipient_hash, message)` or `msg.reply(message)` methods to send messages - Launch the bot using the `run` method -## Message Object +### Message Object Functions decorated by `received` have access to a `msg` parameter that has the following content: @@ -24,10 +29,12 @@ Functions decorated by `received` have access to a `msg` parameter that has the - `msg.reply` : function that takes a string parameter and sends it as reply to the sender - `msg.lxmf` : the complete `LXMessage` object for more complex parsing +### Example + Example of a bot that echos a message back to the sender: ```Python -from bot import LXMFBot +from lxmfbot import LXMFBot bot = LXMFBot("NodeBot") @@ -36,4 +43,14 @@ def echo_msg(msg): msg.reply(msg.content) bot.run() -``` \ No newline at end of file +``` + +### Identity & Announce Files + +The bots' identity and announce file location + +| OS | Path | +|:-- |:--- | +| Linux: | `~/.local/share/LXMFBot/` | +| MacOS: | `~/Library/Application Support/LXMFBot/` | +| Windows: | `C:\Users\\AppData\Local\LXMFBot\` | \ No newline at end of file diff --git a/example.py b/example.py index 3f98381..2adc4c9 100644 --- a/example.py +++ b/example.py @@ -1,6 +1,6 @@ from lxmfbot import LXMFBot -bot = LXMFBot("NodeBot") +bot = LXMFBot("testbot") @bot.received def echo_msg(msg): diff --git a/lxmfbot.py b/lxmfbot.py index 6b8ace0..7e72c1e 100644 --- a/lxmfbot.py +++ b/lxmfbot.py @@ -11,21 +11,33 @@ from types import SimpleNamespace class LXMFBot: delivery_callbacks = [] + receipts = [] queue = Queue(maxsize = 5) + announce_time = 360 - def __init__(self, name='LXMFBot'): + def __init__(self, name='LXMFBot', announce=360, announce_immediately=False): # initialize identiy self.name = name + self.announce_time = announce dirs = AppDirs("LXMFBot", "randogoth") - idpath = os.path.join(dirs.user_data_dir, "identity") - self.announce_path = os.path.join(dirs.user_data_dir, "announce") - if not os.path.isfile(idpath): + self.config_path = os.path.join(dirs.user_data_dir, name) + idfile = os.path.join(self.config_path, "identity") + if not os.path.isdir(dirs.user_data_dir): + os.mkdir(dirs.user_data_dir) + if not os.path.isdir(self.config_path): + os.mkdir(self.config_path) + if not os.path.isfile(idfile): RNS.log('No Primary Identity file found, creating new...', RNS.LOG_INFO) - id = RNS.Identity() - id.to_file(idpath) - self.id = RNS.Identity.from_file(idpath) + id = RNS.Identity(True) + id.to_file(idfile) + self.id = RNS.Identity.from_file(idfile) RNS.log('Loaded identity from file', RNS.LOG_INFO) + if announce_immediately: + af = os.path.join(self.config_path, "announce") + if os.path.isfile(af): + os.remove(af) + RNS.log('Announcing now. Timer reset.', RNS.LOG_INFO) # start RNS and LXMFRouter RNS.Reticulum(loglevel=RNS.LOG_VERBOSE) @@ -36,8 +48,9 @@ class LXMFBot: self._announce() def _announce(self): - if os.path.isfile(self.announce_path): - with open(self.announce_path, "r") as f: + announce_path = os.path.join(self.config_path, "announce") + if os.path.isfile(announce_path): + with open(announce_path, "r") as f: announce = int(f.readline()) else: RNS.log('failed to open announcepath', RNS.LOG_DEBUG) @@ -45,8 +58,8 @@ class LXMFBot: if announce > int(time.time()): RNS.log('Recent announcement', RNS.LOG_DEBUG) else: - with open(self.announce_path, "w") as af: - next_announce = int(time.time()) + 1800 + with open(announce_path, "w+") as af: + next_announce = int(time.time()) + self.announce_time af.write(str(next_announce)) self.local.announce() RNS.log('Announcement sent, expr set 1800 seconds', RNS.LOG_INFO) @@ -57,17 +70,24 @@ class LXMFBot: def _message_received(self, message): sender = RNS.hexrep(message.source_hash, delimit=False) + receipt = RNS.hexrep(message.hash, delimit=False) + RNS.log(f'Message receipt <{receipt}>', RNS.LOG_INFO) def reply(msg): self.send(sender, msg) - for callback in self.delivery_callbacks: - obj = { - 'lxmf' : message, - 'reply' : reply, - 'sender' : sender, - 'content' : message.content.decode('utf-8') - } - msg = SimpleNamespace(**obj) - callback(msg) + if receipt not in self.receipts: + self.receipts.append(receipt) + if len(self.receipts) > 100: + self.receipts.pop(0) + for callback in self.delivery_callbacks: + obj = { + 'lxmf' : message, + 'reply' : reply, + 'sender' : sender, + 'content' : message.content.decode('utf-8'), + 'hash' : receipt + } + msg = SimpleNamespace(**obj) + callback(msg) def send(self, destination, message, title='Reply'): try: diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..1c640c7 Binary files /dev/null and b/screenshot.png differ