Skip to content

Commit 93f0add

Browse files
authored
Merge pull request #105 from geotribu/feature/ping-matrix-channel-on-specific-message
feature(chat): add the ability to ping a matrix room
2 parents 4ba212e + 5ed7636 commit 93f0add

File tree

7 files changed

+94
-4
lines changed

7 files changed

+94
-4
lines changed

.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ MAX_STORED_MESSAGES=5
1111
INSTANCE_ID=abcdefghijklmnopqrstuvwxyz
1212
ENVIRONMENT=production
1313

14+
REDIS_HOST=redis
15+
REDIS_PORT=6379
16+
1417
SENTRY_DSN=
1518
SENTRY_TRACES_SAMPLE_RATE=1
1619
SENTRY_PROFILES_SAMPLE_RATE=1
20+
21+
MATRIX_CHAT_ENABLED=false
22+
23+
MATRIX_PING_MESSAGE_PREFIX="@matrixping"
24+
MATRIX_PING_HOMESERVER=
25+
MATRIX_PING_ROOMID=
26+
MATRIX_PING_TOKEN=

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ Each of them has a `"type"` key based on which it is possible to parse them :
219219
> `RULES` environment variable describes the instance's rules. Useful information that users should know, even when skimming content.
220220
> `MAX_IMAGE_SIZE` environment variable describes the max size of image in pixels. The server will resize images based on this value.
221221
> `MAX_GEOJSON_FEATURES` environment variable describes the max number of features allowed in a `geojson` message. If there is more feature, the message will not be considered and the server will respond with a `uncompliant` message.
222+
> `MATRIX_CHAT_ENABLED` environment variable describes if the instance should serve the matrix-related websocket endpoints.
223+
> `MATRIX_PING_*` environment variables describe the matrix settings to ping a room when a message starts with the `MATRIX_PING_MESSAGE_PREFIX`.
222224
223225
1. Install `docker` using [the official documentation](https://docs.docker.com/engine/install/)
224226
1. Create a `docker-compose.yaml` file on your server:
@@ -243,6 +245,11 @@ Each of them has a `"type"` key based on which it is possible to parse them :
243245
- MAX_GEOJSON_FEATURES=500
244246
- MAX_STORED_MESSAGES=5
245247
- REDIS_HOST=redis
248+
- MATRIX_CHAT_ENABLED=false
249+
- MATRIX_PING_MESSAGE_PREFIX="@matrixping"
250+
- MATRIX_PING_HOMESERVER=https://my.matrix.server.net
251+
- MATRIX_PING_ROOMID=!id:my.matrix.server.net
252+
- MATRIX_PING_TOKEN=mat_token
246253
ports:
247254
- 8000:8000
248255
restart: unless-stopped

gischat/app.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,15 @@
2121
from gischat.dispatchers import (
2222
MatrixDispatcher,
2323
RedisDispatcher,
24+
forward_message_to_matrix_channel,
2425
get_redis_channel_key,
2526
)
2627
from gischat.env import (
27-
MATRIX_ENABLED,
28+
MATRIX_CHAT_ENABLED,
29+
MATRIX_PING_HOMESERVER,
30+
MATRIX_PING_MESSAGE_PREFIX,
31+
MATRIX_PING_ROOMID,
32+
MATRIX_PING_TOKEN,
2833
REDIS_HOST,
2934
REDIS_PORT,
3035
REDIS_URL,
@@ -283,6 +288,18 @@ async def websocket_endpoint(websocket: WebSocket, channel: str) -> None:
283288
if message.text not in QCHAT_CHEATCODES:
284289
redis_dispatcher.store_message(channel, message)
285290

291+
if (
292+
message.text.startswith(MATRIX_PING_MESSAGE_PREFIX)
293+
and MATRIX_PING_MESSAGE_PREFIX
294+
and MATRIX_PING_HOMESERVER
295+
and MATRIX_PING_ROOMID
296+
and MATRIX_PING_TOKEN
297+
):
298+
logger.info(
299+
f"🏓 [{channel}]: Matrix ping received, forwarding to Matrix channel."
300+
)
301+
await forward_message_to_matrix_channel(message, channel)
302+
286303
# image message
287304
if message.type == QChatMessageTypeEnum.IMAGE:
288305
message = QChatImageMessage(**payload)
@@ -420,7 +437,7 @@ async def websocket_endpoint(websocket: WebSocket, channel: str) -> None:
420437
matrix_dispatcher = MatrixDispatcher.instance()
421438

422439

423-
if MATRIX_ENABLED:
440+
if MATRIX_CHAT_ENABLED:
424441

425442
@app.get("/matrix", response_class=HTMLResponse)
426443
async def get_matrix_ws_page(request: Request):
@@ -475,7 +492,6 @@ async def matrix_websocket_endpoint(websocket: WebSocket, request_id: UUID) -> N
475492

476493
# text message
477494
if message.type == QChatMessageTypeEnum.TEXT:
478-
print("payload: ", payload)
479495
message = QMatrixChatTextMessage(**payload)
480496

481497
logger.info(

gischat/dispatchers.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from functools import partial
33
from uuid import UUID, uuid4
44

5+
import markdown
56
from fastapi import WebSocket
67
from fastapi.encoders import jsonable_encoder
78
from nio import AsyncClient, MatrixRoom, RoomMessageText
@@ -11,6 +12,9 @@
1112
from gischat.env import (
1213
INSTANCE_CHANNELS,
1314
INSTANCE_ID,
15+
MATRIX_PING_HOMESERVER,
16+
MATRIX_PING_ROOMID,
17+
MATRIX_PING_TOKEN,
1418
MAX_STORED_MESSAGES,
1519
REDIS_HOST,
1620
REDIS_PORT,
@@ -22,6 +26,7 @@
2226
QChatMessageModel,
2327
QChatNbUsersMessage,
2428
QChatNewcomerMessage,
29+
QChatTextMessage,
2530
QMatrixChatTextMessage,
2631
parse_qchat_message,
2732
)
@@ -407,3 +412,34 @@ async def on_matrix_text_message_received(
407412
)
408413

409414
await websocket.send_json(jsonable_encoder(message))
415+
416+
417+
async def forward_message_to_matrix_channel(
418+
message: QChatTextMessage, channel: str
419+
) -> None:
420+
"""
421+
Forwards a QChat text message to a Matrix channel.
422+
:param message: QChat text message to forward.
423+
:param channel: QChat channel where the message originated.
424+
"""
425+
426+
client = AsyncClient(homeserver=MATRIX_PING_HOMESERVER)
427+
client.access_token = MATRIX_PING_TOKEN
428+
429+
try:
430+
body_md = f"""💬 QChat message in channel **{channel}** by _{message.author}_:
431+
{message.text}"""
432+
body_html = markdown.markdown(body_md)
433+
434+
await client.room_send(
435+
room_id=MATRIX_PING_ROOMID,
436+
message_type="m.room.message",
437+
content={
438+
"msgtype": "m.text",
439+
"body": body_md,
440+
"format": "org.matrix.custom.html",
441+
"formatted_body": body_html,
442+
},
443+
)
444+
finally:
445+
await client.close()

gischat/env.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,13 @@
1010
REDIS_PORT = int(os.getenv("REDIS_PORT", "6379"))
1111
REDIS_URL = f"redis://{REDIS_HOST}:{REDIS_PORT}"
1212

13-
MATRIX_ENABLED = os.getenv("MATRIX_ENABLED", "false").lower() in ("true", "1", "yes")
13+
MATRIX_CHAT_ENABLED = os.getenv("MATRIX_CHAT_ENABLED", "false").lower() in (
14+
"true",
15+
"1",
16+
"yes",
17+
)
18+
19+
MATRIX_PING_MESSAGE_PREFIX = os.getenv("MATRIX_PING_MESSAGE_PREFIX", "@matrixping")
20+
MATRIX_PING_HOMESERVER = os.getenv("MATRIX_PING_HOMESERVER", "")
21+
MATRIX_PING_ROOMID = os.getenv("MATRIX_PING_ROOMID", "")
22+
MATRIX_PING_TOKEN = os.getenv("MATRIX_PING_TOKEN", "")

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies = [
1919
"redis>=7.1.0,<8",
2020
"asyncio>=4.0.0,<5",
2121
"matrix-nio>=0.25.2,<0.26",
22+
"markdown>=3.10",
2223
]
2324

2425
[dependency-groups]

uv.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)