Skip to content
This repository has been archived by the owner on May 22, 2024. It is now read-only.

feat: ✨ add ability to reply with text messages #89

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 50 additions & 17 deletions doc/manual/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,68 @@ async def example(room, message):
example_message = "Hello World"
if match.is_not_from_this_bot():
await bot.api.send_text_message(
room_id=room.room_id,
room_id=room.room_id,
message=example_message)
```
The first two arguments are required. The room_id argument is the id of the destination room. The message argument is the string that is to be sent as a message.

#### Sending a bot notice
To send a bot notice (non-alerting message), set the argument `msgtype="m.notice"`. It can be combined with any of the other optional arguments.
```python
async def example(room, message):
match = botlib.MessageMatch(room, message, bot)
example_message = "Hello World"
if match.is_not_from_this_bot():
await bot.api.send_text_message(
room_id=room.room_id,
message=example_message,
msgtype="m.notice")
```
The first two arguments are required. The room_id argument is the id of the destination room. The message argument is the string that is to be sent as a message. The msgtype argument can be "m.text" (default) or "m.notice".

### Using the send_image_message method
The send_image_message method of the Api class can be used to send image messages in Matrix rooms. An example is shown in the following python code.
#### Sending a reply
To send your message as a reply to another message, pass the original message event to the `reply_to` argument. It can be combined with any of the other optional arguments.
```python
async def example(room, message):
match = botlib.MessageMatch(room, message, bot)
example_image="./img/example.png"
example_message = "Hello World"
if match.is_not_from_this_bot():
await bot.api.send_image_message(
room_id=room.room_id,
image_filepath=example_image)
await bot.api.send_text_message(
room_id=room.room_id,
message=example_message,
reply_to=message)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think calling the received event "message" might be confusing here, since the parameter is also called message and the text is example_message. Perhaps rename it to event?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renaming it to either event_content or message_content would probably be better. I think that event would be confusing.

Copy link
Contributor Author

@HarHarLinks HarHarLinks Nov 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not content though, it's the actual nio.events.room_events.RoomMessage object, isn't it?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. What about content_body?

```

#### Sending a markdown formatted message
To translate markdown in your `example_message` into a HTML formatted message, set the argument `markdown=True`. It can be combined with any of the other optional arguments.
```python
async def example(room, message):
match = botlib.MessageMatch(room, message, bot)
example_message = "Hello World"
if match.is_not_from_this_bot():
await bot.api.send_text_message(
room_id=room.room_id,
message=example_message,
markdown=True)
```

Alternatively, use the send_markdown_message alias method, which also supports the other optional arguments:
```python
await bot.api.send_markdown_message(
room_id=room.room_id,
message=example_markdown,
msgtype="m.notice",
reply_to=message)
```
Both arguments are required. The room_id argument is the id of the destination room. The image_filepath argument is a string that is the path to the image file that is to be sent as a message.

### Using the send_markdown_message method
The send_markdown_message method of the Api class can be used to send markdown messages in Matrix rooms. An example is shown in the following python code.
### Using the send_image_message method
The send_image_message method of the Api class can be used to send image messages in Matrix rooms. An example is shown in the following python code.
```python
async def example(room, message):
match = botlib.MessageMatch(room, message, bot)
example_markdown = "# Hello World from [simplematrixbotlib](https://github.com/KrazyKirby99999/simple-matrix-bot-lib)!"
example_image="./img/example.png"
if match.is_not_from_this_bot():
await bot.api.send_markdown_message(
room_id=room.room_id,
message=example_markdown,
msgtype="m.notice")
await bot.api.send_image_message(
room_id=room.room_id,
image_filepath=example_image)
```
The first two arguments are required. The room_id argument is the id of the destination room. The message argument is the string with markdown syntax that is to be sent as a message. The msgtype argument can be "m.text" (default) or "m.notice".
Both arguments are required. The room_id argument is the id of the destination room. The image_filepath argument is a string that is the path to the image file that is to be sent as a message.
97 changes: 55 additions & 42 deletions simplematrixbotlib/api.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import asyncio
from nio import (AsyncClient, SyncResponse, RoomMessageText)
from nio import (AsyncClient, SyncResponse, RoomMessageText, RoomMessageFormatted)
from PIL import Image
import aiofiles.os
import mimetypes
import os
import markdown
import markdown as md
import aiohttp
import ast
from nio.responses import UploadResponse
Expand Down Expand Up @@ -70,11 +70,11 @@ async def login(self):
raise Exception(resp)


async def send_text_message(self, room_id, message, msgtype='m.text'):
async def send_text_message(self, room_id, message, msgtype='m.text', reply_to=None, markdown=False):
"""
Send a text message in a Matrix room.

Parameteres
Parameters
-----------
room_id : str
The room id of the destination of the message.
Expand All @@ -85,19 +85,63 @@ async def send_text_message(self, room_id, message, msgtype='m.text'):
msgtype : str, optional
The type of message to send: m.text (default), m.notice, etc

reply_to : nio.events.room_events.RoomMessage, optional
The event to reply to

markdown : Boolean, optional
Whether to treat message as markdown and render as HTML

"""
content = {
"msgtype": msgtype,
"body": message
}

if markdown or reply_to is not None:
content["format"] = "org.matrix.custom.html"
content["formatted_body"] = md.markdown(message, extensions=['nl2br'])

if reply_to is not None:
content["m.relates.to"] = {"m.in_reply_to": {"event_id": reply_to.event_id}}
if isinstance(reply_to, RoomMessageFormatted): # quote fallback if it's a text event https://spec.matrix.org/unstable/client-server-api/#fallbacks-for-rich-replies
content["body"] = f'> <{reply_to.sender}> {reply_to.body}\n\n{content["body"]}'
formatted_body = f'<mx-reply><blockquote><a href="https://matrix.to/#/{room_id}/{reply_to.event_id}">In reply to</a> '
formatted_body += f'<a href="https://matrix.to/#/{reply_to.sender}">{reply_to.sender}</a><br>'
formatted_body += f'{reply_to.formatted_body or reply_to.body}</blockquote></mx-reply>{content["formatted_body"]}'
content["formatted_body"] = formatted_body

await self.async_client.room_send(room_id=room_id,
message_type="m.room.message",
content={
"msgtype": msgtype,
"body": message
})
content=content,
ignore_unverified_devices=True)

async def send_markdown_message(self, room_id, message, msgtype='m.text', reply_to=None):
"""
Send a markdown message in a Matrix room.
Wrapper for send_text_message

Parameters
-----------
room_id : str
The room id of the destination of the message.

message : str
The content of the message to be sent.

msgtype : str, optional
The type of message to send: m.text (default), m.notice, etc

reply_to : nio.events.room_events.RoomMessage, optional
The event to reply to

"""
await self.send_text_message(room_id=room_id, message=message, msgtype=msgtype, reply_to=reply_to, markdown=True)

async def send_image_message(self, room_id, image_filepath):
"""
Send an image message in a Matrix room.

Parameteres
Parameters
-----------
room_id : str
The room id of the destination of the message.
Expand Down Expand Up @@ -140,38 +184,7 @@ async def send_image_message(self, room_id, image_filepath):
try:
await self.async_client.room_send(room_id,
message_type="m.room.message",
content=content)
content=content,
ignore_unverified_devices=True)
except:
print(f"Failed to send image file {image_filepath}")

async def send_markdown_message(self, room_id, message, msgtype='m.text'):
"""
Send a markdown message in a Matrix room.

Parameteres
-----------
room_id : str
The room id of the destination of the message.

message : str
The content of the message to be sent.

msgtype : str, optional
The type of message to send: m.text (default), m.notice, etc

"""

await self.async_client.room_send(room_id=room_id,
message_type="m.room.message",
content={
"msgtype":
msgtype,
"body":
message,
"format":
"org.matrix.custom.html",
"formatted_body":
markdown.markdown(message, extensions=['nl2br'])
})