diff --git a/evennia/commands/default/comms.py b/evennia/commands/default/comms.py index c118a61fd0..a68abe14d0 100644 --- a/evennia/commands/default/comms.py +++ b/evennia/commands/default/comms.py @@ -18,6 +18,7 @@ from evennia.locks.lockhandler import LockException from evennia.utils import create, logger, utils, evtable from evennia.utils.logger import tail_log_file from evennia.utils.utils import make_iter, class_from_module +from evennia.utils import evmore COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS) CHANNEL_DEFAULT_TYPECLASS = class_from_module( @@ -49,342 +50,6 @@ _DEFAULT_WIDTH = settings.CLIENT_DEFAULT_WIDTH # helper functions to make it easier to override the main CmdChannel # command and to keep the legacy addcom etc commands around. -def search_channel(caller, channelname, exact=False): - """ - Helper function for searching for a single channel with some error - handling. - - Args: - channelname (str): Name, alias #dbref or partial name/alias to search - for. - exact (bool, optional): If an exact or fuzzy-match of the name should be done. - Note that even for a fuzzy match, an exactly given, unique channel name - will always be returned. - Returns: - list: A list of zero, one or more channels found. - - Notes: - No permission checks will be done here. - - """ - # first see if this is a personal alias - channelname = caller.nicks.get(key=channelname, category="channel") or channelname - - # always try the exact match first. - channels = CHANNEL_DEFAULT_TYPECLASS.objects.channel_search(channelname, exact=True) - - if not channels and not exact: - # try fuzzy matching as well - channels = CHANNEL_DEFAULT_TYPECLASS.objects.channel_search(channelname, exact=exact) - - - # check permissions - channels = [channel for channel in channels - if channel.access(caller, 'listen') or channel.access(caller, 'control')] - - if not channels: - return [] - elif len(channels) > 1: - return list(channels) - return [channels[0]] - - -def msg_channel(caller, channel, message, **kwargs): - """ - Send a message to a given channel. At this point - any permissions should already be done. - - Args: - caller (Object or Account): The entity sending the message. - channel (Channel): The channel to send to. - message (str): The message to send. - **kwargs: Unused by default. These kwargs will be passed into - all channel messaging hooks for custom overriding. - - """ - channel.msg(message, senders=caller, **kwargs) - - -def get_channel_history(caller, channel, start_index=0): - """ - View a channel's history. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to access. - message (str): The message to send. - **kwargs: Unused by default. These kwargs will be passed into - all channel messaging hooks for custom overriding. - - """ - log_file = channel.attributes.get( - "log_file", default=channel.log_file.format(channelkey=channel.key)) - - def send_msg(lines): - return caller.msg( - "".join(line.split("[-]", 1)[1] if "[-]" in line else line for line in lines) - ) - # asynchronously tail the log file - tail_log_file(log_file, start_index, 20, callback=send_msg) - -def sub_to_channel(caller, channel): - """ - Subscribe to a channel. Note that all permissions should - be checked before this step. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to access. - - Returns: - bool, str: True, None if connection failed. If False, - the second part is an error string. - - """ - if channel.has_connection(caller): - return False, f"Already listening to channel {channel.key}." - result = channel.connect(caller) - return result, "" if result else f"Were not allowed to subscribe to channel {channel.key}" - - -def unsub_to_channel(caller, channel): - """ - Un-Subscribe to a channel. Note that all permissions should - be checked before this step. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to unsub from. - - Returns: - bool, str: True, None if un-connection succeeded. If False, - the second part is an error string. - - """ - if not channel.has_connection(caller): - return False, f"Not listening to channel {channel.key}." - # clear nicks - chkey = channel.key - for nick in [ - nick - for nick in make_iter(caller.nicks.get(category="channel", return_obj=True)) - if nick and nick.pk and nick.value[3].lower() == chkey - ]: - nick.delete() - result = channel.disconnect(caller) - return result, "" if result else f"Could not unsubscribe from channel {channel.key}" - - -def add_alias(caller, channel, alias): - """ - Add a new alias for the user to use with this channel. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to alias. - alias (str): The personal alias to use for this channel. - - """ - caller.nicks.add(alias, channel.key, category="channel") - - -def remove_alias(caller, alias): - """ - Remove an alias from a given channel. - - Args: - caller (Object or Account): The entity performing the action. - alias (str, optional): The alias to remove, or `None` for all. - - Returns: - bool, str: True, None if removal succeeded. If False, - the second part is an error string. - - """ - channame = caller.nicks.get(key=alias, category="channel") - if channame: - caller.nicks.remove(key=alias, category="channel") - return True, "" - return False, "No such alias was defined." - - -def mute_channel(caller, channel): - """ - Temporarily mute a channel. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to alias. - - Returns: - bool, str: True, None if muting successful. If False, - the second part is an error string. - """ - if channel.mute(caller): - return True, "" - return False, f"Channel {channel.key} was already muted." - - -def unmute_channel(caller, channel): - """ - Unmute a channel. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to alias. - - Returns: - bool, str: True, None if unmuting successful. If False, - the second part is an error string. - - """ - if channel.unmute(caller): - return True, "" - return False, f"Channel {channel.key} was already unmuted." - - -def create_channel(caller, name, description, aliases=None): - """ - Create a new channel. Its name must not previously exist - (users can alias as needed). Will also connect to the - new channel. - - Args: - caller (Object or Account): The entity performing the action. - name (str): The new channel name/key. - description (str): This is used in listings. - aliases (list): A list of strings - alternative aliases for the channel - (not to be confused with per-user aliases; these are available for - everyone). - - Returns: - channel, str: new_channel, "" if creation successful. If False, - the second part is an error string. - - """ - if CHANNEL_DEFAULT_TYPECLASS.objects.channel_search(name, exact=True): - return False, f"Channel {name} already exists." - # set up the new channel - lockstring = "send:all();listen:all();control:id(%s)" % caller.id - new_chan = create.create_channel(name, aliases=aliases,desc=description, locks=lockstring) - new_chan.connect(caller) - return new_chan, "" - -def destroy_channel(caller, channel, message=None): - """ - Destroy an existing channel. Access should be checked before - calling this function. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to alias. - message (str, optional): Final message to send onto the channel - before destroying it. If not given, a default message is - used. Set to the empty string for no message. - - """ - channel_key = channel.key - if message is None: - message = (f"|rChannel {channel_key} is being destroyed. " - "Make sure to clean any channel aliases.|n") - if message: - channel.msg(message, senders=caller, bypass_mute=True) - channel.delete() - logger.log_sec( - "Channel {} was deleted by {}".format(channel_key, caller) - ) - - -def set_lock(caller, channel, lockstring): - """ - Set a lockstring on a channel. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to operate on. - lockstring (str): A lockstring on the form 'type:lockfunc();...' - - Returns: - bool, str: True, None if setting lock was successful. If False, - the second part is an error string. - - """ - try: - channel.locks.add(lockstring) - except LockException as err: - return False, err - return True, "" - - -def set_desc(caller, channel, description): - """ - Set a channel description. This is shown in listings etc. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to operate on. - description (str): A short description of the channel. - - Returns: - bool, str: True, None if setting lock was successful. If False, - the second part is an error string. - - """ - channel.db.desc = description - -def boot_user(caller, channel, target, quiet=False, reason=""): - """ - Boot a user from a channel, with optional reason. This will - also remove all their aliases for this channel. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to operate on. - target (Object or Account): The entity to boot. - quiet (bool, optional): Whether or not to announce to channel. - reason (str, optional): A reason for the boot. - - Returns: - bool, str: True, None if setting lock was successful. If False, - the second part is an error string. - - """ - if not channel.subscriptions.has(target): - return False, f"{target} is not connected to channel {channel.key}." - # find all of target's nicks linked to this channel and delete them - for nick in [ - nick - for nick in target.nicks.get(category="channel") or [] - if nick.value[3].lower() == channel.key - ]: - nick.delete() - channel.disconnect(target) - reason = f" Reason: {reason}" if reason else "" - target.msg(f"You were booted from channel {channel.key} by {caller.key}.{reason}") - if not quiet: - channel.msg(f"{target.key} was booted from channel by {caller.key}.{reason}") - - logger.log_sec(f"Channel Boot: {target} (Channel: {channel}, " - f"Reason: {reason}, Caller: {caller}") - - -def ban_user(caller, channel, target, quiet=False, reason=""): - """ - Ban a user from a channel, by locking them out. This will also - boot them, if they are currently connected. - - Args: - caller (Object or Account): The entity performing the action. - channel (Channel): The channel to operate on. - target (Object or Account): The entity to ban - quiet (bool, optional): Whether or not to announce to channel. - reason (str, optional): A reason for the ban - - """ - result, err = boot_user(caller, channel, target, quiet=quiet, reason=reason) - - - class CmdChannel(COMMAND_DEFAULT_CLASS): """ @@ -392,6 +57,9 @@ class CmdChannel(COMMAND_DEFAULT_CLASS): Usage: channel channelname [= ] + channel + channel/list + channel/all channel/history channelname [= index] channel/sub channelname [= alias] channel/unsub channelname[,channelname, ...] @@ -399,14 +67,14 @@ class CmdChannel(COMMAND_DEFAULT_CLASS): channel/unalias channelname = alias channel/mute channelname[,channelname,...] channel/unmute channelname[,channelname,...] - channel/create channelname [= description] + channel/create channelname;alias;alias:typeclass [= description] channel/destroy channelname [: reason] channel/lock channelname = lockstring channel/desc channelname = description channel/boot[/quiet] channelname = subscribername [: reason] + channel/ban channelname + channel/ban[/quiet] channelname = subscribername [: reason] channel/who channelname - channel/list - channels This handles all operations on channels. @@ -418,13 +86,642 @@ class CmdChannel(COMMAND_DEFAULT_CLASS): "history", "sub", "unsub", "mute", "alias", "unalias", "create", "destroy", "desc", "boot", "who") - def parse(self): - super().parse() - self.channelnames = self.lhslist + def search_channel(self, channelname, exact=False): + """ + Helper function for searching for a single channel with some error + handling. + + Args: + channelname (str): Name, alias #dbref or partial name/alias to search + for. + exact (bool, optional): If an exact or fuzzy-match of the name should be done. + Note that even for a fuzzy match, an exactly given, unique channel name + will always be returned. + Returns: + list: A list of zero, one or more channels found. + + Notes: + The 'listen' and 'control' accesses are checked before returning. + + """ + caller = self.caller + # first see if this is a personal alias + channelname = caller.nicks.get(key=channelname, category="channel") or channelname + + # always try the exact match first. + channels = CHANNEL_DEFAULT_TYPECLASS.objects.channel_search(channelname, exact=True) + + if not channels and not exact: + # try fuzzy matching as well + channels = CHANNEL_DEFAULT_TYPECLASS.objects.channel_search(channelname, exact=exact) + + # check permissions + channels = [channel for channel in channels + if channel.access(caller, 'listen') or channel.access(caller, 'control')] + + if not channels: + return [] + elif len(channels) > 1: + return list(channels) + return [channels[0]] + + def msg_channel(self, channel, message, **kwargs): + """ + Send a message to a given channel. At this point + any permissions should already be done. + + Args: + channel (Channel): The channel to send to. + message (str): The message to send. + **kwargs: Unused by default. These kwargs will be passed into + all channel messaging hooks for custom overriding. + + """ + channel.msg(message, senders=self.caller, **kwargs) + + def get_channel_history(self, channel, start_index=0): + """ + View a channel's history. + + Args: + channel (Channel): The channel to access. + message (str): The message to send. + **kwargs: Unused by default. These kwargs will be passed into + all channel messaging hooks for custom overriding. + + """ + caller = self.caller + + log_file = channel.attributes.get( + "log_file", default=channel.log_file.format(channelkey=channel.key)) + + def send_msg(lines): + return caller.msg( + "".join(line.split("[-]", 1)[1] if "[-]" in line else line for line in lines) + ) + # asynchronously tail the log file + tail_log_file(log_file, start_index, 20, callback=send_msg) + + def sub_to_channel(self, channel): + """ + Subscribe to a channel. Note that all permissions should + be checked before this step. + + Args: + channel (Channel): The channel to access. + + Returns: + bool, str: True, None if connection failed. If False, + the second part is an error string. + + """ + caller = self.caller + + if channel.has_connection(caller): + return False, f"Already listening to channel {channel.key}." + result = channel.connect(caller) + return result, "" if result else f"Were not allowed to subscribe to channel {channel.key}" + + def unsub_from_channel(self, channel): + """ + Un-Subscribe to a channel. Note that all permissions should + be checked before this step. + + Args: + channel (Channel): The channel to unsub from. + + Returns: + bool, str: True, None if un-connection succeeded. If False, + the second part is an error string. + + """ + caller = self.caller + + if not channel.has_connection(caller): + return False, f"Not listening to channel {channel.key}." + # clear nicks + chkey = channel.key + for nick in [ + nick + for nick in make_iter(caller.nicks.get(category="channel", return_obj=True)) + if nick and nick.pk and nick.value[3].lower() == chkey + ]: + nick.delete() + result = channel.disconnect(caller) + return result, "" if result else f"Could not unsubscribe from channel {channel.key}" + + def add_alias(self, channel, alias): + """ + Add a new alias for the user to use with this channel. + + Args: + channel (Channel): The channel to alias. + alias (str): The personal alias to use for this channel. + + """ + self.caller.nicks.add(alias, channel.key, category="channel") + + def remove_alias(self, alias): + """ + Remove an alias from a given channel. + + Args: + alias (str, optional): The alias to remove, or `None` for all. + + Returns: + bool, str: True, None if removal succeeded. If False, + the second part is an error string. + + """ + caller = self.caller + channame = caller.nicks.get(key=alias, category="channel") + if channame: + caller.nicks.remove(key=alias, category="channel") + return True, "" + return False, "No such alias was defined." + + def mute_channel(self, channel): + """ + Temporarily mute a channel. + + Args: + channel (Channel): The channel to alias. + + Returns: + bool, str: True, None if muting successful. If False, + the second part is an error string. + """ + if channel.mute(self.caller): + return True, "" + return False, f"Channel {channel.key} was already muted." + + def unmute_channel(self, channel): + """ + Unmute a channel. + + Args: + channel (Channel): The channel to alias. + + Returns: + bool, str: True, None if unmuting successful. If False, + the second part is an error string. + + """ + if channel.unmute(self.caller): + return True, "" + return False, f"Channel {channel.key} was already unmuted." + + def create_channel(self, name, description, typeclass=None, aliases=None): + """ + Create a new channel. Its name must not previously exist + (users can alias as needed). Will also connect to the + new channel. + + Args: + name (str): The new channel name/key. + description (str): This is used in listings. + aliases (list): A list of strings - alternative aliases for the channel + (not to be confused with per-user aliases; these are available for + everyone). + + Returns: + channel, str: new_channel, "" if creation successful. If False, + the second part is an error string. + + """ + caller = self.caller + if typeclass: + pass + + if CHANNEL_DEFAULT_TYPECLASS.objects.channel_search(name, exact=True): + return False, f"Channel {name} already exists." + # set up the new channel + lockstring = "send:all();listen:all();control:id(%s)" % caller.id + new_chan = create.create_channel(name, aliases=aliases,desc=description, locks=lockstring) + new_chan.connect(caller) + return new_chan, "" + + def destroy_channel(self, channel, message=None): + """ + Destroy an existing channel. Access should be checked before + calling this function. + + Args: + channel (Channel): The channel to alias. + message (str, optional): Final message to send onto the channel + before destroying it. If not given, a default message is + used. Set to the empty string for no message. + + if typeclass: + pass + + """ + caller = self.caller + + # set up the new channel + lockstring = "send:all();listen:all();control:id(%s)" % caller.id + + channel_key = channel.key + if message is None: + message = (f"|rChannel {channel_key} is being destroyed. " + "Make sure to clean any channel aliases.|n") + if message: + channel.msg(message, senders=caller, bypass_mute=True) + channel.delete() + logger.log_sec( + "Channel {} was deleted by {}".format(channel_key, caller) + ) + + def set_lock(self, channel, lockstring): + """ + Set a lockstring on a channel. + + Args: + channel (Channel): The channel to operate on. + lockstring (str): A lockstring on the form 'type:lockfunc();...' + + Returns: + bool, str: True, None if setting lock was successful. If False, + the second part is an error string. + + """ + try: + channel.locks.add(lockstring) + except LockException as err: + return False, err + return True, "" + + def set_desc(self, channel, description): + """ + Set a channel description. This is shown in listings etc. + + Args: + caller (Object or Account): The entity performing the action. + channel (Channel): The channel to operate on. + description (str): A short description of the channel. + + Returns: + bool, str: True, None if setting lock was successful. If False, + the second part is an error string. + + """ + channel.db.desc = description + + def boot_user(self, channel, target, quiet=False, reason=""): + """ + Boot a user from a channel, with optional reason. This will + also remove all their aliases for this channel. + + Args: + channel (Channel): The channel to operate on. + target (Object or Account): The entity to boot. + quiet (bool, optional): Whether or not to announce to channel. + reason (str, optional): A reason for the boot. + + Returns: + bool, str: True, None if setting lock was successful. If False, + the second part is an error string. + + """ + if not channel.subscriptions.has(target): + return False, f"{target} is not connected to channel {channel.key}." + # find all of target's nicks linked to this channel and delete them + for nick in [ + nick + for nick in target.nicks.get(category="channel") or [] + if nick.value[3].lower() == channel.key + ]: + nick.delete() + channel.disconnect(target) + reason = f" Reason: {reason}" if reason else "" + target.msg(f"You were booted from channel {channel.key} by {caller.key}.{reason}") + if not quiet: + channel.msg(f"{target.key} was booted from channel by {caller.key}.{reason}") + + logger.log_sec(f"Channel Boot: {target} (Channel: {channel}, " + f"Reason: {reason}, Caller: {caller}") + + def ban_user(self, channel, target, quiet=False, reason=""): + """ + Ban a user from a channel, by locking them out. This will also + boot them, if they are currently connected. + + Args: + channel (Channel): The channel to operate on. + target (Object or Account): The entity to ban + quiet (bool, optional): Whether or not to announce to channel. + reason (str, optional): A reason for the ban + + Returns: + bool, str: True, None if banning was successful. If False, + the second part is an error string. + + """ + boot_user(self.caller, channel, target, quiet=quiet, reason=reason) + if channel.ban(target): + return True, "" + return False, f"{target} is already banned from this channel." + + def unban_user(self, channel, target): + """ + Un-Ban a user from a channel. This will not reconnect them + to the channel, just allow them to connect again (assuming + they have the suitable 'listen' lock like everyone else). + + Args: + channel (Channel): The channel to operate on. + target (Object or Account): The entity to unban + + Returns: + bool, str: True, None if unbanning was successful. If False, + the second part is an error string. + + """ + if channel.unban(target): + return True, "" + return False, f"{target} was not previously banned from this channel." + + def channel_list_who(self, channel): + """ + Show a list of online people is subscribing to a channel. This will check + the 'control' permission of `caller` to determine if only online users + should be returned or everyone. + + Args: + channel (Channel): The channel to operate on. + + Returns: + list: A list of prepared strings, with name + markers for if they are + muted or offline. + + """ + caller = self.caller + mute_list = list(channel.mutelist) + online_list = channel.subscriptions.online() + if channel.access(caller, 'control'): + # for those with channel control, show also offline users + all_subs = list(channel.subscriptions.all()) + else: + # for others, only show online users + all_subs = online_list + + who_list = [] + for subscriber in all_subs: + name = subscriber.get_display_name(caller) + conditions = ("muted" if subscriber in mute_list else "", + "offline" if subscriber not in online_list else "") + cond_text = "(" + ", ".join(conditions) + ")" if conditions else "" + who_list.append(f"{name}{cond_text}") + + return who_list + + def list_channels(self, channelcls=CHANNEL_DEFAULT_TYPECLASS): + """ + Return a available channels. + + Args: + channelcls (Channel, optional): The channel-class to query on. Defaults + to the default channel class from settings. + + Returns: + tuple: A tuple `(subbed_chans, available_chans)` with the channels + currently subscribed to, and those we have 'listen' access to but + don't actually sub to yet. + + """ + caller = self.caller + subscribed_channels = channelcls.objects.get_subscriptions(caller) + unsubscribed_available_channels = [ + chan + for chan in channelcls.objects.get_all_channels() + if chan not in subscribed_channels and chan.access(caller, "listen") + ] + return subscribed_channels, unsubscribed_available_channels + + def display_subbed_channels(self, subscribed): + """ + Display channels subscribed to. + + Args: + subscribed (list): List of subscribed channels + + Returns: + EvTable: Table to display. + + """ + caller = self.caller + + comtable = self.styled_table( + "|wchannel|n", + "|wmy aliases|n", + "|wdescription|n", + align="l", + maxwidth=_DEFAULT_WIDTH + ) + + for chan in subscribed: + clower = chan.key.lower() + nicks = caller.nicks.get(category="channel", return_obj=True) + comtable.add_row( + *("{}{}".format( + chan.key, + "({})".format(",".join(chan.aliases.all())) if chan.aliases.all() else ""), + ",".join(nick.db_key for nick in make_iter(nicks) + if nick and nick.value[3].lower() == clower), + chan.db.desc)) + + def display_all_channels(self, subscribed, available): + """ + Display all available channels + + Args: + subscribed (list): List of subscribed channels + + Returns: + EvTable: Table to display. + + """ + caller = self.caller + + comtable = self.styled_table( + "|wsub|n", + "|wchannel|n", + "|wmy aliases|n", + "|wlocks|n", + "|wdescription|n", + maxwidth=_DEFAULT_WIDTH, + ) + channels = subscribed + available + + for chan in channels: + clower = chan.key.lower() + nicks = caller.nicks.get(category="channel", return_obj=True) + nicks = nicks or [] + if chan not in subscribed: + substatus = "|rNo|n" + elif caller in chan.mutelist: + substatus = "|rMuted|n" + else: + substatus = "|gYes|n" + comtable.add_row( + *(substatus, + "{}{}".format( + chan.key, + "({})".format(",".join(chan.aliases.all())) if chan.aliases.all() else ""), + ",".join(nick.db_key for nick in make_iter(nicks) + if nick.value[3].lower() == clower), + str(chan.locks), + chan.db.desc)) + comtable.reformat_column(0, width=9) + comtable.reformat_column(3, width=14) + + return comtable def func(self): - pass + """ + Main functionality of command. + """ + caller = self.caller + switches = self.switches + channel_names = self.lhslist + + if not channel_names: + if 'all' in switches: + # show all available channels + subscribed, available = self.list_channels() + table = self.display_all_channels(subscribed, available) + + self.msg( + "\n|wAvailable channels|n (use /list to " + f"only show subscriptions)\n{table}") + return + else: + # (empty or /list) show only subscribed channels + subscribed, _ = self.list_channels() + table = self.display_subbed_channels(subscribed) + + self.msg("\n|wChannel subscriptions|n " + f"(use |w/all|n to see all available)\n{table}") + return + + if 'create' in switches: + # create a new channel + config = self.lhs + if not config: + self.msg("To create: channel/create name[;aliases][:typeclass] [= description]") + return + name, *typeclass = config.rsplit(":", 1) + typeclass = typeclass[0] if typeclass else None + name, *aliases = name.rsplit(";") + description = self.rhs or "" + self.create_channel(name, description, typeclass=typeclass, aliases=aliases) + + + channels = [] + for channel_name in channel_names: + # find a channel by fuzzy-matching. This also checks + # 'listen/control' perms. + channel = self.search_channel(channel_name, exact=False) + if not channel: + self.msg(f"No channel found matching '{channel_name}'.") + return + elif len(channel) > 1: + self.msg("Multiple possible channel matches/alias for " + "'{channel_name}':\n" + ", ".join(chan.key for chan in channel)) + return + channels.append(channel) + + # we have at least one channel at this point + channel = channels[0] + + if not switches: + # send a message to channel(s) + message = self.rhs + if not message: + self.msg("To send: channel = ") + return + for chan in channels: + self.msg_channel(chan, message) + return + + if 'history' in switches or 'hist' in switches: + # view channel history + + index = self.rhs or 0 + try: + index = max(0, int(index)) + except ValueError: + self.msg("The history index (describing how many lines to go back) " + "must be an integer >= 0.") + return + self.get_channel_history(channel, start_index=index) + return + + if 'sub' in switches: + # subscribe to a channel + success, err = self.sub_to_channel(channel) + if success: + self.msg("You are now subscribed " + f"to the channel {channel.key}. Use /alias to " + "be able to use different names to refer to the channel.") + else: + self.msg(err) + return + + if 'unsub' in switches: + # un-subscribe from a channel + success, err = self.unsub_from_channel(channel) + if success: + self.msg(f"You un-subscribed from channel {channel.key}. " + "All aliases were cleared.") + else: + self.msg(err) + return + + if 'alias' in switches: + # create a new personal alias for a channel + alias = self.rhs + if not alias: + self.msg("Specify the alias as channel/alias channelname = alias") + return + self.add_alias(channel, alias) + self.msg(f"Added/updated your alias '{alias}' for channel {channel.key}.") + return + + if 'unalias' in switches: + # remove a personal alias for a channel + alias = self.rhs + if not alias: + self.msg("Specify the alias to remove as channel/unalias channelname = alias") + return + success, err = self.remove_alias(channel, alias) + if success: + self.msg(f"Removed your alias '{alias}' for channel {channel.key}") + else: + self.msg(err) + return + + if 'mute' in switches: + # mute a given channel + success, err = self.mute_channel(channel) + if success: + self.msg(f"Muted channel {channel.key}.") + else: + self.msg(err) + return + + if 'unmute' in switches: + # unmute a given channel + success, err = self.unmute_channel(channel) + if success: + self.msg(f"Un-muted channel {channel.key}.") + else: + self.msg(err) + return + + + new_chan, err = self.create_channel() diff --git a/evennia/comms/comms.py b/evennia/comms/comms.py index 5e86f96c77..6f0156753e 100644 --- a/evennia/comms/comms.py +++ b/evennia/comms/comms.py @@ -248,7 +248,7 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase): """ # check access - if not self.access(subscriber, "listen"): + if subscriber in self.banlist or not self.access(subscriber, "listen"): return False # pre-join hook connect = self.pre_join_channel(subscriber)