Work towards encryption support

This commit is contained in:
Kumi 2023-11-11 17:22:43 +01:00
parent 09393b4216
commit 2269018e92
Signed by: kumi
GPG key ID: ECBCC9082395383F
9 changed files with 76 additions and 33 deletions

3
.gitignore vendored
View file

@ -5,4 +5,5 @@ config.ini
venv/ venv/
*.pyc *.pyc
__pycache__/ __pycache__/
*.bak *.bak
dist/

View file

@ -1,15 +1,13 @@
from nio import ( from nio import (
RoomMessageText, RoomMessageText,
MegolmEvent,
InviteEvent, InviteEvent,
Event, Event,
SyncResponse, SyncResponse,
JoinResponse, JoinResponse,
InviteEvent,
OlmEvent,
MegolmEvent,
RoomMemberEvent, RoomMemberEvent,
Response, Response,
MegolmEvent,
KeysQueryResponse
) )
from .test import test_callback from .test import test_callback
@ -18,18 +16,21 @@ from .invite import room_invite_callback
from .join import join_callback from .join import join_callback
from .message import message_callback from .message import message_callback
from .roommember import roommember_callback from .roommember import roommember_callback
from .encrypted import encrypted_message_callback
from .keys import keys_query_callback
from .test_response import test_response_callback from .test_response import test_response_callback
RESPONSE_CALLBACKS = { RESPONSE_CALLBACKS = {
Response: test_response_callback, Response: test_response_callback,
SyncResponse: sync_callback, SyncResponse: sync_callback,
JoinResponse: join_callback, JoinResponse: join_callback,
#KeysQueryResponse: keys_query_callback,
} }
EVENT_CALLBACKS = { EVENT_CALLBACKS = {
Event: test_callback, Event: test_callback,
InviteEvent: room_invite_callback, InviteEvent: room_invite_callback,
RoomMessageText: message_callback, RoomMessageText: message_callback,
MegolmEvent: message_callback,
RoomMemberEvent: roommember_callback, RoomMemberEvent: roommember_callback,
MegolmEvent: encrypted_message_callback,
} }

View file

@ -0,0 +1,21 @@
from nio import RoomMessage
async def encrypted_message_callback(room, event, bot):
try:
# Request room key from server
room_key = await bot.matrix_client.request_room_key(room.room_id)
# Attempt to decrypt the event
decrypted_event = await bot.matrix_client.decrypt_event(event)
# Check if decryption was successful and the decrypted event has a new type
if isinstance(decrypted_event, RoomMessage):
# Send the decrypted event back to _event_callback for further processing
await bot._event_callback(room, decrypted_event)
else:
# Handle other decrypted event types or log a message
bot.logger.log(f"Decrypted event of type {type(decrypted_event)}", "info")
except Exception as e:
bot.logger.log(f"Error decrypting event: {e}", "error")
await bot.send_message(room.room_id, "Sorry, I was unable to decrypt your message. Please try again, or use an unencrypted room.", True)

View file

@ -15,4 +15,6 @@ async def join_callback(response, bot):
bot.logger.log(f"Adding new room to space {space[0]}...") bot.logger.log(f"Adding new room to space {space[0]}...")
await bot.add_rooms_to_space(space[0], [new_room.room_id]) await bot.add_rooms_to_space(space[0], [new_room.room_id])
bot.matrix_client.keys_upload()
await bot.send_message(bot.matrix_client.rooms[response.room_id], "Hello! Thanks for inviting me! How can I help you today?") await bot.send_message(bot.matrix_client.rooms[response.room_id], "Hello! Thanks for inviting me! How can I help you today?")

View file

@ -0,0 +1,14 @@
from nio.crypto.device import OlmDevice
async def keys_query_callback(response, bot):
bot.matrix_client.receive_response(response)
try:
for user_id, device_dict in response.device_keys.items():
for device_id, keys in device_dict.items():
bot.logger.log(f"New keys for {device_id} from {user_id}: {keys}", "info")
device = OlmDevice(user_id, device_id, keys)
await bot.matrix_client.verify_device(device)
except Exception as e:
bot.logger.log(f"Error handling KeysQueryResponse: {e}", "error")
raise

View file

@ -1,35 +1,14 @@
from nio import MatrixRoom, RoomMessageText, MegolmEvent, RoomKeyRequestError, RoomKeyRequestResponse from nio import MatrixRoom, RoomMessageText
from datetime import datetime from datetime import datetime
async def message_callback(room: MatrixRoom | str, event: RoomMessageText | MegolmEvent, bot): async def message_callback(room: MatrixRoom | str, event: RoomMessageText, bot):
bot.logger.log(f"Received message from {event.sender} in room {room.room_id}") bot.logger.log(f"Received message from {event.sender} in room {room.room_id}")
sent = datetime.fromtimestamp(event.server_timestamp / 1000) sent = datetime.fromtimestamp(event.server_timestamp / 1000)
received = datetime.now() received = datetime.now()
latency = received - sent latency = received - sent
if isinstance(event, MegolmEvent):
try:
event = await bot.matrix_client.decrypt_event(event)
except Exception as e:
try:
bot.logger.log("Requesting new encryption keys...")
response = await bot.matrix_client.request_room_key(event)
if isinstance(response, RoomKeyRequestError):
bot.logger.log(f"Error requesting encryption keys: {response}", "error")
elif isinstance(response, RoomKeyRequestResponse):
bot.logger.log(f"Encryption keys received: {response}", "debug")
bot.matrix_bot.olm.handle_response(response)
event = await bot.matrix_client.decrypt_event(event)
except:
pass
bot.logger.log(f"Error decrypting message: {e}", "error")
await bot.send_message(room, "Sorry, I couldn't decrypt that message. Please try again later or switch to a room without encryption.", True)
return
if event.sender == bot.matrix_client.user_id: if event.sender == bot.matrix_client.user_id:
bot.logger.log("Message is from bot itself - ignoring") bot.logger.log("Message is from bot itself - ignoring")

View file

View file

@ -1,6 +1,8 @@
from nio import RoomMemberEvent, MatrixRoom from nio import RoomMemberEvent, MatrixRoom
async def roommember_callback(room: MatrixRoom, event: RoomMemberEvent, bot): async def roommember_callback(room: MatrixRoom, event: RoomMemberEvent, bot):
bot.matrix_client.keys_upload()
if event.membership == "leave": if event.membership == "leave":
bot.logger.log(f"User {event.state_key} left room {room.room_id} - am I alone now?") bot.logger.log(f"User {event.state_key} left room {room.room_id} - am I alone now?")

View file

@ -33,10 +33,15 @@ from nio import (
RoomMessageAudio, RoomMessageAudio,
DownloadError, DownloadError,
DownloadResponse, DownloadResponse,
RoomKeyRequest,
RoomKeyRequestError,
ToDeviceEvent,
ToDeviceError,
) )
from nio.crypto import Olm from nio.crypto import Olm
from nio.store import SqliteStore from nio.store import SqliteStore
from typing import Optional, List from typing import Optional, List
from configparser import ConfigParser from configparser import ConfigParser
from datetime import datetime from datetime import datetime
@ -228,6 +233,16 @@ class GPTBot:
if len(messages) >= n: if len(messages) >= n:
break break
if isinstance(event, ToDeviceEvent):
try:
event = await self.matrix_client.decrypt_to_device_event(event)
except ToDeviceError:
self.logger.log(
f"Could not decrypt message {event.event_id} in room {room_id}",
"error",
)
continue
if isinstance(event, MegolmEvent): if isinstance(event, MegolmEvent):
try: try:
event = await self.matrix_client.decrypt_event(event) event = await self.matrix_client.decrypt_event(event)
@ -458,7 +473,7 @@ class GPTBot:
invites = self.matrix_client.invited_rooms invites = self.matrix_client.invited_rooms
for invite in invites.keys(): for invite in [k for k in invites.keys()]:
if invite in self.room_ignore_list: if invite in self.room_ignore_list:
self.logger.log( self.logger.log(
f"Ignoring invite to room {invite} (room is in ignore list)", f"Ignoring invite to room {invite} (room is in ignore list)",
@ -734,7 +749,7 @@ class GPTBot:
) )
# Run initial sync (now includes joining rooms) # Run initial sync (now includes joining rooms)
sync = await self.matrix_client.sync(timeout=30000) sync = await self.matrix_client.sync(timeout=30000, full_state=True)
if isinstance(sync, SyncResponse): if isinstance(sync, SyncResponse):
await self.response_callback(sync) await self.response_callback(sync)
else: else:
@ -773,10 +788,18 @@ class GPTBot:
# Start syncing events # Start syncing events
self.logger.log("Starting sync loop...", "warning") self.logger.log("Starting sync loop...", "warning")
try: try:
await self.matrix_client.sync_forever(timeout=30000) await self.matrix_client.sync_forever(timeout=30000, full_state=True)
finally: finally:
self.logger.log("Syncing one last time...", "warning") self.logger.log("Syncing one last time...", "warning")
await self.matrix_client.sync(timeout=30000) await self.matrix_client.sync(timeout=30000, full_state=True)
async def request_keys(session_id, room_id):
request = RoomKeyRequest(session_id, room_id)
response = await client.send(request)
if isinstance(response, RoomKeyRequestError):
print(f"Failed to request keys for session {session_id}: {response.message}")
else:
print(f"Requested keys for session {session_id}")
async def create_space(self, name, visibility=RoomVisibility.private) -> str: async def create_space(self, name, visibility=RoomVisibility.private) -> str:
"""Create a space. """Create a space.