#!/usr/bin/env python
# -*- coding: utf-8
#
# munin-info.py - "murmur stats (User/Bans/Uptime/Channels)" script.
# Copyright (c) 2019, Charles Lu / c4planted@gmail.com
# Adapted from Natenom's murmur-munin.py
# Copyright (c) 2014, Natenom / natenom@natenom.name
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of the developer nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. 
import Ice, sys, os

class MurmurIce:

    def __init__(self, murmur_ice_path, murmur_host, murmur_icesecreatread, ice_port=6502, message_size_max=65535, exclude_keywords=[]):

        # Exclude names containing these keywords from being counted as users.
        # Useful for excluding bots 
        self.exclude_keywords=exclude_keywords

        # Path to Slice and Murmur.ice
        Ice.loadSlice(f"--all -I{Ice.getSliceDir()} {murmur_ice_path}")

        props = Ice.createProperties([])
        # MessageSizeMax; increase this value, if you get a MemoryLimitException.
        # Also check this value in murmur.ini of your Mumble-Server.
        # This value is being interpreted in kibiBytes.
        props.setProperty("Ice.MessageSizeMax", str(message_size_max))
        props.setProperty("Ice.ImplicitContext", "Shared")
        props.setProperty("Ice.Default.EncodingVersion", "1.0")  # Murmur 1.2.x uses Ice protocl 1.0 only

        id = Ice.InitializationData()
        id.properties = props

        self.ice = Ice.initialize(id)

        # Ice Password to get read access.
        # If there is no such var in your murmur.ini, this can have any value.
        # You can use the values of icesecret, icesecretread or icesecretwrite in your murmur.ini
        self.ice.getImplicitContext().put("secret", murmur_icesecreatread)

        import Murmur

        try:
            # Murmur host IP/DNS and ice_port should be provided
            self.meta = Murmur.MetaPrx.checkedCast(self.ice.stringToProxy(f'Meta:tcp -h {murmur_host} -p {ice_port}'))
        except (Ice.DNSException, Ice.ConnectionRefusedException):
            print('Connection refused.')
            exit(1)

        try:
            self.server=self.meta.getServer(1)
        except Murmur.InvalidSecretException: 
            print('Given icesecreatread password is wrong.')
            exit(1)

        self.users = 0
        self.excludedusers = 0
        self.usersnotauth = 0

        self._count_users()

    def __del__(self):
        self.ice.destroy()

    def _count_users(self):
        # count users
        self.users = self.server.getUsers()
        # count the number of users to exclude
        # also count not authenticated users (who are not excluded)
        for user in self.users.values():
            for keyword in self.exclude_keywords:
                if keyword and str(keyword).lower() in user.name.lower():
                    self.excludedusers += 1
                    break
            else:
                # not excluded
                if user.userid == -1:
                    self.usersnotauth += 1
        
    def get_all_values(self):
        print(f"users: {(len(self.users)-self.excludedusers)}")
        print(f"uptime: {self.meta.getUptime()}")
        print(f"chancount: {(len(self.server.getChannels())-1)}")
        print(f"bancount: {len(self.server.getBans())}")
        print(f"usersnotauth: {self.usersnotauth}")
        print("state: 1")
        print(f'version: {self.meta.getVersion()[0]}.{self.meta.getVersion()[1]}.{self.meta.getVersion()[2]}')

    def get_value(self, key):
        if key == "users":
            print(len(self.users)-self.excludedusers)

        elif key == "uptime":
            print(self.meta.getUptime())

        elif key == "chancount":
            print(len(self.server.getChannels())-1)

        elif key == "bancount":
            print(len(self.server.getBans()))

        elif key == "usersnotauth":
            print(self.usersnotauth)

        elif key == "state":
            print(1)

        elif key == "version":
            print(f'{self.meta.getVersion()[0]}.{self.meta.getVersion()[1]}.{self.meta.getVersion()[2]}')

        elif key == "useronline" and len(sys.argv) == 3:
            for key in self.users.keys():
                if sys.argv[2].lower() == self.users[key].name.lower():
                    print(1)
                    break
            else:
                print(0)
        else:
            self.get_all_values()


if __name__ == "__main__":

    murmur_ice_path = os.getenv('MURMUR_ICE_PATH')
    murmur_host = os.getenv('MURMUR_HOST', '127.0.0.1')
    ice_port = os.getenv('MURMUR_ICE_PORT', 6502)
    murmur_icesecreatread = os.getenv('MURMUR_ICE_SECRET')
    message_size_max = os.getenv('MURMUR_ICE_MSG_SIZE_MAX', 65535)
    exclude_keywords = os.getenv('EXCLUDE_KEYWORDS', '').split(',')

    if not murmur_ice_path:
        raise RuntimeError('MURMUR_ICE_PATH environment variable is not set!')
    if not murmur_icesecreatread:
        raise RuntimeError('MURMUR_ICE_SECRET environment variable is not set!')

    murmur = MurmurIce(
        murmur_ice_path=murmur_ice_path,
        murmur_host=murmur_host, 
        murmur_icesecreatread=murmur_icesecreatread,
        ice_port=ice_port,
        message_size_max=message_size_max,
        exclude_keywords=exclude_keywords
    )

    # Usage
    if sys.argv[1:]:
        if sys.argv[1] == "help":
            print('useronline [name]: Checks if the user with the name provided is online')
            print('users: Number of users online')
            print('usersnotauth: Number of users online (Not authenticated)')
            print('uptime: Uptime in seconds')
            print('chancount: Channel count')
            print('bancount: Bans on server')
            print("state: Mumble server status")
            print("version: Mumble server version")

        else:
            murmur.get_value(sys.argv[1])

    else:
        murmur.get_all_values()
