Merge branch 'master' of github.com:anoadragon453/nio-template
* 'master' of github.com:anoadragon453/nio-template: INSERT OR REPLACE SQL on static value Commit to the database after we write to it Add projects list Add config.yaml to .gitignore Send m.notice by default Add message_responses.py, pass config parameters to callbacks
This commit is contained in:
commit
6f47000988
8 changed files with 97 additions and 19 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -9,6 +9,9 @@ env3/
|
|||
# Bot local files
|
||||
*.db
|
||||
|
||||
# Config file
|
||||
config.yaml
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
|
||||
|
|
19
README.md
19
README.md
|
@ -5,6 +5,13 @@ A template for creating bots with
|
|||
matrix-nio can be found
|
||||
[here](https://matrix-nio.readthedocs.io/en/latest/nio.html).
|
||||
|
||||
## Projects using nio-template
|
||||
|
||||
* [anoadragon453/msc-chatbot](https://github.com/anoadragon453/msc-chatbot) - A matrix bot for matrix spec proposals
|
||||
|
||||
Want your project listed here? [Edit this
|
||||
doc!](https://github.com/anoadragon453/nio-template/edit/master/README.md)
|
||||
|
||||
## Project structure
|
||||
|
||||
### `main.py`
|
||||
|
@ -84,6 +91,18 @@ prefix (defined by the bot's config file), or through a private message
|
|||
directly to the bot. The `process` command is then called for the bot to act on
|
||||
that command.
|
||||
|
||||
### `message_responses.py`
|
||||
|
||||
Where responses to messages that are posted in a room (but not necessarily
|
||||
directed at the bot) are specified. `callbacks.py` will listen for messages in
|
||||
rooms the bot is in, and upon receiving one will create a new `Message` object
|
||||
(which contains the message text, amongst other things) and calls `process()`
|
||||
on it, which can send a message to the room as it sees fit.
|
||||
|
||||
A good example of this would be a Github bot that listens for people mentioning
|
||||
issue numbers in chat (e.g. "We should fix #123"), and the bot sending messages
|
||||
to the room immediately afterwards with the issue name and link.
|
||||
|
||||
### `chat_functions.py`
|
||||
|
||||
A separate file to hold helper methods related to messaging. Mostly just for
|
||||
|
|
|
@ -2,7 +2,7 @@ from chat_functions import send_text_to_room
|
|||
|
||||
|
||||
class Command(object):
|
||||
def __init__(self, client, store, command, room, event):
|
||||
def __init__(self, client, store, config, command, room, event):
|
||||
"""A command made by a user
|
||||
|
||||
Args:
|
||||
|
@ -10,6 +10,8 @@ class Command(object):
|
|||
|
||||
store (Storage): Bot storage
|
||||
|
||||
config (Config): Bot configuration parameters
|
||||
|
||||
command (str): The command and arguments
|
||||
|
||||
room (nio.rooms.MatrixRoom): The room the command was sent in
|
||||
|
|
17
callbacks.py
17
callbacks.py
|
@ -5,6 +5,7 @@ from bot_commands import Command
|
|||
from nio import (
|
||||
JoinError,
|
||||
)
|
||||
from message_responses import Message
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -12,18 +13,19 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class Callbacks(object):
|
||||
|
||||
def __init__(self, client, store, command_prefix):
|
||||
def __init__(self, client, store, config):
|
||||
"""
|
||||
Args:
|
||||
client (nio.AsyncClient): nio client used to interact with matrix
|
||||
|
||||
store (Storage): Bot storage
|
||||
|
||||
command_prefix (str): The prefix for bot commands
|
||||
config (Config): Bot configuration parameters
|
||||
"""
|
||||
self.client = client
|
||||
self.store = store
|
||||
self.command_prefix = command_prefix
|
||||
self.config = config
|
||||
self.command_prefix = config.command_prefix
|
||||
|
||||
async def message(self, room, event):
|
||||
"""Callback for when a message event is received
|
||||
|
@ -46,16 +48,21 @@ class Callbacks(object):
|
|||
f"{room.user_name(event.sender)}: {msg}"
|
||||
)
|
||||
|
||||
# Ignore message if in a public room without command prefix
|
||||
# Process as message if in a public room without command prefix
|
||||
has_command_prefix = msg.startswith(self.command_prefix)
|
||||
if not has_command_prefix and not room.is_group:
|
||||
# General message listener
|
||||
message = Message(self.client, self.store, self.config, msg, room, event)
|
||||
await message.process()
|
||||
return
|
||||
|
||||
# Otherwise if this is in a 1-1 with the bot or features a command prefix,
|
||||
# treat it as a command
|
||||
if has_command_prefix:
|
||||
# Remove the command prefix
|
||||
msg = msg[len(self.command_prefix):]
|
||||
|
||||
command = Command(self.client, self.store, msg, room, event)
|
||||
command = Command(self.client, self.store, self.config, msg, room, event)
|
||||
await command.process()
|
||||
|
||||
async def invite(self, room, event):
|
||||
|
|
|
@ -7,7 +7,13 @@ from markdown import markdown
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def send_text_to_room(client, room_id, message, markdown_convert=True):
|
||||
async def send_text_to_room(
|
||||
client,
|
||||
room_id,
|
||||
message,
|
||||
notice=True,
|
||||
markdown_convert=True
|
||||
):
|
||||
"""Send text to a matrix room
|
||||
|
||||
Args:
|
||||
|
@ -17,23 +23,30 @@ async def send_text_to_room(client, room_id, message, markdown_convert=True):
|
|||
|
||||
message (str): The message content
|
||||
|
||||
notice (bool): Whether the message should be sent with an "m.notice" message type
|
||||
(will not ping users)
|
||||
|
||||
markdown_convert (bool): Whether to convert the message content to markdown.
|
||||
Defaults to true.
|
||||
"""
|
||||
formatted = message
|
||||
# Determine whether to ping room members or not
|
||||
msgtype = "m.notice" if notice else "m.text"
|
||||
|
||||
content = {
|
||||
"msgtype": msgtype,
|
||||
"format": "org.matrix.custom.html",
|
||||
"body": message,
|
||||
}
|
||||
|
||||
if markdown_convert:
|
||||
formatted = markdown(message)
|
||||
content["formatted_body"] = markdown(message)
|
||||
|
||||
try:
|
||||
await client.room_send(
|
||||
room_id,
|
||||
"m.room.message",
|
||||
{
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"body": message,
|
||||
"formatted_body": formatted,
|
||||
}
|
||||
content,
|
||||
)
|
||||
except SendRetryError:
|
||||
logger.exception(f"Unable to send message response to {room_id}")
|
||||
|
||||
|
|
2
main.py
2
main.py
|
@ -42,7 +42,7 @@ async def main():
|
|||
client.access_token = config.access_token
|
||||
|
||||
# Set up event callbacks
|
||||
callbacks = Callbacks(client, store, config.command_prefix)
|
||||
callbacks = Callbacks(client, store, config)
|
||||
client.add_event_callback(callbacks.message, (RoomMessageText,))
|
||||
client.add_event_callback(callbacks.invite, (InviteEvent,))
|
||||
|
||||
|
|
33
message_responses.py
Normal file
33
message_responses.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Message(object):
|
||||
|
||||
def __init__(self, client, store, config, message_content, room, event):
|
||||
"""Initialize a new Message
|
||||
|
||||
Args:
|
||||
client (nio.AsyncClient): nio client used to interact with matrix
|
||||
|
||||
store (Storage): Bot storage
|
||||
|
||||
config (Config): Bot configuration parameters
|
||||
|
||||
message_content (str): The body of the message
|
||||
|
||||
room (nio.rooms.MatrixRoom): The room the event came from
|
||||
|
||||
event (nio.events.room_events.RoomMessageText): The event defining the message
|
||||
"""
|
||||
self.client = client
|
||||
self.store = store
|
||||
self.config = config
|
||||
self.message_content = message_content
|
||||
self.room = room
|
||||
self.event = event
|
||||
|
||||
async def process(self):
|
||||
"""Process and possibly respond to the message"""
|
||||
pass
|
|
@ -30,12 +30,13 @@ class Storage(object):
|
|||
logger.info("Performing initial database setup...")
|
||||
|
||||
# Initialize a connection to the database
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
self.cursor = conn.cursor()
|
||||
self.conn = sqlite3.connect(self.db_path)
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
# Sync token table
|
||||
self.cursor.execute("CREATE TABLE sync_token ("
|
||||
"token TEXT PRIMARY KEY"
|
||||
"dedupe_id INTEGER PRIMARY KEY, "
|
||||
"token TEXT NOT NULL"
|
||||
")")
|
||||
|
||||
logger.info("Database setup complete")
|
||||
|
|
Loading…
Reference in a new issue