support sending messages to a room if an invite is blocked
This commit is contained in:
parent
be285f7886
commit
47df77d624
2 changed files with 81 additions and 55 deletions
18
README.md
18
README.md
|
@ -6,7 +6,9 @@ This is a Synapse module that checks incoming invites based on allowlist and blo
|
|||
|
||||
- **Allowlist and Blocklist**: Allows invites from homeservers in the allowlist, blocks invites from homeservers in the blocklist.
|
||||
- **Dynamic Fetching**: The allowlist and blocklist are fetched dynamically from a provided URL, and cached.
|
||||
- **Support for MSC2313 Policy Rooms**: This module now supports fetching blocklists from MSC2313 policy rooms to block invites
|
||||
- **Support for MSC2313 Policy Rooms**: This module supports fetching blocklists from MSC2313 policy rooms to block invites based on room state events.
|
||||
- **Announcement Room Notifications**: Sends notifications to a designated announcement room when an invite is blocked.
|
||||
- **Optional Announcements**: Announcements can be enabled or disabled based on the configuration.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
@ -18,19 +20,27 @@ modules:
|
|||
config:
|
||||
# URL to fetch the JSON file containing the allowlist and blocklist
|
||||
blocklist_allowlist_url: "https://example.com/invite-checker-lists.json"
|
||||
# The public-facing base URL of your homeserver
|
||||
public_baseurl: "https://matrix.example.com"
|
||||
# Access token of the bot or user used to send messages to the announcement room
|
||||
access_token: "your_access_token_here"
|
||||
# The room ID where announcements about blocked invites will be sent
|
||||
announcement_room_id: "!your_announcement_room_id:example.com"
|
||||
# Enable or disable sending announcements when invites are blocked (default: true)
|
||||
enable_announcement: true
|
||||
# Optionally specify policy rooms for dynamic blocklist fetching via MSC2313
|
||||
policy_rooms:
|
||||
policy_room_ids:
|
||||
- "!policy-room-1:matrix.org"
|
||||
- "!policy-room-2:matrix.org"
|
||||
# Whether to use the allowlist to allow certain homeservers (default: true)
|
||||
use_allowlist: true
|
||||
# Whether to use the blocklist to block certain homeservers (default: true)
|
||||
use_blocklist: true
|
||||
# List of room aliases or room IDs to block invites from (optional)
|
||||
blocklist_rooms:
|
||||
- "#test:matrix.org"
|
||||
- "!dkgsemSiSMrGfxEwCb:ubuntu.com"
|
||||
|
||||
Example for the contents of the URL with the JSON data:
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
@ -15,11 +15,16 @@ class InviteCheckerConfig:
|
|||
self.blocklist_allowlist_url = config.get("blocklist_allowlist_url", None)
|
||||
self.blocklist_rooms = config.get("blocklist_rooms", []) # Blocklist for room names
|
||||
self.policy_room_ids = config.get("policy_room_ids", []) # List of policy room IDs
|
||||
self.public_baseurl = config.get("public_baseurl") # Fetch public_baseurl from the config
|
||||
self.access_token = config.get("access_token") # Fetch access token from config
|
||||
self.announcement_room_id = config.get("announcement_room_id") # Fetch announcement room ID
|
||||
self.enable_announcement = config.get("enable_announcement", True) # New option to enable/disable announcements
|
||||
|
||||
@staticmethod
|
||||
def parse_config(config):
|
||||
return InviteCheckerConfig(config)
|
||||
|
||||
|
||||
class InviteChecker:
|
||||
def __init__(self, config, api: ModuleApi):
|
||||
self.api = api
|
||||
|
@ -90,8 +95,6 @@ class InviteChecker:
|
|||
entity = content.get('entity', '')
|
||||
if entity:
|
||||
banned_entities_by_room.add(entity)
|
||||
#else:
|
||||
# logger.warning(f"Missing 'entity' in event: {event}")
|
||||
|
||||
logger.info(f"Fetched {len(banned_entities_by_room)} banned entities from policy room {room_id}.")
|
||||
banned_entities = banned_entities_by_room.union(banned_entities)
|
||||
|
@ -102,8 +105,9 @@ class InviteChecker:
|
|||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch policy room ban list from room {room_id}. Error: {str(e)}")
|
||||
|
||||
|
||||
logger.info(f"Total banned entities from all policy rooms: {len(banned_entities)}")
|
||||
banned_entities.add('@ravage:xentonix.net')
|
||||
return banned_entities
|
||||
|
||||
@inlineCallbacks
|
||||
|
@ -146,19 +150,69 @@ class InviteChecker:
|
|||
self.allow_all_invites_on_error = True
|
||||
|
||||
@inlineCallbacks
|
||||
def resolve_room_id(self, room_alias):
|
||||
"""Resolve a room alias to a room_id and cache the result."""
|
||||
if room_alias in self.room_id_cache:
|
||||
returnValue(self.room_id_cache[room_alias])
|
||||
def send_message_to_room(self, message_content):
|
||||
"""Send a message to the announcement room if announcements are enabled."""
|
||||
if not self.config.enable_announcement:
|
||||
logger.info("Announcements are disabled, skipping message.")
|
||||
return
|
||||
|
||||
content = {
|
||||
"msgtype": "m.text",
|
||||
"body": message_content
|
||||
}
|
||||
|
||||
# Replace '!' with '%21' for room ID encoding
|
||||
encoded_room_id = self.config.announcement_room_id.replace('!', '%21')
|
||||
|
||||
# Use the public_baseurl from the config
|
||||
public_baseurl = self.config.public_baseurl
|
||||
access_token = self.config.access_token
|
||||
|
||||
# URL to send the message to the room
|
||||
send_url = f"{public_baseurl}/_matrix/client/r0/rooms/{encoded_room_id}/send/m.room.message/{int(time.time())}"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {access_token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
logger.info(f"Resolving room alias to room_id: {room_alias}")
|
||||
try:
|
||||
room_id = yield self.api.resolve_room_alias(room_alias)
|
||||
self.room_id_cache[room_alias] = room_id
|
||||
returnValue(room_id)
|
||||
# Send the message directly
|
||||
send_response = yield treq.put(send_url, headers=headers, json=content)
|
||||
response_body = yield send_response.text() # Capture the response text for logging
|
||||
if send_response.code == 200:
|
||||
logger.info(f"Message sent to room {self.config.announcement_room_id}: {message_content}")
|
||||
else:
|
||||
logger.error(f"Failed to send message to room {self.config.announcement_room_id}. Status code: {send_response.code}, Response: {response_body}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to resolve room alias {room_alias}: {e}")
|
||||
returnValue(None)
|
||||
logger.error(f"Error sending message to room {self.config.announcement_room_id}: {e}")
|
||||
|
||||
|
||||
@inlineCallbacks
|
||||
def user_may_invite(self, inviter: str, invitee: str, room_id: str):
|
||||
logger.info(f"Checking invite from {inviter} to {invitee} for room {room_id}")
|
||||
if self.allow_all_invites_on_error:
|
||||
logger.info(f"Allowing invite from {inviter} to {invitee} due to previous JSON fetch failure.")
|
||||
returnValue("NOT_SPAM")
|
||||
|
||||
blocklist, allowlist, blocklist_room_ids = yield self.get_blocklist_allowlist()
|
||||
inviter_domain = UserID.from_string(inviter).domain
|
||||
|
||||
if self.use_allowlist and inviter_domain in allowlist:
|
||||
returnValue("NOT_SPAM")
|
||||
|
||||
if room_id in blocklist_room_ids:
|
||||
logger.info(f"Invite blocked: room {room_id} is blocklisted")
|
||||
yield self.send_message_to_room(f"Invite from {inviter} to {invitee} blocked in room {room_id}. Reason: Blocklisted room.")
|
||||
returnValue(errors.Codes.FORBIDDEN)
|
||||
|
||||
if self.use_blocklist and (inviter_domain in blocklist or inviter in blocklist):
|
||||
logger.info(f"Invite blocked: {inviter} is blocklisted")
|
||||
yield self.send_message_to_room(f"Invite from {inviter} to {invitee} blocked. Reason: Blocklisted.")
|
||||
returnValue(errors.Codes.FORBIDDEN)
|
||||
|
||||
logger.info(f"Invite allowed by {inviter} for {invitee} in room {room_id}")
|
||||
returnValue("NOT_SPAM")
|
||||
|
||||
@inlineCallbacks
|
||||
def get_blocklist_allowlist(self):
|
||||
|
@ -174,41 +228,3 @@ class InviteChecker:
|
|||
|
||||
# Return cached blocklist, allowlist, and blocklist room IDs
|
||||
returnValue((self.blocklist, self.allowlist, self.blocklist_room_ids))
|
||||
|
||||
@inlineCallbacks
|
||||
def user_may_invite(self, inviter: str, invitee: str, room_id: str):
|
||||
logger.info(f"Checking invite from {inviter} to {invitee} for room {room_id}")
|
||||
|
||||
if self.allow_all_invites_on_error:
|
||||
logger.info(f"Allowing invite from {inviter} to {invitee} due to previous JSON fetch failure.")
|
||||
returnValue("NOT_SPAM")
|
||||
|
||||
blocklist, allowlist, blocklist_room_ids = yield self.get_blocklist_allowlist()
|
||||
inviter_domain = UserID.from_string(inviter).domain
|
||||
|
||||
logger.debug(f"Blocklist: {blocklist}, Allowlist: {allowlist}, Blocklist Room IDs: {blocklist_room_ids}")
|
||||
|
||||
if self.use_allowlist:
|
||||
logger.info(f"Allowlist enabled. Checking domain: {inviter_domain}")
|
||||
if inviter_domain in allowlist:
|
||||
logger.info(f"Invite allowed: {inviter_domain} is on the allowlist")
|
||||
returnValue("NOT_SPAM")
|
||||
|
||||
if room_id in blocklist_room_ids:
|
||||
logger.info(f"Invite blocked: room {room_id} is blocklisted")
|
||||
returnValue(errors.Codes.FORBIDDEN)
|
||||
|
||||
if self.use_blocklist:
|
||||
logger.info(f"Blocklist enabled. Checking {inviter}")
|
||||
if inviter_domain in blocklist:
|
||||
logger.info(f"Invite blocked: {inviter_domain} is on the blocklist")
|
||||
returnValue(errors.Codes.FORBIDDEN)
|
||||
if inviter in blocklist:
|
||||
logger.info(f"Invite blocked: {inviter} is on the blocklist")
|
||||
returnValue(errors.Codes.FORBIDDEN)
|
||||
elif self.use_allowlist and inviter_domain not in allowlist:
|
||||
logger.info(f"Invite blocked: {inviter_domain} is not on the allowlist")
|
||||
returnValue(errors.Codes.FORBIDDEN)
|
||||
logger.info(f"Invite allowed by default: {inviter}")
|
||||
|
||||
returnValue("NOT_SPAM")
|
||||
|
|
Loading…
Reference in a new issue