Compare commits
10 commits
64152fe7d8
...
9d4cd8c574
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d4cd8c574 | ||
|
|
cb8c9ee2c3 | ||
|
|
de29ddb054 | ||
|
|
95ec09a41b | ||
|
|
2ca696ce5c | ||
|
|
0a30648495 | ||
|
|
4280ba5624 | ||
|
|
dde477fcda | ||
|
|
77cbac8deb | ||
|
|
fb9f122fe5 |
4 changed files with 62 additions and 25 deletions
25
README.md
25
README.md
|
|
@ -1,7 +1,11 @@
|
||||||
|
> **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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -10,12 +14,13 @@ $ 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:
|
||||||
|
|
||||||
|
|
@ -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.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 bot import LXMFBot
|
from lxmfbot import LXMFBot
|
||||||
|
|
||||||
bot = LXMFBot("NodeBot")
|
bot = LXMFBot("NodeBot")
|
||||||
|
|
||||||
|
|
@ -36,4 +43,14 @@ def echo_msg(msg):
|
||||||
msg.reply(msg.content)
|
msg.reply(msg.content)
|
||||||
|
|
||||||
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>` |
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from lxmfbot import LXMFBot
|
from lxmfbot import LXMFBot
|
||||||
|
|
||||||
bot = LXMFBot("NodeBot")
|
bot = LXMFBot("testbot")
|
||||||
|
|
||||||
@bot.received
|
@bot.received
|
||||||
def echo_msg(msg):
|
def echo_msg(msg):
|
||||||
|
|
|
||||||
60
lxmfbot.py
60
lxmfbot.py
|
|
@ -11,21 +11,33 @@ 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'):
|
def __init__(self, name='LXMFBot', announce=360, announce_immediately=False):
|
||||||
|
|
||||||
# initialize identiy
|
# initialize identiy
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.announce_time = announce
|
||||||
dirs = AppDirs("LXMFBot", "randogoth")
|
dirs = AppDirs("LXMFBot", "randogoth")
|
||||||
idpath = os.path.join(dirs.user_data_dir, "identity")
|
self.config_path = os.path.join(dirs.user_data_dir, name)
|
||||||
self.announce_path = os.path.join(dirs.user_data_dir, "announce")
|
idfile = os.path.join(self.config_path, "identity")
|
||||||
if not os.path.isfile(idpath):
|
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)
|
RNS.log('No Primary Identity file found, creating new...', RNS.LOG_INFO)
|
||||||
id = RNS.Identity()
|
id = RNS.Identity(True)
|
||||||
id.to_file(idpath)
|
id.to_file(idfile)
|
||||||
self.id = RNS.Identity.from_file(idpath)
|
self.id = RNS.Identity.from_file(idfile)
|
||||||
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)
|
||||||
|
|
@ -36,8 +48,9 @@ class LXMFBot:
|
||||||
self._announce()
|
self._announce()
|
||||||
|
|
||||||
def _announce(self):
|
def _announce(self):
|
||||||
if os.path.isfile(self.announce_path):
|
announce_path = os.path.join(self.config_path, "announce")
|
||||||
with open(self.announce_path, "r") as f:
|
if os.path.isfile(announce_path):
|
||||||
|
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)
|
||||||
|
|
@ -45,8 +58,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(self.announce_path, "w") as af:
|
with open(announce_path, "w+") as af:
|
||||||
next_announce = int(time.time()) + 1800
|
next_announce = int(time.time()) + self.announce_time
|
||||||
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)
|
||||||
|
|
@ -57,17 +70,24 @@ 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)
|
||||||
for callback in self.delivery_callbacks:
|
if receipt not in self.receipts:
|
||||||
obj = {
|
self.receipts.append(receipt)
|
||||||
'lxmf' : message,
|
if len(self.receipts) > 100:
|
||||||
'reply' : reply,
|
self.receipts.pop(0)
|
||||||
'sender' : sender,
|
for callback in self.delivery_callbacks:
|
||||||
'content' : message.content.decode('utf-8')
|
obj = {
|
||||||
}
|
'lxmf' : message,
|
||||||
msg = SimpleNamespace(**obj)
|
'reply' : reply,
|
||||||
callback(msg)
|
'sender' : sender,
|
||||||
|
'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:
|
||||||
|
|
|
||||||
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
Loading…
Add table
Add a link
Reference in a new issue