socialization.ccs.wrapped_god_service API documentation

# Module `socialization.ccs.wrapped_god_service`

Expand source code

    from .base_god_service import BaseGodService
    from .wrapped_goddess_db_agent import WrappedGoddessDBAgent
    import rel

    class WrappedGodService(BaseGodService):
        """
            This is the wrapped God service.
            CCS provides a various of features for users in corresponding channels to use.
        """

        def __init__(self, name='WrappedGodService'):
            super().__init__()
            self.agent = WrappedGoddessDBAgent(name+".json")
            self.uri = None
            self.name = 'WrappedGodService'
            self.feature_commands = []

            self.basic_command_func_map = {}
            self.notice_func_map = {}
            self.feature_func_map = {}

            self.prompt = {}

            self.message_func_map = {}

            self.temp_msg_map = {}

        def on_open(self, ws):
            """
                Default behavior "on_open" for websocket.
                You can override this method to customize your own behavior.
                Also applies to "on_message", "on_error" and "on_close".
            """
            data = {'code': self.codes.COPERATION_GOD_RECONNECT, 'extra': {
                'user_id': self.user_id, 'password': self.password}}
            print('SocialGodService send query: run: ', data)
            self._send_ccs_operation(data, ws, None)
            print(self.name, 'opened')

        def add_feature(self, name, code, prompts, func=None):
            """
                Add a feature to the service.
                Every feature contains a name, a code and a prompt.
                The code is always a five-digited int, and the first number must be 9, like 90001.
                The prompt here will be replaced by "params" in the future.
                Now it's the string that shows to the user in the input bar.
                You do not need to specify a handler func to the feature, but it's recommended to do so.
                If one feature with no handler is triggered, an exception will be raised.
                You can use set_feature_handle to set a handler for a feature.
            """
            self.feature_commands.append(
                {'name': name, 'code': code, 'prompts': [prompts] if prompts else []})
            self.prompt[code] = prompts
            if func:
                self.feature_func_map[code] = func

        def remove_feature(self, code):
            """
                Remove a feature from the service.
                The feature is specified by its code.
            """
            self.feature_commands = list(
                filter(lambda x: x['code'] != code, self.feature_commands))
            self.feature_func_map.pop(code, None)

        def set_basic_command_handle(self, code, func):
            """
                Set a handler for a basic command.
                The command is specified by its code.
                Some commands will be like "help", "quit".
            """
            self.basic_command_func_map[code] = func

        def set_notice_handle(self, code, func):
            """
                Set a handler for a notice.
                The notice is specified by its code.
                Some notices will be like "user joined the channel", "user left the channel".
            """
            self.notice_func_map[code] = func

        def set_feature_handle(self, code, func):
            """
                Set a handler for a feature.
                The feature is specified by its code.
            """
            self.feature_func_map[code] = func

        def set_message_handle(self, code, func):
            """
                Set a handler for a message.
                This code is always MESSAGE_TO_CCS.
            """
            self.message_func_map[code] = func

        def _handle_data_dict(self, data, ws):
            print(
                f'WrappedGodService: _handle_data_dict received {data} at websocket {ws}')
            if data['code'] == self.codes.MESSAGE_TO_CCS:
                try:
                    self._handle_message_to_ccs(data, ws, self.path)
                except Exception as e:
                    print('WrappedGodService: _handle_message_to_ccs error: ', e)
            elif data['code'] == self.codes.COMMAND_TO_CCS:
                try:
                    self._handle_command_to_ccs(data, ws, self.path)
                except Exception as e:
                    print('WrappedGodService: _handle_command_to_ccs error: ', e)
            else:
                raise Exception('no handler for code', data['code'])

        def _handle_message_to_ccs(self, data, ws, path):
            type_code = data['extra']['type_code']
            if type_code in self.message_func_map:
                self.message_func_map[type_code](self, data, ws, path)
            else:
                raise Exception('no message function for code', type_code)

        def _handle_command_to_ccs(self, data, ws, path):
            type_code = data['extra']['type_code']
            if 20000 <= type_code <= 29999:
                self._handle_basic_command(data, ws, path)
            elif 80000 <= type_code <= 89999:
                self._handle_notice(data, ws, path)
            elif 90000 <= type_code <= 99999:
                self._handle_feature(data, ws, path)
            else:
                raise Exception('no command function for code', type_code)

        def _handle_basic_command(self, data, ws, path):
            type_code = data['extra']['type_code']
            if type_code in self.basic_command_func_map:
                self.basic_command_func_map[type_code](self, data, ws, path)
            else:
                raise Exception('no basic command function for code', type_code)

        def _handle_notice(self, data, ws, path):
            type_code = data['extra']['type_code']
            if type_code in self.notice_func_map:
                self.notice_func_map[type_code](self, data, ws, path)
            else:
                raise Exception('no notice function for code', type_code)

        def _handle_feature(self, data, ws, path):
            type_code = data['extra']['type_code']
            if type_code in self.feature_func_map:
                self.feature_func_map[type_code](self, data, ws, path)
            else:
                raise Exception('no feature function for code', type_code)

        def _send_ccs_operation(self, data, ws, path):
            print('WrappedGodService ccs_operation: ', data)
            code = data['code']
            password = data['extra']['password']
            user_id = data['extra']['user_id']
            if code == self.codes.COPERATION_GOD_RECONNECT:
                self._send_data_to_ws(
                    ws, self.codes.COPERATION_GOD_RECONNECT, user_id=user_id, password=password)
            else:
                raise Exception('no ccs operation for code', code)

        def _send_command_down(self, ws, type_code, **kwargs):
            self._send_data_to_ws(ws, self.codes.COMMAND_FROM_CCS,
                                  type_code=type_code, **kwargs)

        def _send_message_down(self, ws, type_code, channel_id, to_user_ids, origin, **kwargs):
            self._send_data_to_ws(ws, self.codes.MESSAGE_FROM_CCS,
                                  type_code=type_code, channel_id=channel_id, to_user_ids=to_user_ids,
                                  origin=origin, **kwargs)

        def broadcast_command_text(self, data, ws, text, clear=False):
            """
                Broadcast a text to all users in a channel.
            """
            channel_id = data['extra']['channel_id']
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                channel_id=channel_id,
                to_user_ids=self.agent.get_channel_user_list(channel_id),
                args={'text': text, 'clear': clear}
            )

        def broadcast_command_image(self, data, ws, url):
            """
                Broadcast an image to all users in a channel.
            """
            channel_id = data['extra']['channel_id']
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_IMAGE,
                channel_id=channel_id,
                to_user_ids=self.agent.get_channel_user_list(channel_id),
                args={
                    'type': 'url',
                    'image': url
                }
            )

        def reply_command_text(self, data, ws, text, clear=False):
            """
                Reply a text to a single user.
            """
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                channel_id=data['extra']['channel_id'],
                to_user_ids=[data['extra']['user_id']],
                args={'text': text, 'clear': clear}
            )

        def reply_command_image(self, data, ws, url):
            """
                Reply an image to a single user.
            """
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_IMAGE,
                channel_id=data['extra']['channel_id'],
                to_user_ids=[data['extra']['user_id']],
                args={'type': 'url',  'image': url}
            )

        def whistle_sender_command_text(
            self, data, ws, *,
            text_to_sender, text_to_others,
            clear_sender=False, clear_others=False
        ):
            """
                Dog whistle.
            """
            channel_id = data['extra']['channel_id']
            # to sender
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                channel_id=channel_id,
                to_user_ids=[data['extra']['user_id']],
                args={'text': text_to_sender, 'clear': clear_sender}
            )
            # to others
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                channel_id=channel_id,
                to_user_ids=list(set(self.agent.get_channel_user_list(
                    channel_id)) - {data['extra']['user_id']}),
                args={'text': text_to_others, 'clear': clear_others}
            )

        def set_account(self, user_id, password):
            """
                Set the account for the service.
                Fetch these in CH0000.
            """
            self.user_id = user_id
            self.password = password

        def run(self):
            """
                Run the service.
            """
            uri = 'wss://frog.4fun.chat/social' if self.uri is None else self.uri
            ws = self.create_connection(
                uri, self.on_open, self.on_message, self.on_error, self.on_close)

            rel.signal(2, rel.abort)  # Keyboard Interrupt, one for all connections
            rel.dispatch()

    def handle_fetch_command(self, data, ws, path):
        """
            A function that handles the "fetch command" notice.
            This can be the default behavior.
        """
        channel_id = data['extra']['channel_id']
        user_id = data['extra']['user_id']
        self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CCS_COMMAND_LIST,
                                channel_id=channel_id, to_user_ids=[user_id], commands=self.feature_commands)

    def handle_fetch_user_list(self, data, ws, path):
        """
            A function that handles the "fetch user list" notice.
            This can be the default behavior.
        """
        channel_id = data['extra']['channel_id']
        user_id = data['extra']['user_id']
        self.agent.join_channel(channel_id, user_id)
        user_ids = self.agent.get_channel_user_list(channel_id)
        self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CHANNEL_USER_LIST,
                                channel_id=channel_id, to_user_ids=user_ids, user_ids=user_ids)

    def handle_fetch_recipient_list(self, data, ws, path):
        pass

    def handle_receive_user_list(self, data, ws, path):
        """
            A function that handles the "get user list" notice.
            This can be the default behavior.
        """
        channel_id = data['extra']['channel_id']
        user_ids = data['extra']['user_ids']
        self.agent.init_user_id_list(channel_id, user_ids)

    def handle_join(self, data, ws, path):
        """
            A function that handles the "user join" notice.
            This can be the default behavior.
        """
        channel_id = data['extra']['channel_id']
        user_id = data['extra']['user_id']
        self.agent.join_channel(channel_id, user_id)
        user_ids = self.agent.get_channel_user_list(channel_id)
        self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CHANNEL_USER_LIST,
                                channel_id=channel_id, to_user_ids=user_ids, user_ids=user_ids)
        self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CCS_COMMAND_LIST,
                                channel_id=channel_id, to_user_ids=[user_id], commands=self.feature_commands)

    def handle_left(self, data, ws, path):
        """
            A function that handles the "user leave" notice.
            This can be the default behavior.
        """
        channel_id = data['extra']['channel_id']
        user_id = data['extra']['user_id']
        self.agent.leave_channel(channel_id, user_id)
        user_ids = self.agent.get_channel_user_list(channel_id)
        self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CHANNEL_USER_LIST,
                                channel_id=channel_id, to_user_ids=user_ids, user_ids=user_ids)

    def handle_notice_take_over(self, data, ws, path):
        """
            A function that handles the "take over channel" notice.
            This can be the default behavior.
        """
        print("NOTICE_TAKE_OVER\n", data)
        target_channel_id = data['extra']['target_channel_id']
        # target_channel_name = data['extra']['target_channel_name']
        user_ids = data["extra"]["target_user_ids"]
        self.agent.init_user_id_list(target_channel_id, user_ids)
        self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CHANNEL_USER_LIST,
                                channel_id=target_channel_id, to_user_ids=user_ids, user_ids=user_ids)
        self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CCS_COMMAND_LIST,
                                channel_id=target_channel_id, to_user_ids=user_ids, user_ids=user_ids, commands=self.feature_commands)

    def handle_notice_release(self, data, ws, path):
        """
            A function that handles the "release channel" notice.
            This can be the default behavior.
        """
        # target_channel_id = data['extra']['target_channel_id']
        # target_user_ids = data['extra']['target_user_ids']
        print("NOTICE_RELEASE")

    def handle_message(self, data, ws, path):
        """
            A function that handles message to ccs.
            This can be the default behavior.
            When a message is sent, CCS will receive a response
            //TODO: describe later things, like what response and whistle parts
            def _send_message_down(self, ws, type_code, channel_id, to_user_ids, origin, **kwargs):
        """
        self._send_message_down(ws, data['extra']['type_code'], data['extra']['channel_id'], data['extra']['to_user_ids'],
                                data['extra']['origins'])
        self.temp_msg_map[data['extra']['temp_msg_id']] = data['extra']['to_user_ids']

## Functions

`def handle_fetch_command(self, data, ws, path)`

  A function that handles the "fetch command" notice. This can be the default behavior.

  Expand source code

      def handle_fetch_command(self, data, ws, path):
          """
              A function that handles the "fetch command" notice.
              This can be the default behavior.
          """
          channel_id = data['extra']['channel_id']
          user_id = data['extra']['user_id']
          self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CCS_COMMAND_LIST,
                                  channel_id=channel_id, to_user_ids=[user_id], commands=self.feature_commands)

`def handle_fetch_recipient_list(self, data, ws, path)`
  Expand source code

      def handle_fetch_recipient_list(self, data, ws, path):
          pass

`def handle_fetch_user_list(self, data, ws, path)`

  A function that handles the "fetch user list" notice. This can be the default behavior.

  Expand source code

      def handle_fetch_user_list(self, data, ws, path):
          """
              A function that handles the "fetch user list" notice.
              This can be the default behavior.
          """
          channel_id = data['extra']['channel_id']
          user_id = data['extra']['user_id']
          self.agent.join_channel(channel_id, user_id)
          user_ids = self.agent.get_channel_user_list(channel_id)
          self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CHANNEL_USER_LIST,
                                  channel_id=channel_id, to_user_ids=user_ids, user_ids=user_ids)

`def handle_join(self, data, ws, path)`

  A function that handles the "user join" notice. This can be the default behavior.

  Expand source code

      def handle_join(self, data, ws, path):
          """
              A function that handles the "user join" notice.
              This can be the default behavior.
          """
          channel_id = data['extra']['channel_id']
          user_id = data['extra']['user_id']
          self.agent.join_channel(channel_id, user_id)
          user_ids = self.agent.get_channel_user_list(channel_id)
          self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CHANNEL_USER_LIST,
                                  channel_id=channel_id, to_user_ids=user_ids, user_ids=user_ids)
          self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CCS_COMMAND_LIST,
                                  channel_id=channel_id, to_user_ids=[user_id], commands=self.feature_commands)

`def handle_left(self, data, ws, path)`

  A function that handles the "user leave" notice. This can be the default behavior.

  Expand source code

      def handle_left(self, data, ws, path):
          """
              A function that handles the "user leave" notice.
              This can be the default behavior.
          """
          channel_id = data['extra']['channel_id']
          user_id = data['extra']['user_id']
          self.agent.leave_channel(channel_id, user_id)
          user_ids = self.agent.get_channel_user_list(channel_id)
          self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CHANNEL_USER_LIST,
                                  channel_id=channel_id, to_user_ids=user_ids, user_ids=user_ids)

`def handle_message(self, data, ws, path)`

  A function that handles message to ccs. This can be the default behavior. When a message is sent, CCS will receive a response //TODO: describe later things, like what response and whistle parts def _send_message_down(self, ws, type_code, channel_id, to_user_ids, origin, **kwargs):

  Expand source code

      def handle_message(self, data, ws, path):
          """
              A function that handles message to ccs.
              This can be the default behavior.
              When a message is sent, CCS will receive a response
              //TODO: describe later things, like what response and whistle parts
              def _send_message_down(self, ws, type_code, channel_id, to_user_ids, origin, **kwargs):
          """
          self._send_message_down(ws, data['extra']['type_code'], data['extra']['channel_id'], data['extra']['to_user_ids'],
                                  data['extra']['origins'])
          self.temp_msg_map[data['extra']['temp_msg_id']] = data['extra']['to_user_ids']

`def handle_notice_release(self, data, ws, path)`

  A function that handles the "release channel" notice. This can be the default behavior.

  Expand source code

      def handle_notice_release(self, data, ws, path):
          """
              A function that handles the "release channel" notice.
              This can be the default behavior.
          """
          # target_channel_id = data['extra']['target_channel_id']
          # target_user_ids = data['extra']['target_user_ids']
          print("NOTICE_RELEASE")

`def handle_notice_take_over(self, data, ws, path)`

  A function that handles the "take over channel" notice. This can be the default behavior.

  Expand source code

      def handle_notice_take_over(self, data, ws, path):
          """
              A function that handles the "take over channel" notice.
              This can be the default behavior.
          """
          print("NOTICE_TAKE_OVER\n", data)
          target_channel_id = data['extra']['target_channel_id']
          # target_channel_name = data['extra']['target_channel_name']
          user_ids = data["extra"]["target_user_ids"]
          self.agent.init_user_id_list(target_channel_id, user_ids)
          self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CHANNEL_USER_LIST,
                                  channel_id=target_channel_id, to_user_ids=user_ids, user_ids=user_ids)
          self._send_command_down(ws, self.codes.COMMAND_DOWN_UPDATE_CCS_COMMAND_LIST,
                                  channel_id=target_channel_id, to_user_ids=user_ids, user_ids=user_ids, commands=self.feature_commands)

`def handle_receive_user_list(self, data, ws, path)`

  A function that handles the "get user list" notice. This can be the default behavior.

  Expand source code

      def handle_receive_user_list(self, data, ws, path):
          """
              A function that handles the "get user list" notice.
              This can be the default behavior.
          """
          channel_id = data['extra']['channel_id']
          user_ids = data['extra']['user_ids']
          self.agent.init_user_id_list(channel_id, user_ids)

## Classes

`class WrappedGodService (name='WrappedGodService')`

  This is the wrapped God service. CCS provides a various of features for users in corresponding channels to use.

  Expand source code

      class WrappedGodService(BaseGodService):
          """
              This is the wrapped God service.
              CCS provides a various of features for users in corresponding channels to use.
          """

          def __init__(self, name='WrappedGodService'):
              super().__init__()
              self.agent = WrappedGoddessDBAgent(name+".json")
              self.uri = None
              self.name = 'WrappedGodService'
              self.feature_commands = []

              self.basic_command_func_map = {}
              self.notice_func_map = {}
              self.feature_func_map = {}

              self.prompt = {}

              self.message_func_map = {}

              self.temp_msg_map = {}

          def on_open(self, ws):
              """
                  Default behavior "on_open" for websocket.
                  You can override this method to customize your own behavior.
                  Also applies to "on_message", "on_error" and "on_close".
              """
              data = {'code': self.codes.COPERATION_GOD_RECONNECT, 'extra': {
                  'user_id': self.user_id, 'password': self.password}}
              print('SocialGodService send query: run: ', data)
              self._send_ccs_operation(data, ws, None)
              print(self.name, 'opened')

          def add_feature(self, name, code, prompts, func=None):
              """
                  Add a feature to the service.
                  Every feature contains a name, a code and a prompt.
                  The code is always a five-digited int, and the first number must be 9, like 90001.
                  The prompt here will be replaced by "params" in the future.
                  Now it's the string that shows to the user in the input bar.
                  You do not need to specify a handler func to the feature, but it's recommended to do so.
                  If one feature with no handler is triggered, an exception will be raised.
                  You can use set_feature_handle to set a handler for a feature.
              """
              self.feature_commands.append(
                  {'name': name, 'code': code, 'prompts': [prompts] if prompts else []})
              self.prompt[code] = prompts
              if func:
                  self.feature_func_map[code] = func

          def remove_feature(self, code):
              """
                  Remove a feature from the service.
                  The feature is specified by its code.
              """
              self.feature_commands = list(
                  filter(lambda x: x['code'] != code, self.feature_commands))
              self.feature_func_map.pop(code, None)

          def set_basic_command_handle(self, code, func):
              """
                  Set a handler for a basic command.
                  The command is specified by its code.
                  Some commands will be like "help", "quit".
              """
              self.basic_command_func_map[code] = func

          def set_notice_handle(self, code, func):
              """
                  Set a handler for a notice.
                  The notice is specified by its code.
                  Some notices will be like "user joined the channel", "user left the channel".
              """
              self.notice_func_map[code] = func

          def set_feature_handle(self, code, func):
              """
                  Set a handler for a feature.
                  The feature is specified by its code.
              """
              self.feature_func_map[code] = func

          def set_message_handle(self, code, func):
              """
                  Set a handler for a message.
                  This code is always MESSAGE_TO_CCS.
              """
              self.message_func_map[code] = func

          def _handle_data_dict(self, data, ws):
              print(
                  f'WrappedGodService: _handle_data_dict received {data} at websocket {ws}')
              if data['code'] == self.codes.MESSAGE_TO_CCS:
                  try:
                      self._handle_message_to_ccs(data, ws, self.path)
                  except Exception as e:
                      print('WrappedGodService: _handle_message_to_ccs error: ', e)
              elif data['code'] == self.codes.COMMAND_TO_CCS:
                  try:
                      self._handle_command_to_ccs(data, ws, self.path)
                  except Exception as e:
                      print('WrappedGodService: _handle_command_to_ccs error: ', e)
              else:
                  raise Exception('no handler for code', data['code'])

          def _handle_message_to_ccs(self, data, ws, path):
              type_code = data['extra']['type_code']
              if type_code in self.message_func_map:
                  self.message_func_map[type_code](self, data, ws, path)
              else:
                  raise Exception('no message function for code', type_code)

          def _handle_command_to_ccs(self, data, ws, path):
              type_code = data['extra']['type_code']
              if 20000 <= type_code <= 29999:
                  self._handle_basic_command(data, ws, path)
              elif 80000 <= type_code <= 89999:
                  self._handle_notice(data, ws, path)
              elif 90000 <= type_code <= 99999:
                  self._handle_feature(data, ws, path)
              else:
                  raise Exception('no command function for code', type_code)

          def _handle_basic_command(self, data, ws, path):
              type_code = data['extra']['type_code']
              if type_code in self.basic_command_func_map:
                  self.basic_command_func_map[type_code](self, data, ws, path)
              else:
                  raise Exception('no basic command function for code', type_code)

          def _handle_notice(self, data, ws, path):
              type_code = data['extra']['type_code']
              if type_code in self.notice_func_map:
                  self.notice_func_map[type_code](self, data, ws, path)
              else:
                  raise Exception('no notice function for code', type_code)

          def _handle_feature(self, data, ws, path):
              type_code = data['extra']['type_code']
              if type_code in self.feature_func_map:
                  self.feature_func_map[type_code](self, data, ws, path)
              else:
                  raise Exception('no feature function for code', type_code)

          def _send_ccs_operation(self, data, ws, path):
              print('WrappedGodService ccs_operation: ', data)
              code = data['code']
              password = data['extra']['password']
              user_id = data['extra']['user_id']
              if code == self.codes.COPERATION_GOD_RECONNECT:
                  self._send_data_to_ws(
                      ws, self.codes.COPERATION_GOD_RECONNECT, user_id=user_id, password=password)
              else:
                  raise Exception('no ccs operation for code', code)

          def _send_command_down(self, ws, type_code, **kwargs):
              self._send_data_to_ws(ws, self.codes.COMMAND_FROM_CCS,
                                    type_code=type_code, **kwargs)

          def _send_message_down(self, ws, type_code, channel_id, to_user_ids, origin, **kwargs):
              self._send_data_to_ws(ws, self.codes.MESSAGE_FROM_CCS,
                                    type_code=type_code, channel_id=channel_id, to_user_ids=to_user_ids,
                                    origin=origin, **kwargs)

          def broadcast_command_text(self, data, ws, text, clear=False):
              """
                  Broadcast a text to all users in a channel.
              """
              channel_id = data['extra']['channel_id']
              self._send_command_down(
                  ws,
                  self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                  channel_id=channel_id,
                  to_user_ids=self.agent.get_channel_user_list(channel_id),
                  args={'text': text, 'clear': clear}
              )

          def broadcast_command_image(self, data, ws, url):
              """
                  Broadcast an image to all users in a channel.
              """
              channel_id = data['extra']['channel_id']
              self._send_command_down(
                  ws,
                  self.codes.COMMAND_DOWN_DISPLAY_IMAGE,
                  channel_id=channel_id,
                  to_user_ids=self.agent.get_channel_user_list(channel_id),
                  args={
                      'type': 'url',
                      'image': url
                  }
              )

          def reply_command_text(self, data, ws, text, clear=False):
              """
                  Reply a text to a single user.
              """
              self._send_command_down(
                  ws,
                  self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                  channel_id=data['extra']['channel_id'],
                  to_user_ids=[data['extra']['user_id']],
                  args={'text': text, 'clear': clear}
              )

          def reply_command_image(self, data, ws, url):
              """
                  Reply an image to a single user.
              """
              self._send_command_down(
                  ws,
                  self.codes.COMMAND_DOWN_DISPLAY_IMAGE,
                  channel_id=data['extra']['channel_id'],
                  to_user_ids=[data['extra']['user_id']],
                  args={'type': 'url',  'image': url}
              )

          def whistle_sender_command_text(
              self, data, ws, *,
              text_to_sender, text_to_others,
              clear_sender=False, clear_others=False
          ):
              """
                  Dog whistle.
              """
              channel_id = data['extra']['channel_id']
              # to sender
              self._send_command_down(
                  ws,
                  self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                  channel_id=channel_id,
                  to_user_ids=[data['extra']['user_id']],
                  args={'text': text_to_sender, 'clear': clear_sender}
              )
              # to others
              self._send_command_down(
                  ws,
                  self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                  channel_id=channel_id,
                  to_user_ids=list(set(self.agent.get_channel_user_list(
                      channel_id)) - {data['extra']['user_id']}),
                  args={'text': text_to_others, 'clear': clear_others}
              )

          def set_account(self, user_id, password):
              """
                  Set the account for the service.
                  Fetch these in CH0000.
              """
              self.user_id = user_id
              self.password = password

          def run(self):
              """
                  Run the service.
              """
              uri = 'wss://frog.4fun.chat/social' if self.uri is None else self.uri
              ws = self.create_connection(
                  uri, self.on_open, self.on_message, self.on_error, self.on_close)

              rel.signal(2, rel.abort)  # Keyboard Interrupt, one for all connections
              rel.dispatch()

  ### Ancestors

  - BaseGodService
  - JSONWebsocketActiveService

  ### Methods

  `def add_feature(self, name, code, prompts, func=None)`

    Add a feature to the service. Every feature contains a name, a code and a prompt. The code is always a five-digited int, and the first number must be 9, like 90001. The prompt here will be replaced by "params" in the future. Now it's the string that shows to the user in the input bar. You do not need to specify a handler func to the feature, but it's recommended to do so. If one feature with no handler is triggered, an exception will be raised. You can use set_feature_handle to set a handler for a feature.

    Expand source code

        def add_feature(self, name, code, prompts, func=None):
            """
                Add a feature to the service.
                Every feature contains a name, a code and a prompt.
                The code is always a five-digited int, and the first number must be 9, like 90001.
                The prompt here will be replaced by "params" in the future.
                Now it's the string that shows to the user in the input bar.
                You do not need to specify a handler func to the feature, but it's recommended to do so.
                If one feature with no handler is triggered, an exception will be raised.
                You can use set_feature_handle to set a handler for a feature.
            """
            self.feature_commands.append(
                {'name': name, 'code': code, 'prompts': [prompts] if prompts else []})
            self.prompt[code] = prompts
            if func:
                self.feature_func_map[code] = func

  `def broadcast_command_image(self, data, ws, url)`

    Broadcast an image to all users in a channel.

    Expand source code

        def broadcast_command_image(self, data, ws, url):
            """
                Broadcast an image to all users in a channel.
            """
            channel_id = data['extra']['channel_id']
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_IMAGE,
                channel_id=channel_id,
                to_user_ids=self.agent.get_channel_user_list(channel_id),
                args={
                    'type': 'url',
                    'image': url
                }
            )

  `def broadcast_command_text(self, data, ws, text, clear=False)`

    Broadcast a text to all users in a channel.

    Expand source code

        def broadcast_command_text(self, data, ws, text, clear=False):
            """
                Broadcast a text to all users in a channel.
            """
            channel_id = data['extra']['channel_id']
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                channel_id=channel_id,
                to_user_ids=self.agent.get_channel_user_list(channel_id),
                args={'text': text, 'clear': clear}
            )

  `def on_open(self, ws)`

    Default behavior "on_open" for websocket. You can override this method to customize your own behavior. Also applies to "on_message", "on_error" and "on_close".

    Expand source code

        def on_open(self, ws):
            """
                Default behavior "on_open" for websocket.
                You can override this method to customize your own behavior.
                Also applies to "on_message", "on_error" and "on_close".
            """
            data = {'code': self.codes.COPERATION_GOD_RECONNECT, 'extra': {
                'user_id': self.user_id, 'password': self.password}}
            print('SocialGodService send query: run: ', data)
            self._send_ccs_operation(data, ws, None)
            print(self.name, 'opened')

  `def remove_feature(self, code)`

    Remove a feature from the service. The feature is specified by its code.

    Expand source code

        def remove_feature(self, code):
            """
                Remove a feature from the service.
                The feature is specified by its code.
            """
            self.feature_commands = list(
                filter(lambda x: x['code'] != code, self.feature_commands))
            self.feature_func_map.pop(code, None)

  `def reply_command_image(self, data, ws, url)`

    Reply an image to a single user.

    Expand source code

        def reply_command_image(self, data, ws, url):
            """
                Reply an image to a single user.
            """
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_IMAGE,
                channel_id=data['extra']['channel_id'],
                to_user_ids=[data['extra']['user_id']],
                args={'type': 'url',  'image': url}
            )

  `def reply_command_text(self, data, ws, text, clear=False)`

    Reply a text to a single user.

    Expand source code

        def reply_command_text(self, data, ws, text, clear=False):
            """
                Reply a text to a single user.
            """
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                channel_id=data['extra']['channel_id'],
                to_user_ids=[data['extra']['user_id']],
                args={'text': text, 'clear': clear}
            )

  `def run(self)`

    Run the service.

    Expand source code

        def run(self):
            """
                Run the service.
            """
            uri = 'wss://frog.4fun.chat/social' if self.uri is None else self.uri
            ws = self.create_connection(
                uri, self.on_open, self.on_message, self.on_error, self.on_close)

            rel.signal(2, rel.abort)  # Keyboard Interrupt, one for all connections
            rel.dispatch()

  `def set_account(self, user_id, password)`

    Set the account for the service. Fetch these in CH0000.

    Expand source code

        def set_account(self, user_id, password):
            """
                Set the account for the service.
                Fetch these in CH0000.
            """
            self.user_id = user_id
            self.password = password

  `def set_basic_command_handle(self, code, func)`

    Set a handler for a basic command. The command is specified by its code. Some commands will be like "help", "quit".

    Expand source code

        def set_basic_command_handle(self, code, func):
            """
                Set a handler for a basic command.
                The command is specified by its code.
                Some commands will be like "help", "quit".
            """
            self.basic_command_func_map[code] = func

  `def set_feature_handle(self, code, func)`

    Set a handler for a feature. The feature is specified by its code.

    Expand source code

        def set_feature_handle(self, code, func):
            """
                Set a handler for a feature.
                The feature is specified by its code.
            """
            self.feature_func_map[code] = func

  `def set_message_handle(self, code, func)`

    Set a handler for a message. This code is always MESSAGE_TO_CCS.

    Expand source code

        def set_message_handle(self, code, func):
            """
                Set a handler for a message.
                This code is always MESSAGE_TO_CCS.
            """
            self.message_func_map[code] = func

  `def set_notice_handle(self, code, func)`

    Set a handler for a notice. The notice is specified by its code. Some notices will be like "user joined the channel", "user left the channel".

    Expand source code

        def set_notice_handle(self, code, func):
            """
                Set a handler for a notice.
                The notice is specified by its code.
                Some notices will be like "user joined the channel", "user left the channel".
            """
            self.notice_func_map[code] = func

  `def whistle_sender_command_text(self, data, ws, *, text_to_sender, text_to_others, clear_sender=False, clear_others=False)`

    Dog whistle.

    Expand source code

        def whistle_sender_command_text(
            self, data, ws, *,
            text_to_sender, text_to_others,
            clear_sender=False, clear_others=False
        ):
            """
                Dog whistle.
            """
            channel_id = data['extra']['channel_id']
            # to sender
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                channel_id=channel_id,
                to_user_ids=[data['extra']['user_id']],
                args={'text': text_to_sender, 'clear': clear_sender}
            )
            # to others
            self._send_command_down(
                ws,
                self.codes.COMMAND_DOWN_DISPLAY_TEXT,
                channel_id=channel_id,
                to_user_ids=list(set(self.agent.get_channel_user_list(
                    channel_id)) - {data['extra']['user_id']}),
                args={'text': text_to_others, 'clear': clear_others}
            )