text: """
  Channel
  
  The channel class represents the out-of-character chat-room usable by
  Accounts in-game. It is mostly overloaded to change its appearance, but
  channels can be used to implement many different forms of message
  distribution systems.
  
  Note that sending data to channels are handled via the CMD_CHANNEL
  syscommand (see evennia.syscmds). The sending should normally not need
  to be modified.
  
  """
  import re
  from evennia import DefaultChannel
  from evennia.utils.ansi import strip_ansi
  from evennia.utils import logger
  
  from server.utils.utils import get_sw, wrap
  
  
  class Channel(DefaultChannel):
      """
      Working methods:
          at_channel_creation() - called once, when the channel is created
          has_connection(account) - check if the given account listens to this channel
          connect(account) - connect account to this channel
          disconnect(account) - disconnect account from channel
          access(access_obj, access_type='listen', default=False) - check the
                      access on this channel (default access_type is listen)
          delete() - delete this channel
          message_transform(msg, emit=False, prefix=True,
                            sender_strings=None, external=False) - called by
                            the comm system and triggers the hooks below
          msg(msgobj, header=None, senders=None, sender_strings=None,
              persistent=None, online=False, emit=False, external=False) - main
                  send method, builds and sends a new message to channel.
          tempmsg(msg, header=None, senders=None) - wrapper for sending non-persistent
                  messages.
          distribute_message(msg, online=False) - send a message to all
                  connected accounts on channel, optionally sending only
                  to accounts that are currently online (optimized for very large sends)
  
      Useful hooks:
          channel_prefix(msg, emit=False) - how the channel should be
                    prefixed when returning to user. Returns a string
          format_senders(senders) - should return how to display multiple
                  senders to a channel
          pose_transform(msg, sender_string) - should detect if the
                  sender is posing, and if so, modify the string
          format_external(msg, senders, emit=False) - format messages sent
                  from outside the game, like from IRC
          format_message(msg, emit=False) - format the message body before
                  displaying it to the user. 'emit' generally means that the
                  message should not be displayed with the sender's name.
  
          pre_join_channel(joiner) - if returning False, abort join
          post_join_channel(joiner) - called right after successful join
          pre_leave_channel(leaver) - if returning False, abort leave
          post_leave_channel(leaver) - called right after successful leave
          pre_send_message(msg) - runs just before a message is sent to channel
          post_send_message(msg) - called just after message was sent to channel
  
      """
  
      def at_channel_creation(self):
          if self.key == "Chat":
              self.db.colorstr = "|M"
          elif self.key == "Question":
              self.db.colorstr = "|G"
          elif self.key == "OOC":
              self.db.colorstr = "|Y"
  
      def channel_prefix(self, msg=None, emit=False):
          """
          How the channel should prefix itself for users. Return a string.
          """
          # use color if defined
          if self.db.colorstr:
              #return " %s%s <%s> " % (self.db.colorstr, msg.senders[0], self.key)
              return " %s<%s> " % (self.db.colorstr, self.key)
          # else default is whether it's private or not
          if self.locks.get("listen").strip() != "listen:all()":
              return " |y<%s> " % self.key
          return " |w<%s> " % self.key
  
      def pose_transform(self, msgobj, sender_string, **kwargs):
          """
          Hook method. Detects if the sender is posing and modifies the message
          accordingly.
  
          Args:
              msgobj (Msg or TempMsg): Message to analyze.
              sender_string (str):     Name of the sender.
              **kwargs (dict):         Optional arguments.
  
          Returns:
              string (str): Message that combines the sender_string component
                            with `msg` in different ways depending on whether
                            an emote was performed or not.
          """
          pose = False
          message = msgobj.message
          message_start = message.lstrip()
          if message_start.startswith((":", ";")):
              pose = True
              message = message[1:]
              if not message.startswith((":", "'", ",")):
                  if not message.startswith(" "):
                      message = " " + message
              
          if pose:
              pre_text = f" <{self.key}> {sender_string} "
              message = sender_string + message
          else:
              pre_text = f" <{self.key}> {sender_string}: "
              message = sender_string + ": " + message
  
          message = strip_ansi(message)
          #message = wrap(message, pre_text=pre_text, omit_pre_text=True)
          return "|n%s" % message
  
      def distribute_message(self, msgobj, online=False, **kwargs):
          """
          Method for grabbing all listeners that a message should be
          sent to on this channel, and sending them a message.
  
          Args:
              msgobj (Msg or TempMsg): Message to distribute.
              online (bool): Only send to receivers who are actually online
                  (not currently used):
              **kwargs (dict): Arbitrary, optional arguments for users
                  overriding the call (unused by default).
  
          Notes:
              This is also where logging happens, if enabled.
          """
          # get all accounts or objects connected to this channel and send to them
          if online:
              subs = self.subscriptions.online()
          else:
              subs = self.subscriptions.all()
          for entity in subs:
              # if the entity is muted, we don't send them a message
              if entity in self.mutelist:
                  continue
              try:
                  # note our addition of the from_channel keyword here. This could be checked
                  # by a custom account.msg() to treat channel-receives differently.
                  message = msgobj.message.split(" ")
  
                  #Separate pre_text to readd during wrap process.
                  pre_text = " ".join(message[0:3]) + " "
                  message = " ".join(message[3:])
  
                  #Grab wrapping dimensions from entity.
                  message = wrap(message, get_sw(entity), get_sw(entity), pre_text)
                  entity.msg(
                      message, from_obj=msgobj.senders, options={"from_channel": self.id}
                  )
              except AttributeError as e:
                  logger.log_trace("%s\nCannot send msg to '%s'." % (e, entity))
  
          if msgobj.keep_log:
              # log to file
              logger.log_file(
                  msgobj.message, self.attributes.get("log_file") or "channel_%s.log" % self.key
              )
  
      def post_send_message(self, msg, **kwargs):
          """
          Hook method. Run after message is sent to a channel.
  
          Args:
              msg (Msg or TempMsg): Message sent.
              **kwargs (dict):      Optional arguments.
          """
  
          for sender in msg.senders:
              sender.msg(prompt="> ")
  
      pass
  