Archived
1
0
Fork 0
This repository has been archived on 2024-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
akari-bot/bots/matrix/bot.py
xtex 23ae1fb5f2
feat: partial matrix support (#680)
* build(deps): add matrix-nio

* build(deps): update requirements.txt for matrix-nio

* feat: add example config for matrix

* feat: init matrix support

* feat: do init sync to avoid receiving old messages

* feat: store next sync batch with file

* feat: parse matrix message

* feat: check moderator permission

* feat: redacting message

* feat: toMessageChain for m.text and m.image

* feat: drop m.notice messages

* feat: sending messages

* fix: power level fetching

* fix: rich reply fallback stripping

* style: less log

* feat: guess image content type

* fix: image upload

* feat: sending audio

* chore: add todo

* chore: add todo

* fix: use FinS.result to store room id

As the spec, room_id is an opaque identifier, and at least all valid ASCII characters except '/' and ':' can be included in room_id. This means in some servers, '|' may be included in room_id.

* fix: print RoomSendError

* fix: public rooms with two user are regarded as DM

* feat: resolving pm

* fix: nio.ErrorResponse

* feat: add post test

* feat: pm post test

* Revert "feat: pm post test"

This reverts commit 3688213c80.

* Revert "feat: add post test"

This reverts commit 259fcf54ca.

* feat: create TPM room

* feat: send typing notifications

* feat: leave empty room

* feat: rich reply formatted fallback support

* fix: import

* fix: include body in formatted rich reply fallbacks

* fix: escape line breaks in html

* fix: <br/> tag

* feat: improve matrix direct messaging

* feat: parse m.emote as m.text

* fix: command run blockingly

* fix: message send error handle

* fix: keep DM room

* fix: reuse of DM rooms

* feat: add error log

* docs: add deploy guide for matrix

* docs: add matrix to readme

* docs: add more warning

* style: remove explictly type declartion  (poljar/matrix-nio#417)

* feat: receive audio message

* feat: standard rich-reply fallback for m.emote

* chore: add matrix to bug report issues template
2023-07-30 17:41:01 +08:00

98 lines
4 KiB
Python

import asyncio
import os
from tracemalloc import start
from bots.matrix import client
from bots.matrix.client import bot
import nio
from core.builtins import PrivateAssets, Url
from core.logger import Logger
from core.parser.message import parser
from core.types import MsgInfo, Session
from core.utils.bot import load_prompt, init_async
from bots.matrix.message import MessageSession, FetchTarget
PrivateAssets.set(os.path.abspath(os.path.dirname(__file__) + "/assets"))
Url.disable_mm = True
async def on_sync(resp: nio.SyncResponse):
with open(client.store_path_next_batch, 'w') as fp:
fp.write(resp.next_batch)
async def on_invite(room: nio.MatrixRoom, event: nio.InviteEvent):
Logger.info(f"Received room invitation for {room.room_id} ({room.name}) from {event.sender}")
await bot.join(room.room_id)
Logger.info(f"Joined room {room.room_id}")
async def on_room_member(room: nio.MatrixRoom, event: nio.RoomMemberEvent):
Logger.info(f"Received m.room.member, {event.sender} : {event.prev_membership} -> {event.membership}")
if event.sender == client.user:
pass
isDirect = (room.member_count == 1 or room.member_count == 2) and room.join_rule == 'invite'
if not isDirect:
resp = await bot.room_get_state_event(room.room_id, 'm.room.member', client.user)
if 'prev_content' in resp.__dict__ and 'is_direct' in resp.__dict__[
'prev_content'] and resp.__dict__['prev_content']['is_direct']:
isDirect = True
if isDirect and room.member_count == 1 and event.membership == 'leave':
resp = await bot.room_leave(room.room_id)
if resp is nio.ErrorResponse:
Logger.error(f"Error leaving empty room {room.room_id}: {str(resp)}")
else:
Logger.info(f"Left empty room {room.room_id}")
async def on_message(room: nio.MatrixRoom, event: nio.RoomMessageFormatted):
if event.source['content']['msgtype'] == 'm.notice':
# https://spec.matrix.org/v1.7/client-server-api/#mnotice
return
isRoom = room.member_count != 2 or room.join_rule != 'invite'
targetId = room.room_id if isRoom else event.sender
replyId = None
if 'm.relates_to' in event.source['content'] and 'm.in_reply_to' in event.source['content']['m.relates_to']:
replyId = event.source['content']['m.relates_to']['m.in_reply_to']['event_id']
senderName = (await bot.get_displayname(event.sender)).displayname
msg = MessageSession(MsgInfo(targetId=f'Matrix|{targetId}',
senderId=f'Matrix|{event.sender}',
targetFrom=f'Matrix',
senderFrom='Matrix',
senderName=senderName,
clientName='Matrix',
messageId=event.event_id,
replyId=replyId),
Session(message=event.source, target=room.room_id, sender=event.sender))
asyncio.create_task(parser(msg))
async def start():
# Logger.info(f"trying first sync")
# sync = await bot.sync()
# Logger.info(f"first sync finished in {sync.elapsed}ms, dropped older messages")
# if sync is nio.SyncError:
# Logger.error(f"failed in first sync: {sync.status_code} - {sync.message}")
try:
with open(client.store_path_next_batch, 'r') as fp:
bot.next_batch = fp.read()
Logger.info(f"loaded next sync batch from storage: {bot.next_batch}")
except FileNotFoundError:
bot.next_batch = 0
bot.add_response_callback(on_sync, nio.SyncResponse)
bot.add_event_callback(on_invite, nio.InviteEvent)
bot.add_event_callback(on_room_member, nio.RoomMemberEvent)
bot.add_event_callback(on_message, nio.RoomMessageFormatted)
await init_async()
await load_prompt(FetchTarget)
Logger.info(f"starting sync loop")
await bot.sync_forever(timeout=30000, full_state=True, set_presence='online')
Logger.error(f"sync loop stopped")
if bot:
asyncio.run(start())