예제 #1
0
 def test_run_set_off(self):
     # Set up a test hallo and server and channel and user
     hallo1 = Hallo()
     perm0 = PermissionMask()
     hallo1.permission_mask = perm0
     serv1 = ServerMock(hallo1)
     serv1.name = "test_serv1"
     perm1 = PermissionMask()
     serv1.permission_mask = perm1
     hallo1.add_server(serv1)
     chan1 = serv1.get_channel_by_address("test_chan1".lower(), "test_chan1")
     perm2 = PermissionMask()
     chan1.permission_mask = perm2
     user1 = serv1.get_user_by_address("test_user1", "test_user1")
     perm3 = PermissionMask()
     user1.permission_mask = perm3
     # Get permission mask of given channel
     test_right = "test_right"
     perm2.set_right(test_right, True)
     self.function_dispatcher.dispatch(EventMessage(serv1, chan1, user1,
                                                    "permissions server=test_serv1 channel=test_chan1 " +
                                                    test_right+" off"))
     data = serv1.get_send_data(1, chan1, EventMessage)
     assert "error" not in data[0].text.lower()
     assert "set "+test_right+" to false" in data[0].text.lower()
     assert test_right in perm2.rights_map
     assert not perm2.rights_map[test_right]
예제 #2
0
 def test_run_set_on(self):
     # Set up a test hallo and server and channel and user
     hallo1 = Hallo()
     perm0 = PermissionMask()
     hallo1.permission_mask = perm0
     serv1 = ServerMock(hallo1)
     serv1.name = "test_serv1"
     perm1 = PermissionMask()
     serv1.permission_mask = perm1
     hallo1.add_server(serv1)
     chan1 = serv1.get_channel_by_name("test_chan1")
     perm2 = PermissionMask()
     chan1.permission_mask = perm2
     user1 = serv1.get_user_by_name("test_user1")
     perm3 = PermissionMask()
     user1.permission_mask = perm3
     # Get permission mask of given channel
     test_right = "test_right"
     perm2.set_right(test_right, False)
     self.function_dispatcher.dispatch("permissions server=test_serv1 channel=test_chan1 "+test_right+" on",
                                       user1, chan1)
     data = serv1.get_send_data(1, chan1, Server.MSG_MSG)
     assert "error" not in data[0][0].lower()
     assert "set "+test_right+" to true" in data[0][0].lower()
     assert test_right in perm2.rights_map
     assert perm2.rights_map[test_right]
예제 #3
0
 def test_run_fail_not_bool(self):
     # Set up a test hallo and server and channel and user
     hallo1 = Hallo()
     perm0 = PermissionMask()
     hallo1.permission_mask = perm0
     serv1 = ServerMock(hallo1)
     serv1.name = "test_serv1"
     perm1 = PermissionMask()
     serv1.permission_mask = perm1
     hallo1.add_server(serv1)
     chan1 = serv1.get_channel_by_address("test_chan1".lower(), "test_chan1")
     perm2 = PermissionMask()
     chan1.permission_mask = perm2
     user1 = serv1.get_user_by_address("test_user1", "test_user1")
     perm3 = PermissionMask()
     user1.permission_mask = perm3
     # Get permission mask of given channel
     test_right = "test_right"
     perm1.set_right(test_right, True)
     self.function_dispatcher.dispatch(EventMessage(serv1, chan1, user1,
                                                    "permissions server=test_serv1 "+test_right+" yellow"))
     data = serv1.get_send_data(1, chan1, EventMessage)
     assert "error" in data[0].text.lower()
     assert "don't understand your boolean value" in data[0].text.lower()
     assert test_right in perm1.rights_map
     assert perm1.rights_map[test_right]
예제 #4
0
 def load_from_xml():
     try:
         doc = ElementTree.parse("config/config.xml")
     except (OSError, IOError):
         print("No current config, loading from default.")
         doc = ElementTree.parse("config/config-default.xml")
     new_hallo = Hallo()
     root = doc.getroot()
     new_hallo.default_nick = root.findtext("default_nick")
     new_hallo.default_prefix = Commons.string_from_file(root.findtext("default_prefix"))
     new_hallo.default_full_name = root.findtext("default_full_name")
     new_hallo.function_dispatcher = FunctionDispatcher.from_xml(
         ElementTree.tostring(root.find("function_dispatcher")), new_hallo)
     user_group_list_xml = root.find("user_group_list")
     for user_group_xml in user_group_list_xml.findall("user_group"):
         user_group_obj = UserGroup.from_xml(ElementTree.tostring(user_group_xml), new_hallo)
         new_hallo.add_user_group(user_group_obj)
     server_list_xml = root.find("server_list")
     for server_xml in server_list_xml.findall("server"):
         server_obj = new_hallo.server_factory.new_server_from_xml(ElementTree.tostring(server_xml))
         new_hallo.add_server(server_obj)
     if root.find("permission_mask") is not None:
         new_hallo.permission_mask = PermissionMask.from_xml(ElementTree.tostring(root.find("permission_mask")))
     api_key_list_xml = root.find("api_key_list")
     for api_key_xml in api_key_list_xml.findall("api_key"):
         api_key_name = api_key_xml.findtext("name")
         api_key_key = api_key_xml.findtext("key")
         new_hallo.add_api_key(api_key_name, api_key_key)
     return new_hallo
예제 #5
0
 def __init__(self):
     self.default_nick = "Hallo"
     """:type : str"""
     self.default_prefix = False
     """:type : bool | str"""
     self.default_full_name = "HalloBot HalloHost HalloServer :an irc bot by spangle"
     """:type : str"""
     self.open = False
     """:type : bool"""
     self.user_group_list = set()
     """:type : set[UserGroup]"""
     self.server_list = set()
     """:type : set[Server.Server]"""
     self.logger = Logger(self)
     """:type : Logger"""
     self.printer = Printer(self)
     """:type : Printer"""
     self.api_key_list = {}
     """:type : dict[str,str]"""
     # Create ServerFactory
     self.server_factory = ServerFactory(self)
     """:type : ServerFactory"""
     self.permission_mask = PermissionMask()
     """:type : PermissionMask"""
     # TODO: manual FunctionDispatcher construction, user input?
     self.function_dispatcher = None
     """:type : Optional[FunctionDispatcher]"""
예제 #6
0
 def from_xml(xml_string, hallo):
     """
     Loads a new UserGroup object from XML
     :param xml_string: String containing XML to parse for usergroup
     :type xml_string: str
     :param hallo: Hallo object to add user group to
     :type hallo: Hallo.Hallo
     """
     doc = minidom.parseString(xml_string)
     new_name = doc.getElementsByTagName("name")[0].firstChild.data
     new_user_group = UserGroup(new_name, hallo)
     if len(doc.getElementsByTagName("permission_mask")) != 0:
         new_user_group.permission_mask = PermissionMask.from_xml(
             doc.getElementsByTagName("permission_mask")[0].toxml())
     return new_user_group
예제 #7
0
 def from_json(json_obj, hallo):
     api_key = json_obj["api_key"]
     new_server = ServerTelegram(hallo, api_key)
     new_server.name = json_obj["name"]
     new_server.auto_connect = json_obj["auto_connect"]
     if "nick" in json_obj:
         new_server.nick = json_obj["nick"]
     if "prefix" in json_obj:
         new_server.prefix = json_obj["prefix"]
     if "permission_mask" in json_obj:
         new_server.permission_mask = PermissionMask.from_json(json_obj["permission_mask"])
     for channel in json_obj["channels"]:
         new_server.add_channel(Channel.from_json(channel, new_server))
     for user in json_obj["users"]:
         new_server.add_user(User.from_json(user, new_server))
     return new_server
예제 #8
0
 def __init__(self, name, hallo):
     """
     Constructor
     :param name: Name of the user group
     :type name: str
     :param hallo: Hallo object which owns the user group
     :type hallo: Hallo.Hallo
     """
     self.user_list = set()  # Dynamic userlist of this group
     """:type : set[Destination.User]"""
     self.hallo = hallo  # Hallo instance that owns this UserGroup
     """:type : Hallo.Hallo"""
     self.name = name  # Name of the UserGroup
     """:type : str"""
     self.permission_mask = PermissionMask()  # PermissionMask for the UserGroup
     """:type : PermissionMask"""
예제 #9
0
파일: Server.py 프로젝트: joshcoales/Hallo
 def __init__(self, hallo):
     """
     Constructor for server object
     :param hallo: Hallo Instance of hallo that contains this server object
     :type hallo: Hallo.Hallo
     """
     self.hallo = hallo  # The hallo object that created this server
     # Persistent/saved class variables
     self.name = None  # Server name
     self.auto_connect = True  # Whether to automatically connect to this server when hallo starts
     self.channel_list = []  # List of channels on this server (which may or may not be currently active)
     """ :type : list[Destination.Channel]"""
     self.user_list = []  # Users on this server (not all of which are online)
     """ :type : list[Destination.User]"""
     self.nick = None  # Nickname to use on this server
     self.prefix = None  # Prefix to use with functions on this server
     self.full_name = None  # Full name to use on this server
     self.permission_mask = PermissionMask()  # PermissionMask for the server
     """ :type : PermissionMask"""
     # Dynamic/unsaved class variables
     self.state = Server.STATE_CLOSED  # Current state of the server, replacing open
예제 #10
0
 def from_xml(xml_string, server):
     """
     Loads a new Channel object from XML
     :param xml_string: XML string representation of the channel
     :type xml_string: str
     :param server: Server the channel is on
     :type server: Server.Server
     """
     doc = minidom.parseString(xml_string)
     new_name = doc.getElementsByTagName("channel_name")[0].firstChild.data
     channel = Channel(new_name, server)
     channel.logging = Commons.string_from_file(doc.getElementsByTagName("logging")[0].firstChild.data)
     channel.use_caps_lock = Commons.string_from_file(doc.getElementsByTagName("caps_lock")[0].firstChild.data)
     if len(doc.getElementsByTagName("password")) != 0:
         channel.password = doc.getElementsByTagName("password")[0].firstChild.data
     channel.passive_enabled = Commons.string_from_file(
         doc.getElementsByTagName("passive_enabled")[0].firstChild.data)
     channel.auto_join = Commons.string_from_file(doc.getElementsByTagName("auto_join")[0].firstChild.data)
     if len(doc.getElementsByTagName("permission_mask")) != 0:
         channel.permission_mask = PermissionMask.from_xml(doc.getElementsByTagName("permission_mask")[0].toxml())
     return channel
예제 #11
0
 def __init__(self, hallo, api_key):
     super().__init__(hallo)
     """
     Constructor for server object
     :param hallo: Hallo Instance of hallo that contains this server object
     :type hallo: Hallo.Hallo
     """
     self.hallo = hallo  # The hallo object that created this server
     # Persistent/saved class variables
     self.api_key = api_key
     self.name = "Telegram"  # Server name #TODO: needs to be configurable!
     self.auto_connect = True  # Whether to automatically connect to this server when hallo starts
     self.channel_list = []  # List of channels on this server (which may or may not be currently active)
     """ :type : list[Destination.Channel]"""
     self.user_list = []  # Users on this server (not all of which are online)
     """ :type : list[Destination.User]"""
     self.nick = None  # Nickname to use on this server
     self.prefix = None  # Prefix to use with functions on this server
     self.full_name = None  # Full name to use on this server
     self.permission_mask = PermissionMask()  # PermissionMask for the server
     # Dynamic/unsaved class variables
     self.state = Server.STATE_CLOSED  # Current state of the server, replacing open
     self._connect_lock = Lock()
     request = Request(con_pool_size=8)
     self.bot = telegram.Bot(token=self.api_key, request=request)
     self.bot.logger.setLevel(logging.INFO)
     self.updater = Updater(bot=self.bot)
     self.dispatcher = self.updater.dispatcher
     logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.ERROR)
     # Message handlers
     self.private_msg_handler = MessageHandler(Filters.private, self.parse_private_message)
     self.dispatcher.add_handler(self.private_msg_handler)
     self.group_msg_handler = MessageHandler(Filters.group, self.parse_group_message)
     self.dispatcher.add_handler(self.group_msg_handler)
     # Catch-all message handler for anything not already handled.
     self.core_msg_handler = MessageHandler(Filters.all, self.parse_unhandled, channel_post_updates=True)
     self.dispatcher.add_handler(self.core_msg_handler)
예제 #12
0
 def from_xml(xml_string, server):
     """
     Loads a new User object from XML
     :param xml_string: XML string representation of the user to create
     :type xml_string: str
     :param server: Server which the user is on
     :type server: Server.Server
     """
     doc = minidom.parseString(xml_string)
     new_name = doc.getElementsByTagName("user_name")[0].firstChild.data
     new_user = User(new_name, server)
     new_user.logging = Commons.string_from_file(doc.getElementsByTagName("logging")[0].firstChild.data)
     new_user.use_caps_lock = Commons.string_from_file(doc.getElementsByTagName("caps_lock")[0].firstChild.data)
     # Load UserGroups from XML
     user_group_list_elem = doc.getElementsByTagName("user_group_membership")[0]
     for user_group_elem in user_group_list_elem.getElementsByTagName("user_group_name"):
         user_group_name = user_group_elem.firstChild.data
         user_group = server.hallo.get_user_group_by_name(user_group_name)
         if user_group is not None:
             new_user.add_user_group(user_group)
     # Add PermissionMask, if one exists
     if len(doc.getElementsByTagName("permission_mask")) != 0:
         new_user.permission_mask = PermissionMask.from_xml(doc.getElementsByTagName("permission_mask")[0].toxml())
     return new_user
예제 #13
0
파일: Server.py 프로젝트: joshcoales/Hallo
class Server(metaclass=ABCMeta):
    """
    Generic server object. An interface for ServerIRC or ServerSkype or whatever objects.
    """
    # Constants
    TYPE_IRC = "irc"
    TYPE_MOCK = "mock"
    TYPE_TELEGRAM = "telegram"
    STATE_CLOSED = "disconnected"
    STATE_OPEN = "connected"
    STATE_CONNECTING = "connecting"
    STATE_DISCONNECTING = "disconnecting"

    type = None

    def __init__(self, hallo):
        """
        Constructor for server object
        :param hallo: Hallo Instance of hallo that contains this server object
        :type hallo: Hallo.Hallo
        """
        self.hallo = hallo  # The hallo object that created this server
        # Persistent/saved class variables
        self.name = None  # Server name
        self.auto_connect = True  # Whether to automatically connect to this server when hallo starts
        self.channel_list = []  # List of channels on this server (which may or may not be currently active)
        """ :type : list[Destination.Channel]"""
        self.user_list = []  # Users on this server (not all of which are online)
        """ :type : list[Destination.User]"""
        self.nick = None  # Nickname to use on this server
        self.prefix = None  # Prefix to use with functions on this server
        self.full_name = None  # Full name to use on this server
        self.permission_mask = PermissionMask()  # PermissionMask for the server
        """ :type : PermissionMask"""
        # Dynamic/unsaved class variables
        self.state = Server.STATE_CLOSED  # Current state of the server, replacing open

    def __eq__(self, other):
        return isinstance(other, Server) and self.hallo == other.hallo and self.type == other.type and \
               self.name.lower() == other.name.lower()

    def __hash__(self):
        return hash((self.hallo, self.type, self.name.lower()))

    def start(self):
        """
        Starts the new server, launching new thread as appropriate.
        """
        raise NotImplementedError

    def disconnect(self, force=False):
        """
        Disconnects from the server, shutting down remaining threads
        """
        raise NotImplementedError

    def reconnect(self):
        """
        Disconnects and reconnects from the server
        """
        self.disconnect()
        self.start()

    def send(self, event):
        """
        Sends a message to the server, or a specific channel in the server
        :param event: Event to send, should be outbound.
        :type event: Events.ServerEvent
        :rtype : Events.ServerEvent | None
        """
        raise NotImplementedError

    def reply(self, old_event, new_event):
        """
        Sends a message as a reply to another message, such as a response to a function call
        :param old_event: The event which was received, to reply to
        :type old_event: Events.ChannelUserTextEvent
        :param new_event: The event to be sent
        :type new_event: Events.ChannelUserTextEvent
        """
        # This method will just do some checks, implementations will have to actually send events
        if not old_event.is_inbound or new_event.is_inbound:
            raise ServerException("Cannot reply to outbound event, or send inbound one")
        if old_event.channel != new_event.channel:
            raise ServerException("Cannot send reply to a different channel than original message came from")
        if new_event.user is not None and old_event.user != new_event.user:
            raise ServerException("Cannot send reply to a different private chat than original message came from")
        if old_event.server != new_event.server:
            raise ServerException("Cannot send reply to a different server than the original message came from")
        return

    def to_json(self):
        """
        Returns a dict formatted so it may be serialised into json configuration data
        :return: dict
        """
        raise NotImplementedError

    def get_nick(self):
        """Nick getter"""
        if self.nick is None:
            return self.hallo.default_nick
        return self.nick

    def set_nick(self, nick):
        """
        Nick setter
        :param nick: New nick for hallo to use on this server
        :type nick: str
        """
        self.nick = nick

    def get_prefix(self):
        """Prefix getter"""
        if self.prefix is None:
            return self.hallo.default_prefix
        return self.prefix

    def set_prefix(self, prefix):
        """
        Prefix setter
        :param prefix: Prefix for hallo to use for function calls on this server
        :type prefix: str | bool | None
        """
        self.prefix = prefix

    def get_full_name(self):
        """Full name getter"""
        if self.full_name is None:
            return self.hallo.default_full_name
        return self.full_name

    def set_full_name(self, full_name):
        """
        Full name setter
        :param full_name: Full name for Hallo to use on this server
        :type full_name: str
        """
        self.full_name = full_name

    def get_auto_connect(self):
        """AutoConnect getter"""
        return self.auto_connect

    def set_auto_connect(self, auto_connect):
        """
        AutoConnect setter
        :param auto_connect: Whether or not to autoconnect to the server
        :type auto_connect: bool
        """
        self.auto_connect = auto_connect

    def is_connected(self):
        """Returns boolean representing whether the server is connected or not."""
        return self.state == Server.STATE_OPEN

    def get_channel_by_name(self, channel_name):
        """
        Returns a Channel object with the specified channel name.
        :param channel_name: Name of the channel which is being searched for
        :type channel_name: str
        :rtype: Optional[Destination.Channel]
        """
        channel_name = channel_name.lower()
        for channel in self.channel_list:
            if channel.name == channel_name:
                return channel
        return None

    def get_channel_by_address(self, address, channel_name=None):
        """
        Returns a Channel object with the specified channel name.
        :param address: Address of the channel
        :type address: str
        :param channel_name: Name of the channel which is being searched for
        :type channel_name: str
        :rtype: Destination.Channel
        """
        for channel in self.channel_list:
            if channel.address == address:
                return channel
        if channel_name is None:
            channel_name = self.get_name_by_address(address)
        new_channel = Channel(self, address, channel_name)
        self.add_channel(new_channel)
        return new_channel

    def get_name_by_address(self, address):
        """
        Returns the name of a destination, based on the address
        :param address: str
        :return: str
        """
        raise NotImplementedError()

    def add_channel(self, channel_obj):
        """
        Adds a channel to the channel list
        :param channel_obj: Adds a channel to the list, without joining it
        :type channel_obj: Destination.Channel
        """
        self.channel_list.append(channel_obj)

    def join_channel(self, channel_obj):
        """
        Joins a specified channel
        :param channel_obj: Channel to join
        :type channel_obj: Destination.Channel
        """
        raise NotImplementedError

    def leave_channel(self, channel_obj):
        """
        Leaves a specified channel
        :param channel_obj: Channel for hallo to leave
        :type channel_obj: Destination.Channel
        """
        # If channel isn't in channel list, do nothing
        if channel_obj not in self.channel_list:
            return
        # Set channel to not AutoJoin, for the future
        channel_obj.auto_join = False
        # Set not in channel
        channel_obj.set_in_channel(False)

    def get_user_by_name(self, user_name):
        """
        Returns a User object with the specified user name.
        :param user_name: Name of user which is being searched for
        :type user_name: str
        :rtype: Destination.User | None
        """
        user_name = user_name.lower()
        for user in self.user_list:
            if user.name == user_name:
                return user
        # No user by that name exists, return None
        return None

    def get_user_by_address(self, address, user_name=None):
        """
        Returns a User object with the specified user name.
        :param address: address of the user which is being searched for or added
        :type address: str
        :param user_name: Name of user which is being searched for
        :type user_name: str
        :return: Destination.User | None
        """
        for user in self.user_list:
            if user.address == address:
                return user
        if user_name is None:
            user_name = self.get_name_by_address(address)
        # No user by that name exists, so create one
        new_user = User(self, address, user_name)
        self.add_user(new_user)
        return new_user

    def get_user_list(self):
        """Returns the full list of users on this server."""
        return self.user_list

    def add_user(self, user_obj):
        """
        Adds a user to the user list
        :param user_obj: User to add to user list
        :type user_obj: Destination.User
        """
        self.user_list.append(user_obj)

    def rights_check(self, right_name):
        """
        Checks the value of the right with the specified name. Returns boolean
        :param right_name: Name of the right to check default server value for
        :type right_name: str
        """
        if self.permission_mask is not None:
            right_value = self.permission_mask.get_right(right_name)
            # If PermissionMask contains that right, return it.
            if right_value in [True, False]:
                return right_value
        # Fallback to the parent Hallo's decision.
        return self.hallo.rights_check(right_name)

    def check_user_identity(self, user_obj):
        """
        Check if a user is identified and verified
        :param user_obj: User to check identity of
        :type user_obj: Destination.User
        """
        raise NotImplementedError
예제 #14
0
class ServerTelegram(Server):

    type = Server.TYPE_TELEGRAM
    image_extensions = ["jpg", "jpeg", "png"]

    def __init__(self, hallo, api_key):
        super().__init__(hallo)
        """
        Constructor for server object
        :param hallo: Hallo Instance of hallo that contains this server object
        :type hallo: Hallo.Hallo
        """
        self.hallo = hallo  # The hallo object that created this server
        # Persistent/saved class variables
        self.api_key = api_key
        self.name = "Telegram"  # Server name #TODO: needs to be configurable!
        self.auto_connect = True  # Whether to automatically connect to this server when hallo starts
        self.channel_list = []  # List of channels on this server (which may or may not be currently active)
        """ :type : list[Destination.Channel]"""
        self.user_list = []  # Users on this server (not all of which are online)
        """ :type : list[Destination.User]"""
        self.nick = None  # Nickname to use on this server
        self.prefix = None  # Prefix to use with functions on this server
        self.full_name = None  # Full name to use on this server
        self.permission_mask = PermissionMask()  # PermissionMask for the server
        # Dynamic/unsaved class variables
        self.state = Server.STATE_CLOSED  # Current state of the server, replacing open
        self._connect_lock = Lock()
        request = Request(con_pool_size=8)
        self.bot = telegram.Bot(token=self.api_key, request=request)
        self.bot.logger.setLevel(logging.INFO)
        self.updater = Updater(bot=self.bot)
        self.dispatcher = self.updater.dispatcher
        logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.ERROR)
        # Message handlers
        self.private_msg_handler = MessageHandler(Filters.private, self.parse_private_message)
        self.dispatcher.add_handler(self.private_msg_handler)
        self.group_msg_handler = MessageHandler(Filters.group, self.parse_group_message)
        self.dispatcher.add_handler(self.group_msg_handler)
        # Catch-all message handler for anything not already handled.
        self.core_msg_handler = MessageHandler(Filters.all, self.parse_unhandled, channel_post_updates=True)
        self.dispatcher.add_handler(self.core_msg_handler)

    class ChannelFilter(BaseFilter):
        def filter(self, message):
            return message.chat.type in [Chat.CHANNEL]

    def start(self):
        """
        Starts up the server and launches the new thread
        """
        if self.state != Server.STATE_CLOSED:
            raise ServerException("Already started.")
        self.state = Server.STATE_CONNECTING
        with self._connect_lock:
            Thread(target=self.connect).start()

    def connect(self):
        """
        Internal method
        Method to read from stream and process. Will connect and call internal parsing methods or whatnot.
        Needs to be started in it's own thread, only exits when the server connection ends
        """
        with self._connect_lock:
            self.updater.start_polling()
            self.state = Server.STATE_OPEN

    def disconnect(self, force=False):
        self.state = Server.STATE_DISCONNECTING
        with self._connect_lock:
            self.updater.stop()
            self.state = Server.STATE_CLOSED

    def reconnect(self):
        super().reconnect()

    def parse_private_message(self, bot, update):
        """
        Handles a new private message
        :param bot: telegram bot object
        :type bot: telegram.Bot
        :param update: Update object from telegram API
        :type update: telegram.Update
        """
        # Get sender object
        telegram_chat = update.message.chat
        names_list = [telegram_chat.first_name, telegram_chat.last_name]
        message_sender_name = " ".join([name for name in names_list if name is not None])
        message_sender_addr = update.message.chat.id
        message_sender = self.get_user_by_address(message_sender_addr, message_sender_name)
        message_sender.update_activity()
        # Create Event object
        if update.message.photo:
            photo_id = update.message.photo[-1]["file_id"]
            message_text = update.message.caption or ""
            message_evt = EventMessageWithPhoto(self, None, message_sender, message_text, photo_id)\
                .with_raw_data(RawDataTelegram(update))
        else:
            message_text = update.message.text
            message_evt = EventMessage(self, None, message_sender, message_text).with_raw_data(RawDataTelegram(update))
        # Print and Log the private message
        self.hallo.printer.output(message_evt)
        self.hallo.logger.log(message_evt)
        self.hallo.function_dispatcher.dispatch(message_evt)

    def parse_group_message(self, bot, update):
        """
        Handles a new group or supergroup message (does not handle channel posts)
        :param bot: telegram bot object
        :type bot: telegram.Bot
        :param update: Update object from telegram API
        :type update: telegram.Update
        """
        # Get sender object
        message_sender_name = " ".join([update.message.from_user.first_name, update.message.from_user.last_name])
        message_sender_addr = update.message.from_user.id
        message_sender = self.get_user_by_address(message_sender_addr, message_sender_name)
        message_sender.update_activity()
        # Get channel object
        message_channel_name = update.message.chat.title
        message_channel_addr = update.message.chat.id
        message_channel = self.get_channel_by_address(message_channel_addr, message_channel_name)
        message_channel.update_activity()
        # Create message event object
        if update.message.photo:
            photo_id = update.message.photo[-1]["file_id"]
            message_text = update.message.caption or ""
            message_evt = EventMessageWithPhoto(self, message_channel, message_sender, message_text, photo_id)\
                .with_raw_data(RawDataTelegram(update))
        else:
            message_text = update.message.text
            message_evt = EventMessage(self, message_channel, message_sender, message_text)\
                .with_raw_data(RawDataTelegram(update))
        # Print and log the public message
        self.hallo.printer.output(message_evt)
        self.hallo.logger.log(message_evt)
        # Send event to function dispatcher or passive dispatcher
        function_dispatcher = self.hallo.function_dispatcher
        if message_evt.is_prefixed:
            if message_evt.is_prefixed is True:
                function_dispatcher.dispatch(message_evt)
            else:
                function_dispatcher.dispatch(message_evt, [message_evt.is_prefixed])
        else:
            function_dispatcher.dispatch_passive(message_evt)

    def parse_join(self, bot, update):
        # TODO
        pass

    def parse_unhandled(self, bot, update):
        """
        Parses an unhandled message from the server
        :param bot: telegram bot object
        :type bot: telegram.Bot
        :param update: Update object from telegram API
        :type update: telegram.Update
        """
        # Print it to console
        error = MessageError("Unhandled data received on Telegram server: {}".format(update))
        self.hallo.logger.log(error)
        self.hallo.printer.output(error)

    def formatting_to_telegram_mode(self, event_formatting):
        """
        :type event_formatting: EventMessage.Formatting
        :rtype: telegram.ParseMode
        """
        return {EventMessage.Formatting.MARKDOWN: telegram.ParseMode.MARKDOWN,
                EventMessage.Formatting.HTML: telegram.ParseMode.HTML}.get(event_formatting)

    def send(self, event):
        if isinstance(event, EventMessageWithPhoto):
            destination = event.user if event.channel is None else event.channel
            if any([event.photo_id.lower().endswith("." + x) for x in ServerTelegram.image_extensions]):
                msg = self.bot.send_photo(
                    chat_id=destination.address,
                    photo=event.photo_id,
                    caption=event.text,
                    parse_mode=self.formatting_to_telegram_mode(event.formatting))
            else:
                msg = self.bot.send_document(
                    chat_id=destination.address,
                    document=event.photo_id,
                    caption=event.text,
                    parse_mode=self.formatting_to_telegram_mode(event.formatting))
            event.with_raw_data(RawDataTelegramOutbound(msg))
            self.hallo.printer.output(event)
            self.hallo.logger.log(event)
            return event
        if isinstance(event, EventMessage):
            destination = event.user if event.channel is None else event.channel
            msg = self.bot.send_message(
                chat_id=destination.address,
                text=event.text,
                parse_mode=self.formatting_to_telegram_mode(event.formatting))
            event.with_raw_data(RawDataTelegramOutbound(msg))
            self.hallo.printer.output(event)
            self.hallo.logger.log(event)
            return event
        else:
            error = MessageError("Unsupported event type, {}, sent to Telegram server".format(event.__class__.__name__))
            self.hallo.logger.log(error)
            self.hallo.printer.output(error)
            raise NotImplementedError()

    def reply(self, old_event, new_event):
        """
        :type old_event: Events.ChannelUserTextEvent
        :param new_event:
        :return:
        """
        # Do checks
        super().reply(old_event, new_event)
        if old_event.raw_data is None or not isinstance(old_event.raw_data, RawDataTelegram):
            raise ServerException("Old event has no telegram data associated with it")
        # Send event
        if isinstance(new_event, EventMessageWithPhoto):
            destination = new_event.user if new_event.channel is None else new_event.channel
            old_message_id = old_event.raw_data.update_obj.message.message_id
            if any([new_event.photo_id.lower().endswith("." + x) for x in ServerTelegram.image_extensions]):
                self.bot.send_photo(
                    destination.address,
                    new_event.photo_id,
                    caption=new_event.text,
                    reply_to_message_id=old_message_id,
                    parse_mode=self.formatting_to_telegram_mode(new_event.formatting))
            else:
                self.bot.send_document(
                    destination.address,
                    new_event.photo_id,
                    caption=new_event.text,
                    reply_to_message_id=old_message_id,
                    parse_mode=self.formatting_to_telegram_mode(new_event.formatting))
            self.hallo.printer.output(new_event)
            self.hallo.logger.log(new_event)
            return
        if isinstance(new_event, EventMessage):
            destination = new_event.user if new_event.channel is None else new_event.channel
            old_message_id = old_event.raw_data.update_obj.message.message_id
            self.bot.send_message(
                destination.address,
                new_event.text,
                reply_to_message_id=old_message_id,
                parse_mode=self.formatting_to_telegram_mode(new_event.formatting))
            self.hallo.printer.output(new_event)
            self.hallo.logger.log(new_event)
            return
        else:
            error = MessageError("Unsupported event type, {}, sent as reply to Telegram server".format(
                new_event.__class__.__name__))
            self.hallo.logger.log(error)
            self.hallo.printer.output(error)
            raise NotImplementedError()

    def get_name_by_address(self, address):
        chat = self.bot.get_chat(address)
        if chat.type == chat.PRIVATE:
            return " ".join([chat.first_name, chat.last_name])
        if chat.type in [chat.GROUP, chat.SUPERGROUP, chat.CHANNEL]:
            return chat.title

    def to_json(self):
        """
        Creates a dict of configuration for the server, to store as json
        :return: dict
        """
        json_obj = dict()
        json_obj["type"] = Server.TYPE_TELEGRAM
        json_obj["name"] = self.name
        json_obj["auto_connect"] = self.auto_connect
        json_obj["channels"] = []
        for channel in self.channel_list:
            json_obj["channels"].append(channel.to_json())
        json_obj["users"] = []
        for user in self.user_list:
            json_obj["users"].append(user.to_json())
        if self.nick is not None:
            json_obj["nick"] = self.nick
        if self.prefix is not None:
            json_obj["prefix"] = self.prefix
        if not self.permission_mask.is_empty():
            json_obj["permission_mask"] = self.permission_mask.to_json()
        json_obj["api_key"] = self.api_key
        return json_obj

    @staticmethod
    def from_json(json_obj, hallo):
        api_key = json_obj["api_key"]
        new_server = ServerTelegram(hallo, api_key)
        new_server.name = json_obj["name"]
        new_server.auto_connect = json_obj["auto_connect"]
        if "nick" in json_obj:
            new_server.nick = json_obj["nick"]
        if "prefix" in json_obj:
            new_server.prefix = json_obj["prefix"]
        if "permission_mask" in json_obj:
            new_server.permission_mask = PermissionMask.from_json(json_obj["permission_mask"])
        for channel in json_obj["channels"]:
            new_server.add_channel(Channel.from_json(channel, new_server))
        for user in json_obj["users"]:
            new_server.add_user(User.from_json(user, new_server))
        return new_server

    def join_channel(self, channel_obj):
        pass
        # TODO

    def check_user_identity(self, user_obj):
        return True
예제 #15
0
class Hallo:

    def __init__(self):
        self.default_nick = "Hallo"
        """:type : str"""
        self.default_prefix = False
        """:type : bool | str"""
        self.default_full_name = "HalloBot HalloHost HalloServer :an irc bot by spangle"
        """:type : str"""
        self.open = False
        """:type : bool"""
        self.user_group_list = set()
        """:type : set[UserGroup]"""
        self.server_list = set()
        """:type : set[Server.Server]"""
        self.logger = Logger(self)
        """:type : Logger"""
        self.printer = Printer(self)
        """:type : Printer"""
        self.api_key_list = {}
        """:type : dict[str,str]"""
        # Create ServerFactory
        self.server_factory = ServerFactory(self)
        """:type : ServerFactory"""
        self.permission_mask = PermissionMask()
        """:type : PermissionMask"""
        # TODO: manual FunctionDispatcher construction, user input?
        self.function_dispatcher = None
        """:type : Optional[FunctionDispatcher]"""

    def start(self):
        # If no function dispatcher, create one
        # TODO: manual FunctionDispatcher construction, user input?
        if self.function_dispatcher is None:
            self.function_dispatcher = FunctionDispatcher({"ChannelControl", "Convert", "HalloControl", "Lookup",
                                                           "Math", "PermissionControl", "Random", "ServerControl"},
                                                          self)
        # If no servers, ask for a new server
        if len(self.server_list) == 0:
            if sum([server.get_auto_connect() for server in self.server_list]) == 0:
                self.manual_server_connect()
        # Connect to auto-connect servers
        self.printer.output_raw('connecting to servers')
        for server in self.server_list:
            if server.get_auto_connect():
                Thread(target=server.run).start()
        self.open = True
        count = 0
        while all(not server.open for server in self.server_list if server.get_auto_connect()):
            time.sleep(0.1)
            count += 1
            if count > 600:
                self.open = False
                print("No servers managed to connect in 60 seconds.")
                break
        # Main loop, sticks around throughout the running of the bot
        self.printer.output_raw('connected to all servers.')
        self.core_loop_time_events()

    def core_loop_time_events(self):
        """
        Runs a loop to keep hallo running, while calling time events with the FunctionDispatcher passive dispatcher
        """
        last_date_time = datetime.now()
        while self.open:
            now_date_time = datetime.now()
            if now_date_time.second != last_date_time.second:
                self.function_dispatcher.dispatch_passive(Function.EVENT_SECOND, None, None, None, None)
            if now_date_time.minute != last_date_time.minute:
                self.function_dispatcher.dispatch_passive(Function.EVENT_MINUTE, None, None, None, None)
            if now_date_time.hour != last_date_time.hour:
                self.function_dispatcher.dispatch_passive(Function.EVENT_HOUR, None, None, None, None)
            if now_date_time.day != last_date_time.day:
                self.function_dispatcher.dispatch_passive(Function.EVENT_DAY, None, None, None, None)
            last_date_time = now_date_time
            time.sleep(0.1)

    @staticmethod
    def load_from_xml():
        try:
            doc = ElementTree.parse("config/config.xml")
        except (OSError, IOError):
            print("No current config, loading from default.")
            doc = ElementTree.parse("config/config-default.xml")
        new_hallo = Hallo()
        root = doc.getroot()
        new_hallo.default_nick = root.findtext("default_nick")
        new_hallo.default_prefix = Commons.string_from_file(root.findtext("default_prefix"))
        new_hallo.default_full_name = root.findtext("default_full_name")
        new_hallo.function_dispatcher = FunctionDispatcher.from_xml(
            ElementTree.tostring(root.find("function_dispatcher")), new_hallo)
        user_group_list_xml = root.find("user_group_list")
        for user_group_xml in user_group_list_xml.findall("user_group"):
            user_group_obj = UserGroup.from_xml(ElementTree.tostring(user_group_xml), new_hallo)
            new_hallo.add_user_group(user_group_obj)
        server_list_xml = root.find("server_list")
        for server_xml in server_list_xml.findall("server"):
            server_obj = new_hallo.server_factory.new_server_from_xml(ElementTree.tostring(server_xml))
            new_hallo.add_server(server_obj)
        if root.find("permission_mask") is not None:
            new_hallo.permission_mask = PermissionMask.from_xml(ElementTree.tostring(root.find("permission_mask")))
        api_key_list_xml = root.find("api_key_list")
        for api_key_xml in api_key_list_xml.findall("api_key"):
            api_key_name = api_key_xml.findtext("name")
            api_key_key = api_key_xml.findtext("key")
            new_hallo.add_api_key(api_key_name, api_key_key)
        return new_hallo

    def save_to_xml(self):
        # Create document, with DTD
        docimp = minidom.DOMImplementation()
        doctype = docimp.createDocumentType(
            qualifiedName='config',
            publicId='',
            systemId='config.dtd',
        )
        doc = docimp.createDocument(None, 'config', doctype)
        # Get root element
        root = doc.getElementsByTagName("config")[0]
        # Create default_nick element
        default_nick_elem = doc.createElement("default_nick")
        default_nick_elem.appendChild(doc.createTextNode(self.default_nick))
        root.appendChild(default_nick_elem)
        # Create default_prefix element
        if self.default_prefix is not None:
            default_prefix_elem = doc.createElement("default_prefix")
            if self.default_prefix is False:
                default_prefix_elem.appendChild(doc.createTextNode("0"))
            else:
                default_prefix_elem.appendChild(doc.createTextNode(self.default_prefix))
            root.appendChild(default_prefix_elem)
        # Create default_full_name element
        default_full_name_elem = doc.createElement("default_full_name")
        default_full_name_elem.appendChild(doc.createTextNode(self.default_full_name))
        root.appendChild(default_full_name_elem)
        # Create function dispatcher
        function_dispatcher_elem = minidom.parseString(self.function_dispatcher.to_xml()).firstChild
        root.appendChild(function_dispatcher_elem)
        # Create server list
        server_list_elem = doc.createElement("server_list")
        for server_elem in self.server_list:
            server_xml_str = server_elem.to_xml()
            if server_xml_str is not None:
                server_xml = minidom.parseString(server_elem.to_xml()).firstChild
                server_list_elem.appendChild(server_xml)
        root.appendChild(server_list_elem)
        # Create user_group list
        user_group_list_elem = doc.createElement("user_group_list")
        for user_group in self.user_group_list:
            user_group_elem = minidom.parseString(user_group.to_xml()).firstChild
            user_group_list_elem.appendChild(user_group_elem)
        root.appendChild(user_group_list_elem)
        # Create permission_mask element, if it's not empty.
        if not self.permission_mask.is_empty():
            permission_mask_elem = minidom.parseString(self.permission_mask.to_xml()).firstChild
            root.appendChild(permission_mask_elem)
        # Save api key list
        api_key_list_elem = doc.createElement("api_key_list")
        for api_key_name in self.api_key_list:
            api_key_elem = doc.createElement("api_key")
            api_key_name_elem = doc.createElement("name")
            api_key_name_elem.appendChild(doc.createTextNode(api_key_name))
            api_key_elem.appendChild(api_key_name_elem)
            api_key_key_elem = doc.createElement("key")
            api_key_key_elem.appendChild(doc.createTextNode(self.api_key_list[api_key_name]))
            api_key_elem.appendChild(api_key_key_elem)
            api_key_list_elem.appendChild(api_key_elem)
        root.appendChild(api_key_list_elem)
        # Save XML
        doc.writexml(open("config/config.xml", "w"), addindent="\t", newl="\r\n")

    def add_user_group(self, user_group):
        """
        Adds a new UserGroup to the UserGroup list
        :param user_group: UserGroup to add to the hallo object's list of user groups
        :type user_group: UserGroup
        """
        self.user_group_list.add(user_group)

    def get_user_group_by_name(self, user_group_name):
        """
        Returns the UserGroup with the specified name
        :param user_group_name: Name of user group to search for
        :type user_group_name: str
        :return: User Group matching specified name, or None
        :rtype: UserGroup | None
        """
        for user_group in self.user_group_list:
            if user_group_name == user_group.name:
                return user_group
        return None

    def remove_user_group(self, user_group):
        """
        Removes a user group specified by name
        :param user_group: Name of the user group to remove from list
        :type user_group: UserGroup
        """
        self.user_group_list.remove(user_group)

    def add_server(self, server):
        """
        Adds a new server to the server list
        :param server: Server to add to Hallo's list of servers
        :type server: Server.Server
        """
        self.server_list.add(server)

    def get_server_by_name(self, server_name):
        """
        Returns a server matching the given name
        :param server_name: name of the server to search for
        :return: Server matching specified name of None
        """
        for server in self.server_list:
            if server.get_name().lower() == server_name.lower():
                return server
        return None

    def get_server_list(self):
        """
        Returns the server list for hallo
        :rtype: list[Server.Server]
        """
        return self.server_list

    def remove_server(self, server):
        """
        Removes a server from the list of servers
        :param server: The server to remove
        :type server: Server.Server
        """
        self.server_list.remove(server)

    def remove_server_by_name(self, server_name):
        """
        Removes a server, specified by name, from the list of servers
        :param server_name: Name of the server to remove
        :type server_name: str
        """
        for server in self.server_list:
            if server.get_name() == server_name:
                self.server_list.remove(server)

    def close(self):
        """Shuts down the entire program"""
        for server in self.server_list:
            server.disconnect()
        self.function_dispatcher.close()
        self.save_to_xml()
        self.open = False

    def rights_check(self, right_name):
        """
        Checks the value of the right with the specified name. Returns boolean
        :param right_name: name of the user right to search for
        :return: Boolean, whether or not the specified right is given
        """
        right_value = self.permission_mask.get_right(right_name)
        # If PermissionMask contains that right, return it.
        if right_value in [True, False]:
            return right_value
        # If it's a function right, go to default_function right
        if right_name.startswith("function_"):
            return self.rights_check("default_function")
        # If default_function is not defined, define and return it as True
        if right_name == "default_function":
            self.permission_mask.set_right("default_function", True)
            return True
        else:
            # Else, define and return False
            self.permission_mask.set_right(right_name, False)
            return False

    def get_default_nick(self):  # Todo: deprecate and remove.
        """Default nick getter"""
        return self.default_nick

    def set_default_nick(self, default_nick):  # Todo: deprecate and remove.
        """
        Default nick setter
        :param default_nick: The new default nick to use on all new servers
        """
        self.default_nick = default_nick

    def get_default_prefix(self):  # Todo: deprecate and remove.
        """Default prefix getter"""
        return self.default_prefix

    def set_default_prefix(self, default_prefix):  # Todo: deprecate and remove.
        """
        Default prefix setter
        :param default_prefix: Default prefix to use for commands addressed to the bot
        """
        self.default_prefix = default_prefix

    def get_default_full_name(self):  # Todo: deprecate and remove.
        """Default full name getter"""
        return self.default_full_name

    def set_default_full_name(self, default_full_name):  # Todo: deprecate and remove.
        """
        Default full name setter
        :param default_full_name: Default full name to use on all new server connections
        """
        self.default_full_name = default_full_name

    def get_permission_mask(self):  # Todo: deprecate and remove.
        return self.permission_mask

    def get_function_dispatcher(self):  # Todo: deprecate and remove.
        """Returns the FunctionDispatcher object"""
        return self.function_dispatcher

    def get_logger(self):  # Todo: deprecate and remove.
        """Returns the Logger object"""
        return self.logger

    def get_printer(self):  # Todo: deprecate and remove
        """Returns the Printer object"""
        return self.printer

    def add_api_key(self, name, key):
        """
        Adds an api key to the list, or overwrites one.
        :param name: Name of the API to add
        :type name: str
        :param key: The actual API key to use
        :type key: str
        """
        self.api_key_list[name] = key

    def get_api_key(self, name):
        """
        Returns a specified api key.
        :param name: Name of the API key to retrieve
        """
        if name in self.api_key_list:
            return self.api_key_list[name]
        return None

    def manual_server_connect(self):
        # TODO: add ability to connect to non-IRC servers
        print("No servers have been loaded or connected to. Please connect to an IRC server.")
        # godNick = input("What nickname is the bot operator using? [deer-spangle] ")
        # godNick = godNick.replace(' ', '')
        # if godNick == '':
        #     godNick = 'deer-spangle'
        # TODO: do something with godNick
        server_addr = input("What server should the bot connect to? [irc.freenode.net:6667] ")
        server_addr = server_addr.replace(' ', '')
        if server_addr == '':
            server_addr = 'irc.freenode.net:6667'
        server_url = server_addr.split(':')[0]
        server_port = int(server_addr.split(':')[1])
        server_match = re.match(r'([a-z\d\.-]+\.)?([a-z\d-]{1,63})\.([a-z]{2,3}\.[a-z]{2}|[a-z]{2,6})', server_url,
                                re.I)
        server_name = server_match.group(2)
        # Create the server object
        new_server = ServerIRC(self, server_name, server_url, server_port)
        # Add new server to server list
        self.add_server(new_server)
        # Save XML
        self.save_to_xml()
        print("Config file saved.")
예제 #16
0
class UserGroup:
    """
    UserGroup object, mostly exists for a speedy way to apply a PermissionsMask to a large amount of users at once
    """

    def __init__(self, name, hallo):
        """
        Constructor
        :param name: Name of the user group
        :type name: str
        :param hallo: Hallo object which owns the user group
        :type hallo: Hallo.Hallo
        """
        self.user_list = set()  # Dynamic userlist of this group
        """:type : set[Destination.User]"""
        self.hallo = hallo  # Hallo instance that owns this UserGroup
        """:type : Hallo.Hallo"""
        self.name = name  # Name of the UserGroup
        """:type : str"""
        self.permission_mask = PermissionMask()  # PermissionMask for the UserGroup
        """:type : PermissionMask"""

    def __eq__(self, other):
        return (self.hallo, self.name) == (self.hallo, other.name)

    def __hash__(self):
        return (self.hallo, self.name).__hash__()

    def rights_check(self, right_name, user_obj, channel_obj=None):
        """Checks the value of the right with the specified name. Returns boolean
        :param right_name: Name of the right to check
        :type right_name: str
        :param user_obj: User which is having rights checked
        :type user_obj: Destination.User
        :param channel_obj: Channel in which rights are being checked, None for private messages
        :type channel_obj: Destination.Channel
        """
        right_value = self.permission_mask.get_right(right_name)
        # PermissionMask contains that right, return it.
        if right_value in [True, False]:
            return right_value
        # Fall back to channel, if defined
        if channel_obj is not None:
            return channel_obj.rights_check(right_name)
        # Fall back to the parent Server's decision.
        return user_obj.get_server().rights_check(right_name)

    def get_name(self):
        return self.name

    def get_permission_mask(self):
        return self.permission_mask

    def set_permission_mask(self, new_permission_mask):
        """
        Sets the permission mask of the user group
        :param new_permission_mask: Permission mask to set for user group
        :type new_permission_mask: PermissionMask.PermissionMask
        """
        self.permission_mask = new_permission_mask

    def get_hallo(self):
        return self.hallo

    def add_user(self, new_user):
        """
        Adds a new user to this group
        :param new_user: User to add to group
        :type new_user: Destination.User
        """
        self.user_list.add(new_user)

    def remove_user(self, remove_user):
        self.user_list.remove(remove_user)

    def to_xml(self):
        """Returns the UserGroup object XML"""
        # create document
        doc = minidom.Document()
        # create root element
        root = doc.createElement("user_group")
        doc.appendChild(root)
        # create name element
        name_elem = doc.createElement("name")
        name_elem.appendChild(doc.createTextNode(self.name))
        root.appendChild(name_elem)
        # create permission_mask element
        if not self.permission_mask.is_empty():
            permission_mask_elem = minidom.parseString(self.permission_mask.to_xml()).firstChild
            root.appendChild(permission_mask_elem)
        # output XML string
        return doc.toxml()

    @staticmethod
    def from_xml(xml_string, hallo):
        """
        Loads a new UserGroup object from XML
        :param xml_string: String containing XML to parse for usergroup
        :type xml_string: str
        :param hallo: Hallo object to add user group to
        :type hallo: Hallo.Hallo
        """
        doc = minidom.parseString(xml_string)
        new_name = doc.getElementsByTagName("name")[0].firstChild.data
        new_user_group = UserGroup(new_name, hallo)
        if len(doc.getElementsByTagName("permission_mask")) != 0:
            new_user_group.permission_mask = PermissionMask.from_xml(
                doc.getElementsByTagName("permission_mask")[0].toxml())
        return new_user_group