2024-08-19 15:08:37 +00:00
import asyncio
import pathlib
import yaml
from nio import (
AsyncClient ,
MatrixRoom ,
RoomMessageText ,
RoomCreateResponse ,
RoomGetStateResponse ,
)
2024-08-25 11:53:41 +00:00
class AwarenessBot :
2024-08-19 15:08:37 +00:00
def __init__ ( self , config_path ) :
# Load configuration from YAML file
with open ( config_path , " r " ) as config_file :
self . config = yaml . safe_load ( config_file )
# Load dictionary from YAML file specified in the config
with open (
self . config . get (
" dictionary_path " ,
pathlib . Path ( __file__ ) . parent / " dictionary " / " default.yaml " ,
) ,
" r " ,
) as dictionary_file :
dictionary = yaml . safe_load ( dictionary_file )
self . non_inclusive_terms = dictionary [ " non_inclusive_terms " ]
self . unacceptable_terms = dictionary [ " unacceptable_terms " ]
# Initialize the client with server URL and credentials from config
self . client = AsyncClient ( self . config [ " server_url " ] , self . config [ " username " ] )
def check_inclusive_language ( self , message ) :
suggestions = [ ]
for term , details in self . non_inclusive_terms . items ( ) :
if term in message :
suggestions . append (
{
" term " : term ,
" replacement " : details [ " replacement " ] ,
" reason " : details [ " reason " ] ,
}
)
return suggestions
def check_unacceptable_language ( self , message ) :
for entry in self . unacceptable_terms :
term = entry [ " term " ]
if term in message :
return entry
return None
async def get_dm_room ( self , user_id ) :
# Check if a DM room already exists
for room_id , room in self . client . rooms . items ( ) :
if user_id in room . users and len ( room . users ) == 2 :
return room_id
# Create a new DM room with the user
response = await self . client . room_create ( invite = [ user_id ] , is_direct = True )
if isinstance ( response , RoomCreateResponse ) :
return response . room_id
else :
return None # Failed to create or invite to a room
async def send_private_message ( self , user_id , message ) :
room_id = await self . get_dm_room ( user_id )
if room_id :
await self . client . room_send (
room_id ,
message_type = " m.room.message " ,
content = { " msgtype " : " m.text " , " body " : message } ,
)
async def get_room_admins ( self , room_id ) :
response = await self . client . room_get_state ( room_id )
if isinstance ( response , RoomGetStateResponse ) :
admins = [ ]
for event in response . events :
if event . type == " m.room.power_levels " :
power_levels = event . content
for user , level in power_levels . get ( " users " , { } ) . items ( ) :
if level > = 100 : # TODO: Make this configurable or something
admins . append ( user )
return admins
return [ ]
async def message_callback ( self , room : MatrixRoom , event : RoomMessageText ) :
if event . sender == self . client . user :
return # Ignore messages sent by the bot
# Check for unacceptable language
unacceptable_entry = self . check_unacceptable_language ( event . body )
if unacceptable_entry :
admin_response = f " Attention: The term ' { unacceptable_entry [ ' term ' ] } ' used by { event . sender } in { room . room_id } might be unacceptable ( { unacceptable_entry [ ' reason ' ] } ). "
# Get room admins and send them a message
admins = await self . get_room_admins ( room . room_id )
for admin in admins :
await self . send_private_message ( admin , admin_response )
response = f " Your message in { room . room_id } contains the term ' { unacceptable_entry [ ' term ' ] } ' which might be unacceptable ( { unacceptable_entry [ ' reason ' ] } ). Please consider revising it. "
# Send private message to the user
await self . send_private_message ( event . sender , response )
# Check for non-inclusive language
suggestions = self . check_inclusive_language ( event . body )
if suggestions :
response = f " I ' ve noticed some potentially non-inclusive language in your message in { room . room_id } : \n "
for suggestion in suggestions :
response + = f " - ' { suggestion [ ' term ' ] } ' could potentially be replaced with ' { suggestion [ ' replacement ' ] } ' ( { suggestion [ ' reason ' ] } ). \n "
response + = " Thank you for using inclusive language and making our community a better place! 🌈🦄 "
# Send private message to the user
await self . send_private_message ( event . sender , response )
async def run ( self ) :
await self . client . login ( self . config [ " password " ] )
self . client . add_event_callback ( self . message_callback , RoomMessageText )
await self . client . sync_forever ( timeout = 30000 )
def main ( ) :
2024-08-25 11:53:41 +00:00
bot = AwarenessBot ( " config.yaml " )
2024-08-19 15:08:37 +00:00
asyncio . get_event_loop ( ) . run_until_complete ( bot . run ( ) )
if __name__ == " __main__ " :
main ( )