Compare commits

..

No commits in common. "9d4cd8c5744f9ff7ebdb64cc3b4639e096f39672" and "64152fe7d8c57ac140b31e61d599a22ac0eadd8d" have entirely different histories.

4 changed files with 25 additions and 62 deletions

View file

@ -1,11 +1,7 @@
> **Migrated to [code.randogoth.com/randogoth/lxmf-bot](https://code.randogoth.com/randogoth/lxmf-bot)**
# LXMFBot # LXMFBot
Python class to easily develop a simple Telethon style chatbot for the LXMF protocol. Python class to easily develop a simple Telethon style chatbot for the LXMF protocol.
![screenshot](screenshot.png)
## Installation ## Installation
``` ```
@ -14,13 +10,12 @@ $ pip install -r requirements.txt
## Usage ## 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 `received` decorator to define functions for parsing received messages
- Use the `<instance>.send(recipient_hash, message)` or `msg.reply(message)` methods to send messages - Use the `<instance>.send(recipient_hash, message)` or `msg.reply(message)` methods to send messages
- Launch the bot using the `run` method - 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: Functions decorated by `received` have access to a `msg` parameter that has the following content:
@ -29,12 +24,10 @@ 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.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 - `msg.lxmf` : the complete `LXMessage` object for more complex parsing
### Example
Example of a bot that echos a message back to the sender: Example of a bot that echos a message back to the sender:
```Python ```Python
from lxmfbot import LXMFBot from bot import LXMFBot
bot = LXMFBot("NodeBot") bot = LXMFBot("NodeBot")
@ -44,13 +37,3 @@ def echo_msg(msg):
bot.run() bot.run()
``` ```
### Identity & Announce Files
The bots' identity and announce file location
| OS | Path |
|:-- |:--- |
| Linux: | `~/.local/share/LXMFBot/<botname>` |
| MacOS: | `~/Library/Application Support/LXMFBot/<botname>` |
| Windows: | `C:\Users\<username>\AppData\Local\LXMFBot\<botname>` |

View file

@ -1,6 +1,6 @@
from lxmfbot import LXMFBot from lxmfbot import LXMFBot
bot = LXMFBot("testbot") bot = LXMFBot("NodeBot")
@bot.received @bot.received
def echo_msg(msg): def echo_msg(msg):

View file

@ -11,33 +11,21 @@ from types import SimpleNamespace
class LXMFBot: class LXMFBot:
delivery_callbacks = [] delivery_callbacks = []
receipts = []
queue = Queue(maxsize = 5) queue = Queue(maxsize = 5)
announce_time = 360
def __init__(self, name='LXMFBot', announce=360, announce_immediately=False): def __init__(self, name='LXMFBot'):
# initialize identiy # initialize identiy
self.name = name self.name = name
self.announce_time = announce
dirs = AppDirs("LXMFBot", "randogoth") dirs = AppDirs("LXMFBot", "randogoth")
self.config_path = os.path.join(dirs.user_data_dir, name) idpath = os.path.join(dirs.user_data_dir, "identity")
idfile = os.path.join(self.config_path, "identity") self.announce_path = os.path.join(dirs.user_data_dir, "announce")
if not os.path.isdir(dirs.user_data_dir): if not os.path.isfile(idpath):
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) RNS.log('No Primary Identity file found, creating new...', RNS.LOG_INFO)
id = RNS.Identity(True) id = RNS.Identity()
id.to_file(idfile) id.to_file(idpath)
self.id = RNS.Identity.from_file(idfile) self.id = RNS.Identity.from_file(idpath)
RNS.log('Loaded identity from file', RNS.LOG_INFO) 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 # start RNS and LXMFRouter
RNS.Reticulum(loglevel=RNS.LOG_VERBOSE) RNS.Reticulum(loglevel=RNS.LOG_VERBOSE)
@ -48,9 +36,8 @@ class LXMFBot:
self._announce() self._announce()
def _announce(self): def _announce(self):
announce_path = os.path.join(self.config_path, "announce") if os.path.isfile(self.announce_path):
if os.path.isfile(announce_path): with open(self.announce_path, "r") as f:
with open(announce_path, "r") as f:
announce = int(f.readline()) announce = int(f.readline())
else: else:
RNS.log('failed to open announcepath', RNS.LOG_DEBUG) RNS.log('failed to open announcepath', RNS.LOG_DEBUG)
@ -58,8 +45,8 @@ class LXMFBot:
if announce > int(time.time()): if announce > int(time.time()):
RNS.log('Recent announcement', RNS.LOG_DEBUG) RNS.log('Recent announcement', RNS.LOG_DEBUG)
else: else:
with open(announce_path, "w+") as af: with open(self.announce_path, "w") as af:
next_announce = int(time.time()) + self.announce_time next_announce = int(time.time()) + 1800
af.write(str(next_announce)) af.write(str(next_announce))
self.local.announce() self.local.announce()
RNS.log('Announcement sent, expr set 1800 seconds', RNS.LOG_INFO) RNS.log('Announcement sent, expr set 1800 seconds', RNS.LOG_INFO)
@ -70,24 +57,17 @@ class LXMFBot:
def _message_received(self, message): def _message_received(self, message):
sender = RNS.hexrep(message.source_hash, delimit=False) 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): def reply(msg):
self.send(sender, msg) self.send(sender, msg)
if receipt not in self.receipts: for callback in self.delivery_callbacks:
self.receipts.append(receipt) obj = {
if len(self.receipts) > 100: 'lxmf' : message,
self.receipts.pop(0) 'reply' : reply,
for callback in self.delivery_callbacks: 'sender' : sender,
obj = { 'content' : message.content.decode('utf-8')
'lxmf' : message, }
'reply' : reply, msg = SimpleNamespace(**obj)
'sender' : sender, callback(msg)
'content' : message.content.decode('utf-8'),
'hash' : receipt
}
msg = SimpleNamespace(**obj)
callback(msg)
def send(self, destination, message, title='Reply'): def send(self, destination, message, title='Reply'):
try: try:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB