Compare commits

...

10 commits

Author SHA1 Message Date
randogoth
9d4cd8c574 Add migration notice 2026-09-10 09:56:50 +02:00
randogoth
cb8c9ee2c3 screenshot 2023-05-24 17:31:22 +03:00
randogoth
de29ddb054 fixed missing folder, more instance options 2023-05-24 16:37:56 +03:00
randogoth
95ec09a41b align 2023-05-24 00:45:49 +03:00
randogoth
2ca696ce5c table 2023-05-24 00:44:05 +03:00
randogoth
0a30648495 identity per bot 2023-05-24 00:41:10 +03:00
randogoth
4280ba5624 added receipts 2023-05-19 15:25:56 +03:00
randogoth
dde477fcda typo 2023-05-19 11:18:30 +03:00
randogoth
77cbac8deb example 2023-05-19 11:13:55 +03:00
randogoth
fb9f122fe5 header 2023-05-19 11:13:33 +03:00
4 changed files with 62 additions and 25 deletions

View file

@ -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 `<instance>.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()
```
```
### 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
bot = LXMFBot("NodeBot")
bot = LXMFBot("testbot")
@bot.received
def echo_msg(msg):

View file

@ -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:

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB