# permissions.py # -*- coding: utf-8 -*- """ A plugin for irc3 that provides a permission system using TinyDB, including message blocking. Author: Your Name License: MIT """ import irc3 from irc3.plugins.command import command from tinydb import TinyDB, Query import fnmatch from ircstyle import style @irc3.plugin class TinyDBPermissions: """Integrated permissions plugin with message blocking""" priority = 10 # Lower number = higher priority def __init__(self, bot): self.bot = bot self.permission_db = TinyDB('permissions.json') self.bot.permission_db = self.permission_db self.User = Query() self.bot.log.info("TinyDB permissions plugin initialized") @irc3.event(irc3.rfc.PRIVMSG) async def check_ignored_users(self, mask, event, target, data): """Block messages from ignored users first""" hostmask = str(mask) ignored = self.permission_db.search(self.User.permission == 'ignore') for entry in ignored: if fnmatch.fnmatch(hostmask, entry['mask']): self.bot.log.debug(f"Blocking message from ignored user: {hostmask}") return True # Block processing return False @command(permission='admin') def perm(self, mask, target, args): """ Manage permissions. Use --add, --del, or --list. Usage: %%perm --add %%perm --del %%perm --list [] """ if args['--add']: self._add_permission(target, args[''], args['']) elif args['--del']: self._del_permission(target, args[''], args['']) elif args['--list']: self._list_permissions(target, args['']) else: error_msg = style( "Invalid syntax. Use --add, --del, or --list.", fg='red', bold=True ) self.bot.privmsg(target, error_msg) def _add_permission(self, target, user_mask, perm): existing = self.permission_db.search( (self.User.mask == user_mask) & (self.User.permission == perm) ) if existing: msg = style( f"Permission '{perm}' already exists for {user_mask}", fg='yellow', bold=True ) else: self.permission_db.insert({'mask': user_mask, 'permission': perm}) msg = style( f"Added permission '{perm}' for {user_mask}", fg='green', bold=True ) self.bot.privmsg(target, msg) def _del_permission(self, target, user_mask, perm): removed = self.permission_db.remove( (self.User.mask == user_mask) & (self.User.permission == perm) ) if removed: msg = style( f"Removed {len(removed)} '{perm}' permission(s) for {user_mask}", fg='green', bold=True ) else: msg = style( f"No '{perm}' permissions found for {user_mask}", fg='red', bold=True ) self.bot.privmsg(target, msg) def _list_permissions(self, target, mask_filter): mask_filter = mask_filter or '*' regex = fnmatch.translate(mask_filter).split('(?ms)')[0].rstrip('\\Z') entries = self.permission_db.search(self.User.mask.matches(regex)) if not entries: msg = style("No permissions found", fg='red', bold=True) self.bot.privmsg(target, msg) return for entry in entries: msg = style( f"{entry['mask']}: {entry['permission']}", fg='blue', bold=True ) self.bot.privmsg(target, msg) @command(permission='admin') def ignore(self, mask, target, args): """Manage ignores Usage: %%ignore --add %%ignore --del """ nick = args[''] user_mask = f"{nick}!*@*" if args['--add']: if self.permission_db.contains( (self.User.mask == user_mask) & (self.User.permission == 'ignore') ): msg = style(f"{nick} already ignored", fg='yellow', bold=True) else: self.permission_db.insert({'mask': user_mask, 'permission': 'ignore'}) msg = style(f"Ignored {nick}", fg='green', bold=True) elif args['--del']: removed = self.permission_db.remove( (self.User.mask == user_mask) & (self.User.permission == 'ignore') ) msg = style(f"Unignored {nick} ({len(removed)} entries)", fg='green', bold=True) else: msg = style("Invalid syntax", fg='red', bold=True) self.bot.privmsg(target, msg) class TinyDBPolicy: """Permission policy enforcement""" def __init__(self, bot): self.bot = bot self.User = Query() def has_permission(self, client_mask, permission): # Check ignore list first using fnmatch ignored_entries = self.bot.permission_db.search( self.User.permission == 'ignore' ) for entry in ignored_entries: if fnmatch.fnmatch(client_mask, entry['mask']): return False # Check permissions if not ignored if permission is None: return True perm_entries = self.bot.permission_db.search( (self.User.permission == permission) | (self.User.permission == 'all_permissions') ) for entry in perm_entries: if fnmatch.fnmatch(client_mask, entry['mask']): return True return False def __call__(self, predicates, meth, client, target, args): cmd_name = predicates.get('name', meth.__name__) client_hostmask = str(client) if self.has_permission(client_hostmask, predicates.get('permission')): return meth(client, target, args) error_msg = style( f"Access denied for '{cmd_name}' command", fg='red', bold=True ) self.bot.privmsg(client.nick, error_msg)