예제 #1
0
def get_user_id(server_id, name):
    """Gets the user ID from a readable name or nickname."""
    users_data = servermanager.servers_data[server_id]['users']
    name = name.strip()
    if name.startswith('<@') and name.endswith('>'): # Mention
        try: # Check if user exists
            users_data[name[2:-1]]
            return name[2:-1]
        except KeyError:
            pass
    discovered_already = False
    found_id = ''
    for user_id in users_data: # Iterate to find name or nickname
        if users_data[user_id]['name'] == name or users_data[user_id]['nickname'] == name:
            if not discovered_already:
                found_id = user_id
                discovered_already = True
            else:
                raise bot_exception(EXCEPT_TYPE, "Duplicate names found for '{}'! Specify user with a mention".format(name))
    if found_id:
        return found_id
    try: # Raw user ID - last resort
        users_data[name]
        return name
    except KeyError:
        raise bot_exception(EXCEPT_TYPE, "User '{}' was not found".format(name))
예제 #2
0
def wikipedia_query(query, simple_result=False):
    if not query:
        return "Try searching for *something* next time, knucklehead."
    try:
        page = wikipedia.page(query, auto_suggest=True)
        if simple_result: # Just return the url of the found page
            return page.url
        else: # Return the first ~500 characters of the summary
            title = page.title
            summary = page.summary
            for i in range(0, (len(summary) if len(summary) < 500 else 500) - 1):
                if summary[i] == '=' and summary[i+1] == '=':
                    summary = summary[0:i]
                    break;
            if len(summary) >= 500:
                summary = summary[0:500]
                summary += ' ...*`[truncated]`*'
            return "***```{title}```***\n{summary}".format(title=title, summary=summary)
    except wikipedia.exceptions.PageError:
        raise bot_exception(WIKIPEDIA_EXCEPTION, 
            "Page doesn't exist. Trying for some suggestions...", '```{}```'.format(
            (wikipedia.suggest(query) if wikipedia.suggest(query) is not None else "None")))
    except wikipedia.exceptions.DisambiguationError as tp: # Try to get list of suggestions
        suggestions = wikipedia.search(query, results=5)
        if len(suggestions) > 0:
            formatted_suggestions = '```\n'
            for suggestion in suggestions:
                formatted_suggestions += '{}\n'.format(suggestion)
            formatted_suggestions += '```'
            raise bot_exception(WIKIPEDIA_EXCEPTION, "Query is too ambiguous. Here are some suggestions:", formatted_suggestions)
        else:
            raise bot_exception(WIKIPEDIA_EXCEPTION, "Query is too ambiguous. No suggestions found.")
예제 #3
0
def check_tag_access(server_id, tag_data, tag_name, user_id, need_owner=False):
    """Ensures that the given user is author of the tag (or a bot admin) if the tag is private, otherwise throw an exception."""
    if (tag_data['private'] or need_owner) and (tag_data['author_id'] != user_id and not servermanager.is_admin(server_id, user_id)):
        tag_author = usermanager.get_name(server_id, tag_data['author_id'])
        if need_owner:
            raise bot_exception(EXCEPT_TYPE, "User is not the author of tag '{tag_name}', created by {tag_author}".format(tag_name=tag_name, tag_author=tag_author))
        else:
            raise bot_exception(EXCEPT_TYPE, "The tag '{tag_name}' was made private by the author, {tag_author}".format(tag_name=tag_name, tag_author=tag_author))
예제 #4
0
async def update_color(server_id, user_id):
    """Refreshes the color role so that it is on top."""
    if not botmanager.has_role_permissions(server_id):
        raise bot_exception(EXCEPT_TYPE, "Bot must be able to manage permissions in order to change role colors.")
    color = servermanager.servers_data[server_id]['users'][user_id]['color']
    if color:
        converted = int(color[1:], 16)
        await botmanager.update_color_role(server_id, user_id, converted)
    else:
        raise bot_exception(EXCEPT_TYPE, "You currently don't have a custom color!")
    return "Color successfully refreshed!"
예제 #5
0
def add_ban(server_id, user_id, add=True):
    """Bans or unbans the given user."""
    if (add and is_banned(server_id, user_id)) or (not add and not is_banned(server_id, user_id)):
        raise bot_exception(EXCEPT_TYPE, "User is already {}banned".format('' if add else "un"))
    elif add and is_admin(server_id, user_id):
        raise bot_exception(EXCEPT_TYPE, "Admins cannot be banned (remove admin status first to ban)")
    if add:
        servers_data[server_id]['bans'].append(user_id)
    else:
        servers_data[server_id]['bans'].remove(user_id)
    write_data()
    return "User successfully {}banned".format('' if add else "un")
예제 #6
0
def add_admin(server_id, user_id, add=True):
    """Adds or revokes admin privileges for given user."""
    if (add and is_admin(server_id, user_id)) or (not add and not is_admin(server_id, user_id)):
        raise bot_exception(EXCEPT_TYPE, "User is already {} as an admin".format("added" if add else "removed"))
    if add:
        servers_data[server_id]['admins'].append(user_id)
    else:
        if user_id == configmanager.config['owner_id']:
            raise bot_exception(EXCEPT_TYPE, "You can't remove yourself, silly goose!")
        servers_data[server_id]['admins'].remove(user_id)
    write_data()
    return "User successfully {} as an admin".format("added" if add else "removed")
예제 #7
0
def get_random_avatar():
    """Returns a bytes-like file from a downloaded image specified in avatars.txt."""

    if os.stat(config_directory + '/avatars.txt').st_size > 0:
        image_url = random.choice(list(open(config_directory + '/avatars.txt')))
        try: # Make sure the file can actually be downloaded
            urllib.request.urlretrieve(image_url, data_directory + '/tempavatar')
            with open(data_directory + '/tempavatar', 'rb') as avatar:
                return avatar.read()
        except:
            raise bot_exception(EXCEPT_TYPE, "Failed to download avatar from url {}\n(ensure that the url is a direct link to a PNG or JPG image, or try uploading the image to a better host, such as imgur.com or mixtape.moe)".format(image_url))
    else:
        raise bot_exception(EXCEPT_TYPE, "Failed to update the avatar because the avatars.txt file is empty")
예제 #8
0
async def play_sound_tag(server_id, voice_channel_id, sound_tag_name, user_id):
    """Plays the sound from the given sound tag if it is available."""
    
    try:
        if servermanager.is_muted(server_id, voice_channel_id):
            raise bot_exception(EXCEPT_TYPE, "The bot is muted in this voice channel")
    except KeyError:
        raise bot_exception(EXCEPT_TYPE, "You are not in a voice channel (are you perhaps on a different server?)")
    
    sound_tag_data = get_sound_tag_data(server_id, sound_tag_name)
    check_sound_tag_access(server_id, sound_tag_data, user_id, need_owner=False)
    update_sound_tag(server_id, sound_tag_name, increment_hits=True) # Increment hits
    
    from jshbot.botmanager import client
    from jshbot.botmanager import voice_player
    global timeout_goal
    
    channel = botmanager.get_voice_channel(server_id, voice_channel_id)
    if client.voice == None or client.voice.channel != channel or not client.voice.is_connected(): # Connect to channel
        if client.voice: # Disconnect from old channel
            await client.voice.disconnect()
        client.voice = await client.join_voice_channel(channel)
        voice_player.server_id = server_id
    
    if voice_player.player is not None and voice_player.player.is_playing(): # Stop if playing
        voice_player.player.stop()
        
    if sound_tag_data['type'] == 'YouTube':
        # To prevent playlist downloads
        # 'noplaylist': True
        voice_player.player = await client.voice.create_ytdl_player(sound_tag_data['url'])
    else: # Direct download (or stream? Not sure)
        try:
            # One day, I will figure out how to stream this crap. But today is not that day.
            #response = urllib.request.urlopen(sound_tag_data['url'])
            #voice_player.player = client.voice.create_ffmpeg_player(response, use_avconv=True)
            urllib.request.urlretrieve (sound_tag_data['url'], '{}/tempsound'.format(configmanager.data_directory))
            voice_player.player = client.voice.create_ffmpeg_player('{}/tempsound'.format(configmanager.data_directory))
        except:
            raise bot_exception(EXCEPT_TYPE, "An error occurred when downloading the sound file")
    voice_player.player.start()
    
    timeout = configmanager.config['voice_timeout']
    if timeout >= 0:
        timeout_reset_lock.acquire()
        timeout_goal = time.time() + ((timeout*60) if timeout > 0 else (sound_tag_data['length']+1))
        timeout_reset_lock.release()
        await timeout_disconnect()
예제 #9
0
def get_tag_data(server_id, tag_name):
    """Ensures that the given tag exists and return tag data, otherwise throw an exception."""
    try:
        return servermanager.servers_data[server_id]['tags'][tag_name]
    except KeyError:
        raise bot_exception(EXCEPT_TYPE,
                            "Tag '{}' doesn't exist".format(tag_name))
예제 #10
0
def check_tag_access(server_id, tag_data, tag_name, user_id, need_owner=False):
    """Ensures that the given user is author of the tag (or a bot admin) if the tag is private, otherwise throw an exception."""
    if (tag_data['private'] or need_owner) and (
            tag_data['author_id'] != user_id
            and not servermanager.is_admin(server_id, user_id)):
        tag_author = usermanager.get_name(server_id, tag_data['author_id'])
        if need_owner:
            raise bot_exception(
                EXCEPT_TYPE,
                "User is not the author of tag '{tag_name}', created by {tag_author}"
                .format(tag_name=tag_name, tag_author=tag_author))
        else:
            raise bot_exception(
                EXCEPT_TYPE,
                "The tag '{tag_name}' was made private by the author, {tag_author}"
                .format(tag_name=tag_name, tag_author=tag_author))
예제 #11
0
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private):
    """Gets a response from the command and parameters."""
    
    to_return = ''
    num_options = len(options)
    num_arguments = len(arguments)
    using_shortcut = command in commands_dictionary['shortcut_commands']
    
    # Wikipedia query
    if command in wikipedia_commands and ((num_options == 1 and options[0] in ['s', 'simple', 'url']) or num_options == 0):
        simple_result = False
        if using_shortcut or num_options == 1:
            simple_result = True
        return wikipedia_query(arguments[0] if num_arguments == 1 else arguments_blocks[0], simple_result=simple_result)
    
    # Wolfram|Alpha query
    elif (command in wolfram_alpha_commands and num_arguments >= 1 and 
            ((num_options == 1 and (options[0].startswith('results=') or options[0] in ['s', 'simple']) or num_options == 0))):
        simple_result = False
        results_limit = 2
        if using_shortcut or (num_options == 1 and options[0] in ['s', 'simple']):
            simple_result = True
        elif num_options == 1: # Result number specification
            results_limit = int(options[0][8:])
        wolfram_query = arguments[0] if num_arguments == 1 else arguments_blocks[0]
        return wolfram_alpha_query(wolfram_query, simple_result=simple_result, results_limit=results_limit)
    
    # Urban Dictionary definition
    elif command in urban_dictionary_commands and num_arguments >= 1 and num_options == 0:
        urban_query = arguments[0] if num_arguments == 1 else arguments_blocks[0]
        return urban_dictionary_definition(urban_query)
    
    # Invalid command
    command, EXCEPT_TYPE = get_command_info(command)
    raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string(command))
예제 #12
0
def set_nickname(server_id, user_id, nickname_text):
    """Sets the user nickname as the given text."""
    if len(nickname_text) > 50:
        raise bot_exception(EXCEPT_TYPE, "Nickname cannot be more than 50 characters long")
    servers_data = servermanager.servers_data
    servers_data[server_id]['users'][user_id]['nickname'] = nickname_text
    write_data()
    return "Nickname successfully {}!".format("set" if nickname_text else "cleared")
예제 #13
0
def set_status(server_id, user_id, status_text):
    """Sets the user status as the given text."""
    if len(status_text) > 1000:
        raise bot_exception(EXCEPT_TYPE, "Status cannot be more than 1000 characters long")
    servers_data = servermanager.servers_data
    servers_data[server_id]['users'][user_id]['status'] = status_text
    write_data()
    return "Status successfully {}!".format("set" if status_text else "cleared")
예제 #14
0
def add_admin(server_id, user_id, add=True):
    """Adds or revokes admin privileges for given user."""
    if (add and is_admin(server_id, user_id)) or (
            not add and not is_admin(server_id, user_id)):
        raise bot_exception(
            EXCEPT_TYPE, "User is already {} as an admin".format(
                "added" if add else "removed"))
    if add:
        servers_data[server_id]['admins'].append(user_id)
    else:
        if user_id == configmanager.config['owner_id']:
            raise bot_exception(EXCEPT_TYPE,
                                "You can't remove yourself, silly goose!")
        servers_data[server_id]['admins'].remove(user_id)
    write_data()
    return "User successfully {} as an admin".format(
        "added" if add else "removed")
예제 #15
0
def add_ban(server_id, user_id, add=True):
    """Bans or unbans the given user."""
    if (add and is_banned(server_id, user_id)) or (
            not add and not is_banned(server_id, user_id)):
        raise bot_exception(
            EXCEPT_TYPE,
            "User is already {}banned".format('' if add else "un"))
    elif add and is_admin(server_id, user_id):
        raise bot_exception(
            EXCEPT_TYPE,
            "Admins cannot be banned (remove admin status first to ban)")
    if add:
        servers_data[server_id]['bans'].append(user_id)
    else:
        servers_data[server_id]['bans'].remove(user_id)
    write_data()
    return "User successfully {}banned".format('' if add else "un")
예제 #16
0
def mute_server(server_id, mute=True):
    """Mutes or unmutes the specified server."""
    server_muted = servers_data[server_id]['muted']
    if (mute and server_muted) or (not mute and not server_muted):
        raise bot_exception(EXCEPT_TYPE, "Server is already {}muted".format('' if mute else "un"))
    servers_data[server_id]['muted'] = mute
    write_data()
    return "Server successfully {}muted".format('' if mute else "un")
예제 #17
0
def get_random_status():
    """Returns a random status from the statuses.txt file."""
    
    if os.stat(config_directory + '/statuses.txt').st_size > 0:
        with open(config_directory + '/statuses.txt', 'r') as statuses_file:
            return str(random.choice(list(statuses_file))).rstrip()
    else:
        raise bot_exception(EXCEPT_TYPE, "Failed to update the status because the statuses.txt file is empty")
예제 #18
0
def update_tag(server_id, tag_name, increment_hits=False, **kwargs):
    """Updates or creates a tag in the server under the given author ID.
    
    Keyword arguments:
    increment_hits -- increments the hits counter of the tag
    user_id -- user trying to modify the tag
    author_id -- author of the tag
    tag_text -- text to go along with the tag
    private -- whether or not the tag can only be called by the author
    hits -- how many times the tag has been called
    full_name -- what the full name (with spaces) is (never passed in)
    """
    to_return = ''
    full_name = tag_name
    tag_name = tag_name.replace(' ', '')
    servers_data = servermanager.servers_data
    if increment_hits: # Updating hit counter
        servers_data[server_id]['tags'][tag_name]['hits'] += 1
    else: # Creating or modifying a tag
        try: # Modify a tag
            tag_data = servers_data[server_id]['tags'][tag_name]
            try:
                check_tag_access(server_id, tag_data, tag_name, kwargs['user_id'], need_owner=True)
            except KeyError: # user_id not found, meant to create but tag exists
                raise bot_exception(EXCEPT_TYPE, "Tag '{}' already exists".format(tag_name))
            del kwargs['user_id'] # Don't write user_id to updated tag
            servers_data[server_id]['tags'][tag_name].update(kwargs)
            to_return += "Tag '{}' successfully modified!".format(full_name)
        except KeyError: # Tag doesn't exist. Create it.
            if configmanager.config['tags_per_server'] > 0 and len(servers_data[server_id]['tags']) >= configmanager.config['tags_per_server']:
                raise bot_exception(EXCEPT_TYPE, "This server has hit the tag limit of {}".format(configmanager.config['tags_per_server']))
            if 'tag_text' not in kwargs:
                raise bot_exception(EXCEPT_TYPE, "Tag '{}' does not exist".format(tag_name))
            if len(tag_name) > 50:
                raise bot_exception(EXCEPT_TYPE, "Tag names cannot be larger than 50 characters long")
            if len(kwargs['tag_text']) > 2000: # This shouldn't really happen, ever
                raise bot_exception(EXCEPT_TYPE, "Tag text cannot be larger than 2000 characters long")
            # Edit safety
            if 'user_id' in kwargs:
                kwargs['author_id'] = kwargs['user_id']
                del kwargs['user_id']
            kwargs['full_name'] = full_name
            servers_data[server_id]['tags'][tag_name] = {**kwargs, 'hits':0, 'date_created':time.strftime("%c")}
            to_return += "Tag '{}' successfully created!".format(full_name)
    write_data()
    return to_return
예제 #19
0
def mute_channel(server_id, channel_id, mute=True):
    """Mutes or unmutes the specified channel."""
    channel_muted = servers_data[server_id]['channels'][channel_id]['muted']
    if (mute and channel_muted) or (not mute and not channel_muted):
        raise bot_exception(EXCEPT_TYPE, "Channel is already {}muted".format('' if mute else "un"))
    servers_data[server_id]['channels'][channel_id]['muted'] = mute
    write_data()
    return "Channel successfully {}muted".format('' if mute else "un")
예제 #20
0
def mute_channel(server_id, channel_id, mute=True):
    """Mutes or unmutes the specified channel."""
    channel_muted = servers_data[server_id]['channels'][channel_id]['muted']
    if (mute and channel_muted) or (not mute and not channel_muted):
        raise bot_exception(
            EXCEPT_TYPE,
            "Channel is already {}muted".format('' if mute else "un"))
    servers_data[server_id]['channels'][channel_id]['muted'] = mute
    write_data()
    return "Channel successfully {}muted".format('' if mute else "un")
예제 #21
0
def mute_server(server_id, mute=True):
    """Mutes or unmutes the specified server."""
    server_muted = servers_data[server_id]['muted']
    if (mute and server_muted) or (not mute and not server_muted):
        raise bot_exception(
            EXCEPT_TYPE,
            "Server is already {}muted".format('' if mute else "un"))
    servers_data[server_id]['muted'] = mute
    write_data()
    return "Server successfully {}muted".format('' if mute else "un")
예제 #22
0
async def set_color(server_id, user_id, color):
    """Gives the user the given color as a role."""
    if not botmanager.has_role_permissions(server_id): # Check bot permissions first
        raise bot_exception(EXCEPT_TYPE, "Bot must be able to manage permissions in order to change role colors.")
    if color is None:
        servermanager.servers_data[server_id]['users'][user_id]['color'] = ''
        write_data()
        await botmanager.update_color_role(server_id, user_id, None)
        return "Color successfully cleared!"
    if color.startswith('#'): # Just in case people just copy values over
        color = color[1:]
    try: # Make sure we're given a valid hex value here
        converted = int(color, 16)
    except ValueError:
        raise bot_exception(EXCEPT_TYPE, "'{}' does not appear to be a valid hex color.".format(color))
    await botmanager.update_color_role(server_id, user_id, converted)
    servermanager.servers_data[server_id]['users'][user_id]['color'] = '#{}'.format(color.upper())
    write_data()    
    return "Color successfully set!"
예제 #23
0
def get_random_status():
    """Returns a random status from the statuses.txt file."""

    if os.stat(config_directory + '/statuses.txt').st_size > 0:
        with open(config_directory + '/statuses.txt', 'r') as statuses_file:
            return str(random.choice(list(statuses_file))).rstrip()
    else:
        raise bot_exception(
            EXCEPT_TYPE,
            "Failed to update the status because the statuses.txt file is empty"
        )
예제 #24
0
def get_random_avatar():
    """Returns a bytes-like file from a downloaded image specified in avatars.txt."""

    if os.stat(config_directory + '/avatars.txt').st_size > 0:
        image_url = random.choice(list(open(config_directory +
                                            '/avatars.txt')))
        try:  # Make sure the file can actually be downloaded
            urllib.request.urlretrieve(image_url,
                                       data_directory + '/tempavatar')
            with open(data_directory + '/tempavatar', 'rb') as avatar:
                return avatar.read()
        except:
            raise bot_exception(
                EXCEPT_TYPE,
                "Failed to download avatar from url {}\n(ensure that the url is a direct link to a PNG or JPG image, or try uploading the image to a better host, such as imgur.com or mixtape.moe)"
                .format(image_url))
    else:
        raise bot_exception(
            EXCEPT_TYPE,
            "Failed to update the avatar because the avatars.txt file is empty"
        )
예제 #25
0
async def stop_sounds(server_id, timeout=False):
    """Stops all audio and disconnects the bot from the voice channel."""
    from jshbot.botmanager import client
    from jshbot.botmanager import voice_player
    if voice_player.player and voice_player.player.is_playing():
        if not timeout:
            if voice_player.server_id != server_id: # Ensure we're stopping the bot on the server it is playing at
                raise bot_exception(EXCEPT_TYPE, "The bot is not connected to this server")
        voice_player.player.stop()
        if client.voice:
            await client.voice.disconnect()
        voice_player.server_id = None
    elif client.voice.is_connected():
        if not timeout:
            if voice_player.server_id != server_id: # Ensure we're stopping the bot on the server it is playing at
                raise bot_exception(EXCEPT_TYPE, "The bot is not connected to this server")
        await client.voice.disconnect()
        if voice_player:
            voice_player.server_id = None
    elif not timeout:
        raise bot_exception(EXCEPT_TYPE, "No sound is playing")
    return "Stopping all sounds and disconnecting..."
예제 #26
0
def urban_dictionary_definition(query):
    parsed_query = urllib.parse.quote_plus(query)
    query_url = 'http://www.urbandictionary.com/define.php?term={}'.format(parsed_query)
    try:
        urban_data = requests.get('http://api.urbandictionary.com/v0/define?term={}'.format(parsed_query)).json()
    except:
        raise bot_exception(URBAN_DICTIONARY_EXCEPTION, "Failed to get definition from Urban Dictionary", query_url)
    if urban_data['result_type'] != 'exact': # Probably 'no_result', but this is safer
        raise bot_exception(URBAN_DICTIONARY_EXCEPTION, "The query '{}' returned no results".format(query), query_url)
    definition = urban_data['list'][0]
    rating = '{}% rating (:thumbsup:{} | :thumbsdown:{})'.format(
            int(100*definition['thumbs_up'] / (definition['thumbs_up'] + definition['thumbs_down'])),
            definition['thumbs_up'], definition['thumbs_down'])
    # Truncate definition and examples so that they aren't so freakin' long
    if len(definition['definition']) > 500:
        definition['definition'] = '{} ...*`[truncated]`*'.format(definition['definition'][:499])
    if len(definition['example']) > 500:
        definition['example'] = '{} ...`[truncated]`'.format(definition['example'][:499])
    elif len(definition['example']) == 0:
        definition['example'] = 'No example provided'
    return '***```{definition[word]}```***\n{definition[definition]}\n\n*{definition[example]}*\n\n*{query_url}* - {rating}'.format(
            definition=definition, rating=rating, query_url=query_url)
예제 #27
0
def get_channel_info(server_id, channel_id):
    """Retrieves a bundle of channel information."""
    if channel_id == 0: # No voice channel
        raise bot_exception(EXCEPT_TYPE, "You are not in a voice channel on this server")
    return """Channel information for {channel_data[name]}:
```
ID: {channel_id}
Name: {channel_data[name]}
Muted: {channel_data[muted]}
Position: {channel_data[position]}
Voice channel: {channel_data[voice]}
Default: {channel_data[default]}
```""".format(channel_id=channel_id, channel_data=servers_data[server_id]['channels'][channel_id], config=configmanager.config)
예제 #28
0
def get_random_number(minimum=1, maximum=100, is_private=True, **kwargs):
    """Obtains a random number with the specified minimum and maximum values. Same keyword arguments as flip_coin."""
    try: # Typecheck minimum and maximum values
        minimum = int(minimum)
        maximum = int(maximum)
    except:
        raise bot_exception(EXCEPT_TYPE, "Inputs are not valid integers")
    if (minimum == maximum):
        return "You must think you're really clever."
    if (minimum > maximum):
        minimum, maximum = maximum, minimum
    return "{identifier} got {result}.".format(
        identifier="You" if is_private else usermanager.get_name(kwargs['server_id'], kwargs['user_id']),
        result=random.randint(minimum, maximum))
예제 #29
0
def roll_die(faces=6, is_private=True, **kwargs):
    """Rolls a 6 sided die unless specified. Same keyword arguments as flip_coin."""
    try: # Typecheck faces value
        faces = int(faces)
    except:
        raise bot_exception(EXCEPT_TYPE, "Input is not a valid integer")
    to_return = "You" if is_private else usermanager.get_name(kwargs['server_id'], kwargs['user_id'])
    if (faces == 1):
        to_return += " rolled a {}. Just kidding.".format(random.randint(1,2147483647))
    elif (faces <= 0):
        return "Look, I can't roll something that breaks the laws of physics."
    else: # Valid roll
        to_return += " rolled a {}.".format(random.randint(1, faces))
    return to_return
예제 #30
0
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private):
    """Gets a response from the command and parameters."""
    
    to_return = ''
    num_options = len(options)
    num_arguments = len(arguments)
    using_shortcut = command in commands_dictionary['shortcut_commands']

    # Flip a coin
    if ((command in ['flip'] and num_options == 0 and num_arguments == 0) or
            (not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['f', 'flip'])):
        public_info = {}
        if not is_private: # Bundle server and user ID if we need a name
            public_info = {'server_id':server_id, 'user_id':user_id}
        return flip_coin(is_private=is_private, **public_info)

    # Roll dice
    elif ((command in ['roll'] and num_arguments <= 1) or
            (not using_shortcut and num_options == 1 and num_arguments <= 1 and options[0] in ['r', 'roll', 'd', 'dice'])):
        public_info = {}
        if not is_private:
            public_info = {'server_id':server_id, 'user_id':user_id}
        return roll_die(faces=arguments[0] if num_arguments == 1 else 6, is_private=is_private, **public_info)

    # Pick a choice
    elif ((command in ['pick'] and num_options == 0 and num_arguments >= 0) or
            (not using_shortcut and num_options == 1 and num_arguments >= 0 and options[0] in ['p', 'pick', 'c', 'choose', 'd', 'decide', 'f', 'fluky'])):
        return pick_choice(*arguments) # Number of arguments is checked in function
        
    # Get a random tag lolwat
    elif not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['t', 'tag']:
        return tagmanager.get_random_tag(server_id, user_id)

    # Get a random tag uw0t
    elif not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['st', 'soundtag']:
        return await soundtagmanager.get_random_sound_tag(server_id, voice_channel_id, user_id)
    
    # Get random number
    elif not using_shortcut and num_options == 0 and (num_arguments == 0 or num_arguments == 2):
        public_info = {}
        if not is_private:
            public_info = {'server_id':server_id, 'user_id':user_id}
        if num_arguments == 2:
            return get_random_number(minimum=arguments[0], maximum=arguments[1], is_private=is_private, **public_info)
        else:
            return get_random_number(is_private=is_private, **public_info)

    # Invalid command
    raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string())
예제 #31
0
def get_random_number(minimum=1, maximum=100, is_private=True, **kwargs):
    """Obtains a random number with the specified minimum and maximum values. Same keyword arguments as flip_coin."""
    try:  # Typecheck minimum and maximum values
        minimum = int(minimum)
        maximum = int(maximum)
    except:
        raise bot_exception(EXCEPT_TYPE, "Inputs are not valid integers")
    if (minimum == maximum):
        return "You must think you're really clever."
    if (minimum > maximum):
        minimum, maximum = maximum, minimum
    return "{identifier} got {result}.".format(
        identifier="You" if is_private else usermanager.get_name(
            kwargs['server_id'], kwargs['user_id']),
        result=random.randint(minimum, maximum))
예제 #32
0
async def stop_sounds(server_id, timeout=False):
    """Stops all audio and disconnects the bot from the voice channel."""
    from jshbot.botmanager import client
    from jshbot.botmanager import voice_player
    if voice_player.player and voice_player.player.is_playing():
        if not timeout:
            if voice_player.server_id != server_id:  # Ensure we're stopping the bot on the server it is playing at
                raise bot_exception(EXCEPT_TYPE,
                                    "The bot is not connected to this server")
        voice_player.player.stop()
        if client.voice:
            await client.voice.disconnect()
        voice_player.server_id = None
    elif client.voice.is_connected():
        if not timeout:
            if voice_player.server_id != server_id:  # Ensure we're stopping the bot on the server it is playing at
                raise bot_exception(EXCEPT_TYPE,
                                    "The bot is not connected to this server")
        await client.voice.disconnect()
        if voice_player:
            voice_player.server_id = None
    elif not timeout:
        raise bot_exception(EXCEPT_TYPE, "No sound is playing")
    return "Stopping all sounds and disconnecting..."
예제 #33
0
def get_channel_info(server_id, channel_id):
    """Retrieves a bundle of channel information."""
    if channel_id == 0:  # No voice channel
        raise bot_exception(EXCEPT_TYPE,
                            "You are not in a voice channel on this server")
    return """Channel information for {channel_data[name]}:
```
ID: {channel_id}
Name: {channel_data[name]}
Muted: {channel_data[muted]}
Position: {channel_data[position]}
Voice channel: {channel_data[voice]}
Default: {channel_data[default]}
```""".format(channel_id=channel_id,
              channel_data=servers_data[server_id]['channels'][channel_id],
              config=configmanager.config)
예제 #34
0
def roll_die(faces=6, is_private=True, **kwargs):
    """Rolls a 6 sided die unless specified. Same keyword arguments as flip_coin."""
    try:  # Typecheck faces value
        faces = int(faces)
    except:
        raise bot_exception(EXCEPT_TYPE, "Input is not a valid integer")
    to_return = "You" if is_private else usermanager.get_name(
        kwargs['server_id'], kwargs['user_id'])
    if (faces == 1):
        to_return += " rolled a {}. Just kidding.".format(
            random.randint(1, 2147483647))
    elif (faces <= 0):
        return "Look, I can't roll something that breaks the laws of physics."
    else:  # Valid roll
        to_return += " rolled a {}.".format(random.randint(1, faces))
    return to_return
예제 #35
0
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private):
    """Gets a response from the command and parameters."""
    
    to_return = ''
    num_options = len(options)
    num_arguments = len(arguments)
    using_shortcut = command in commands_dictionary['shortcut_commands']
    
    # User information
    if (command in ['userinfo', 'info', 'user', 'u'] and (num_options == 0 or
            (num_options == 1 and options[0] in ['i', 'info']))):
        if num_arguments == 0: # Self information
            return get_info(server_id, user_id)
        else: # Other user information
            return get_info(server_id, get_user_id(server_id, arguments[0] if num_arguments == 1 else arguments_blocks[0]))

    # User status
    elif ((command in ['status'] and num_options == 0) or
            (not using_shortcut and num_options == 1 and options[0] in ['s', 'status'])):
        if num_arguments == 0: # Self status
            return get_status(server_id, user_id)
        else: # Other user status
            return get_status(server_id, get_user_id(server_id, arguments[0] if num_arguments == 1 else arguments_blocks[0]))

    # User nickname
    elif ((command in ['nickname', 'nick'] and num_options == 0) or
            (not using_shortcut and num_options == 1 and options[0] in ['n', 'nick', 'nickname'])):
        if num_arguments == 0: # Self nickname
            return get_nickname(server_id, user_id)
        else: # Other user nickname
            return get_nickname(server_id, get_user_id(server_id, arguments[0] if num_arguments == 1 else arguments_blocks[0]))

    # Modify status
    elif ((command in ['setstatus'] and num_options == 0) or
            (not using_shortcut and num_options == 1 and options[0] in ['ss', 'setstatus'])):
        if num_arguments == 0: # Clear status
            return set_status(server_id, user_id, '')
        else: # Set status
            return set_status(server_id, user_id, arguments[0] if num_arguments == 1 else arguments_blocks[0])

    # Modify nickname
    elif ((command in ['setnickname', 'setnick'] and num_options == 0) or
            (not using_shortcut and num_options == 1 and options[0] in ['setnickname', 'setnick', 'sn'])):
        if num_arguments == 0: # Clear nickname
            return set_nickname(server_id, user_id, '')
        else: # Set nickname
            return set_nickname(server_id, user_id, arguments[0] if num_arguments == 1 else arguments_blocks[0])
    
    # Set role color
    elif (not using_shortcut and num_options == 1 and options[0] in ['setcolor', 'sc'] and num_arguments <= 1):
        if num_arguments == 0: # Clear nickname
            return await set_color(server_id, user_id, None)
        else: # Set nickname
            return await set_color(server_id, user_id, arguments[0])

    # Refresh color
    elif (not using_shortcut and num_options == 1 and options[0] in ['updatecolor', 'uc'] and num_arguments == 0):
        return await update_color(server_id, user_id)
    
    # Invalid command
    raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string())
예제 #36
0
def update_tag(server_id, tag_name, increment_hits=False, **kwargs):
    """Updates or creates a tag in the server under the given author ID.
    
    Keyword arguments:
    increment_hits -- increments the hits counter of the tag
    user_id -- user trying to modify the tag
    author_id -- author of the tag
    tag_text -- text to go along with the tag
    private -- whether or not the tag can only be called by the author
    hits -- how many times the tag has been called
    full_name -- what the full name (with spaces) is (never passed in)
    """
    to_return = ''
    full_name = tag_name
    tag_name = tag_name.replace(' ', '')
    servers_data = servermanager.servers_data
    if increment_hits:  # Updating hit counter
        servers_data[server_id]['tags'][tag_name]['hits'] += 1
    else:  # Creating or modifying a tag
        try:  # Modify a tag
            tag_data = servers_data[server_id]['tags'][tag_name]
            try:
                check_tag_access(server_id,
                                 tag_data,
                                 tag_name,
                                 kwargs['user_id'],
                                 need_owner=True)
            except KeyError:  # user_id not found, meant to create but tag exists
                raise bot_exception(EXCEPT_TYPE,
                                    "Tag '{}' already exists".format(tag_name))
            del kwargs['user_id']  # Don't write user_id to updated tag
            servers_data[server_id]['tags'][tag_name].update(kwargs)
            to_return += "Tag '{}' successfully modified!".format(full_name)
        except KeyError:  # Tag doesn't exist. Create it.
            if configmanager.config['tags_per_server'] > 0 and len(
                    servers_data[server_id]
                ['tags']) >= configmanager.config['tags_per_server']:
                raise bot_exception(
                    EXCEPT_TYPE,
                    "This server has hit the tag limit of {}".format(
                        configmanager.config['tags_per_server']))
            if 'tag_text' not in kwargs:
                raise bot_exception(EXCEPT_TYPE,
                                    "Tag '{}' does not exist".format(tag_name))
            if len(tag_name) > 50:
                raise bot_exception(
                    EXCEPT_TYPE,
                    "Tag names cannot be larger than 50 characters long")
            if len(kwargs['tag_text']
                   ) > 2000:  # This shouldn't really happen, ever
                raise bot_exception(
                    EXCEPT_TYPE,
                    "Tag text cannot be larger than 2000 characters long")
            # Edit safety
            if 'user_id' in kwargs:
                kwargs['author_id'] = kwargs['user_id']
                del kwargs['user_id']
            kwargs['full_name'] = full_name
            servers_data[server_id]['tags'][tag_name] = {
                **kwargs, 'hits': 0,
                'date_created': time.strftime("%c")
            }
            to_return += "Tag '{}' successfully created!".format(full_name)
    write_data()
    return to_return
예제 #37
0
async def get_response(command, options, arguments, arguments_blocks,
                       raw_parameters, server_id, channel_id, voice_channel_id,
                       user_id, is_admin, is_private):
    """Gets a response from the command and parameters."""

    to_return = ''
    num_options = len(options)
    num_arguments = len(arguments)
    using_shortcut = command in commands_dictionary['shortcut_commands']

    # Flip a coin
    if ((command in ['flip'] and num_options == 0 and num_arguments == 0)
            or (not using_shortcut and num_options == 1 and num_arguments == 0
                and options[0] in ['f', 'flip'])):
        public_info = {}
        if not is_private:  # Bundle server and user ID if we need a name
            public_info = {'server_id': server_id, 'user_id': user_id}
        return flip_coin(is_private=is_private, **public_info)

    # Roll dice
    elif ((command in ['roll'] and num_arguments <= 1)
          or (not using_shortcut and num_options == 1 and num_arguments <= 1
              and options[0] in ['r', 'roll', 'd', 'dice'])):
        public_info = {}
        if not is_private:
            public_info = {'server_id': server_id, 'user_id': user_id}
        return roll_die(faces=arguments[0] if num_arguments == 1 else 6,
                        is_private=is_private,
                        **public_info)

    # Pick a choice
    elif ((command in ['pick'] and num_options == 0 and num_arguments >= 0)
          or (not using_shortcut and num_options == 1 and num_arguments >= 0
              and options[0]
              in ['p', 'pick', 'c', 'choose', 'd', 'decide', 'f', 'fluky'])):
        return pick_choice(
            *arguments)  # Number of arguments is checked in function

    # Get a random tag lolwat
    elif not using_shortcut and num_options == 1 and num_arguments == 0 and options[
            0] in ['t', 'tag']:
        return tagmanager.get_random_tag(server_id, user_id)

    # Get a random tag uw0t
    elif not using_shortcut and num_options == 1 and num_arguments == 0 and options[
            0] in ['st', 'soundtag']:
        return await soundtagmanager.get_random_sound_tag(
            server_id, voice_channel_id, user_id)

    # Get random number
    elif not using_shortcut and num_options == 0 and (num_arguments == 0
                                                      or num_arguments == 2):
        public_info = {}
        if not is_private:
            public_info = {'server_id': server_id, 'user_id': user_id}
        if num_arguments == 2:
            return get_random_number(minimum=arguments[0],
                                     maximum=arguments[1],
                                     is_private=is_private,
                                     **public_info)
        else:
            return get_random_number(is_private=is_private, **public_info)

    # Invalid command
    raise bot_exception(EXCEPT_TYPE, "Invalid syntax",
                        get_formatted_usage_string())
예제 #38
0
def wolfram_alpha_query(query, simple_result=False, results_limit=2):
    """Returns a query result from Wolfram|Alpha, either in full text or just one result."""
    
    if results_limit < 1:
        raise bot_exception(EXCEPT_TYPE, "Invalid number of results (1 to 8)")
    results_limit += 1 # Increment to account for input result, which doesn't really count
    
    query_url = "Query URL: http://www.wolframalpha.com/input/?i={}\n".format(urllib.parse.quote_plus(query))
    to_return = ''
    try:
        query_result = wolframalpha.Client(configmanager.config['wolfram_api_key']).query(query)
    except:
        raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Wolfram|Alpha is not configured for use right now, sorry")
    result_root = query_result.tree.getroot()
    
    # Error handling
    if result_root.get('success') == 'false':
        try: # 
            suggestion = result_root.find('didyoumeans').find('didyoumean').text
        except Exception as e: # TODO: Get proper exception type
            print("Something bad happened to the query:\n" + str(e)) # DEBUG
            raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Wolfram|Alpha could not interpret your query\n{}".format(query_url))
        raise bot_exception(WOLFRAM_ALPHA_EXCEPTION,
            "Wolfram|Alpha could not interpret your query. Trying for first suggestion '{}'...".format(suggestion),
            wolfram_alpha_query(suggestion))
    elif result_root.get('timedout'):
        if len(query_result.pods) == 0:
            raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Query timed out", query_url)
        elif not simple_result:
            to_return += "```\nWarning: query timed out but returned some results:```\n"
    elif len(query_result.pods) == 0:
        raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "No result given (general error)", query_url)
    
    number_of_results = 0
    # Format answer
    if simple_result: # Return a straight, single answer
        if len(list(query_result.results)) > 0:
            to_return += list(query_result.results)[0].text + "\n"
        else: # No explicit 'result' was found
            try:
                to_return += "Closest result:\n{}\n".format(list(query_result.pods)[1].text)
            except IndexError:
                to_return += "No valid result returned. This is a bug! Avert your eyes!"
            except Exception as e: # This shouldn't happen, really
                print("Something bad happened to the query (returning simple result):\n" + str(e)) # DEBUG
                raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Wolfram|Alpha is now dead. Nice work.")
    else: # Full answer, up to 1800 characters long
        for pod in list(query_result.pods):
            for sub_pod in list(pod.node):
                image = sub_pod.find('img')
                if image is not None:
                    to_return += "{pod_title}: {image_url}\n".format(pod_title=pod.__dict__['title'], image_url=image.get('src'))
                    number_of_results += 1
                    if len(to_return) > 1800: # We're getting a very large result. Truncate.
                        to_return += "```\nWarning: truncating very long result here...```\n"
                        break
            if number_of_results >= results_limit:
                break
        to_return += query_url
    
    return to_return
예제 #39
0
def get_sound_tag_data(server_id, sound_tag_name):
    """Ensures that the given sound tag exists and return sound tag data, otherwise throw an exception."""
    try:
        return servermanager.servers_data[server_id]['sound_tags'][sound_tag_name]
    except KeyError:
        raise bot_exception(EXCEPT_TYPE, "Sound tag '{}' doesn't exist".format(sound_tag_name))
예제 #40
0
def update_sound_tag(server_id,
                     sound_tag_name,
                     increment_hits=False,
                     **kwargs):
    """Updates or creates a tag in the server under the given author ID.
    
    Keyword arguments:
    increment_hits -- increments the hits counter of the tag
    user_id -- user trying to modify the tag
    author_id -- author of the tag
    duration -- how long the sound should play for
    url -- url of the audio source
    private -- whether or not the tag can only be called by the author
    hits -- how many times the tag has been called
    type -- tag type (either YouTube or direct)
    length -- how long the sound tag is in seconds
    full_name -- what the full name (with spaces) is
    """
    to_return = ''
    full_name = sound_tag_name
    sound_tag_name = sound_tag_name.replace(' ', '')
    servers_data = servermanager.servers_data
    if increment_hits:  # Updating hit counter
        servers_data[server_id]['sound_tags'][sound_tag_name]['hits'] += 1
    else:  # Creating or modifying a tag
        if kwargs['url'].startswith('https://www.youtube.com/') or kwargs[
                'url'].startswith('https://youtu.be/'):
            kwargs['type'] = 'YouTube'
        else:
            kwargs['type'] = 'direct'

        # Check tag length
        if kwargs['type'] == 'YouTube':  # Find length of YouTube video
            try:
                length = pafy.new(kwargs['url']).length
            except:
                raise bot_exception(EXCEPT_TYPE, "Invalid YouTube video")
        else:  # Download file and check length
            try:
                urllib.request.urlretrieve(
                    kwargs['url'],
                    '{}/tempsound'.format(configmanager.data_directory))
                length = MP3('{}/tempsound'.format(
                    configmanager.data_directory)).info.length
            except:
                raise bot_exception(EXCEPT_TYPE,
                                    "Invalid direct download file or URL")

        length_limit = int(configmanager.config['sound_tag_length_limit'])
        if length_limit > 0 and int(length) > length_limit:
            raise bot_exception(
                EXCEPT_TYPE,
                "Sound tags can be no longer than {} second{}".format(
                    length_limit, 's' if length_limit > 1 else ''))
        kwargs['length'] = int(length)

        try:
            sound_tag_data = servers_data[server_id]['sound_tags'][
                sound_tag_name]
            try:
                check_sound_tag_access(server_id,
                                       sound_tag_data,
                                       kwargs['user_id'],
                                       need_owner=True)
            except KeyError:  # user_id not found, meant to create but tag exists
                raise bot_exception(
                    EXCEPT_TYPE,
                    "Sound tag '{}' already exists".format(sound_tag_name))
            del kwargs['user_id']  # Don't write user_id to updated tag
            servers_data[server_id]['sound_tags'][sound_tag_name].update(
                kwargs)
            to_return += "Sound tag '{}' successfully modified!".format(
                full_name)
        except KeyError:  # Tag doesn't exist. Create it.
            if configmanager.config['sound_tags_per_server'] > 0 and len(
                    servers_data[server_id]['sound_tags']
            ) >= configmanager.config['sound_tags_per_server']:
                raise bot_exception(
                    EXCEPT_TYPE,
                    "This server has hit the sound tag limit of {}".format(
                        configmanager.config['sound_tags_per_server']))
            if 'url' not in kwargs:
                raise bot_exception(
                    EXCEPT_TYPE,
                    "Sound tag '{}' does not exist".format(sound_tag_name))
            if len(sound_tag_name) > 50:
                raise bot_exception(
                    EXCEPT_TYPE,
                    "Sound tag names cannot be larger than 50 characters long")
            if len(kwargs['url']) > 2000:  # This shouldn't really happen, ever
                raise bot_exception(
                    EXCEPT_TYPE,
                    "Sound tag url cannot be larger than 2000 characters long (how did you do this)"
                )
            # Edit safety
            if 'user_id' in kwargs:
                kwargs['author_id'] = kwargs['user_id']
                del kwargs['user_id']
            kwargs['full_name'] = full_name
            servers_data[server_id]['sound_tags'][sound_tag_name] = {
                **kwargs, 'hits': 0,
                'date_created': time.strftime("%c")
            }
            to_return += "Sound tag '{}' successfully created!".format(
                full_name)
    write_data()
    return to_return
예제 #41
0
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private):
    """Gets a response from the command and parameters."""
    
    to_return = ''
    num_options = len(options)
    num_arguments = len(arguments)
    using_shortcut = command in commands_dictionary['shortcut_commands']

    # Play sounds
    if (not using_shortcut and num_options == 0 and num_arguments >= 1):
        try: # For convenience, try with both raw parameters and single argument
            if num_arguments == 1:
                await play_sound_tag(server_id, voice_channel_id, arguments[0].lower().replace(' ', ''), user_id)
                return None;
        except bot_exception:
            pass
        await play_sound_tag(server_id, voice_channel_id, arguments_blocks[0].lower().replace(' ', ''), user_id)
        return None;

    # Create sound tag
    elif (num_arguments == 2 and ((command in ['stc'] and num_options == 0) or
            (not using_shortcut and (num_options == 1 or (num_options == 2 and options[1] in ['p', 'private'])) and
            options[0] in ['c', 'create']))):
        use_private = num_options == 2 and options[1] in ['p', 'private'] # Private tag
        return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), url=arguments[1], author_id=user_id, private=use_private)

    # Remove sound tag
    elif (num_arguments >= 1 and ((command in ['str'] and num_options == 0) or
            (not using_shortcut and num_options == 1 and options[0] in ['r', 'remove']))):
        sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '')
        return remove_sound_tag(server_id, sound_tag_name, user_id)
    
    # List sound tags
    elif ((command in ['stl'] and num_options == 0) or
            (not using_shortcut and num_options == 1 and num_arguments <= 1 and options[0] in ['l', 'list'])):
        return list_sound_tags(server_id, user_id=usermanager.get_user_id(server_id, arguments[0]) if num_arguments == 1 else '')
    
    # Search sound tags
    elif (num_arguments >= 1 and ((command in ['sts'] and num_options == 0) or
            (not using_shortcut and num_options == 1 and options[0] in ['s', 'search']))):
        sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '')
        return search_sound_tags(server_id, sound_tag_name)
        
    # Sound tag info
    elif not using_shortcut and num_options == 1 and num_arguments >= 1 and options[0] in ['i', 'info']:
        sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '')
        return get_sound_info(server_id, sound_tag_name)
    
    # Edit sound tag
    elif not using_shortcut and (num_options in range(1,3)) and num_arguments <= 2 and options[0] in ['e', 'edit']:
        if num_options == 2 and num_arguments == 1: # Set private or public
            if options[1] == 'setpublic': # Explicit to follow a strict syntax
                return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, private=False)
            elif options[1] == 'setprivate':
                return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, private=True)
        elif num_options == 1 and num_arguments == 2: # Modify sound tag url
            return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, url=arguments[1], private=False)
    
    # Stop sounds
    elif ((command in ['stfu'] and num_arguments == 0 and num_options == 0) or
            (not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['stop', 'silence', 'silent', 'fu'])):
        return await stop_sounds(server_id)
    

    # Invalid command
    raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string())
예제 #42
0
async def play_sound_tag(server_id, voice_channel_id, sound_tag_name, user_id):
    """Plays the sound from the given sound tag if it is available."""

    try:
        if servermanager.is_muted(server_id, voice_channel_id):
            raise bot_exception(EXCEPT_TYPE,
                                "The bot is muted in this voice channel")
    except KeyError:
        raise bot_exception(
            EXCEPT_TYPE,
            "You are not in a voice channel (are you perhaps on a different server?)"
        )

    sound_tag_data = get_sound_tag_data(server_id, sound_tag_name)
    check_sound_tag_access(server_id,
                           sound_tag_data,
                           user_id,
                           need_owner=False)
    update_sound_tag(server_id, sound_tag_name,
                     increment_hits=True)  # Increment hits

    from jshbot.botmanager import client
    from jshbot.botmanager import voice_player
    global timeout_goal

    channel = botmanager.get_voice_channel(server_id, voice_channel_id)
    if client.voice == None or client.voice.channel != channel or not client.voice.is_connected(
    ):  # Connect to channel
        if client.voice:  # Disconnect from old channel
            await client.voice.disconnect()
        client.voice = await client.join_voice_channel(channel)
        voice_player.server_id = server_id

    if voice_player.player is not None and voice_player.player.is_playing(
    ):  # Stop if playing
        voice_player.player.stop()

    if sound_tag_data['type'] == 'YouTube':
        # To prevent playlist downloads
        # 'noplaylist': True
        voice_player.player = await client.voice.create_ytdl_player(
            sound_tag_data['url'])
    else:  # Direct download (or stream? Not sure)
        try:
            # One day, I will figure out how to stream this crap. But today is not that day.
            #response = urllib.request.urlopen(sound_tag_data['url'])
            #voice_player.player = client.voice.create_ffmpeg_player(response, use_avconv=True)
            urllib.request.urlretrieve(
                sound_tag_data['url'],
                '{}/tempsound'.format(configmanager.data_directory))
            voice_player.player = client.voice.create_ffmpeg_player(
                '{}/tempsound'.format(configmanager.data_directory))
        except:
            raise bot_exception(
                EXCEPT_TYPE,
                "An error occurred when downloading the sound file")
    voice_player.player.start()

    timeout = configmanager.config['voice_timeout']
    if timeout >= 0:
        timeout_reset_lock.acquire()
        timeout_goal = time.time() + ((timeout * 60) if timeout > 0 else
                                      (sound_tag_data['length'] + 1))
        timeout_reset_lock.release()
        await timeout_disconnect()
예제 #43
0
def update_sound_tag(server_id, sound_tag_name, increment_hits=False, **kwargs):
    """Updates or creates a tag in the server under the given author ID.
    
    Keyword arguments:
    increment_hits -- increments the hits counter of the tag
    user_id -- user trying to modify the tag
    author_id -- author of the tag
    duration -- how long the sound should play for
    url -- url of the audio source
    private -- whether or not the tag can only be called by the author
    hits -- how many times the tag has been called
    type -- tag type (either YouTube or direct)
    length -- how long the sound tag is in seconds
    full_name -- what the full name (with spaces) is
    """
    to_return = ''
    full_name = sound_tag_name
    sound_tag_name = sound_tag_name.replace(' ', '')
    servers_data = servermanager.servers_data
    if increment_hits: # Updating hit counter
        servers_data[server_id]['sound_tags'][sound_tag_name]['hits'] += 1
    else: # Creating or modifying a tag
        if kwargs['url'].startswith('https://www.youtube.com/') or kwargs['url'].startswith('https://youtu.be/'):
            kwargs['type'] = 'YouTube'
        else:
            kwargs['type'] = 'direct'
        
        # Check tag length
        if kwargs['type'] == 'YouTube': # Find length of YouTube video
            try:
                length = pafy.new(kwargs['url']).length
            except:
                raise bot_exception(EXCEPT_TYPE, "Invalid YouTube video")
        else: # Download file and check length
            try:
                urllib.request.urlretrieve (kwargs['url'], '{}/tempsound'.format(configmanager.data_directory))
                length = MP3('{}/tempsound'.format(configmanager.data_directory)).info.length
            except:
                raise bot_exception(EXCEPT_TYPE, "Invalid direct download file or URL")
            
        length_limit = int(configmanager.config['sound_tag_length_limit'])
        if length_limit > 0 and int(length) > length_limit:
            raise bot_exception(EXCEPT_TYPE, "Sound tags can be no longer than {} second{}".format(
                length_limit, 's' if length_limit > 1 else ''))
        kwargs['length'] = int(length)
        
        try:
            sound_tag_data = servers_data[server_id]['sound_tags'][sound_tag_name]
            try:
                check_sound_tag_access(server_id, sound_tag_data, kwargs['user_id'], need_owner=True)
            except KeyError: # user_id not found, meant to create but tag exists
                raise bot_exception(EXCEPT_TYPE, "Sound tag '{}' already exists".format(sound_tag_name))
            del kwargs['user_id'] # Don't write user_id to updated tag
            servers_data[server_id]['sound_tags'][sound_tag_name].update(kwargs)
            to_return += "Sound tag '{}' successfully modified!".format(full_name)
        except KeyError: # Tag doesn't exist. Create it.
            if configmanager.config['sound_tags_per_server'] > 0 and len(servers_data[server_id]['sound_tags']) >= configmanager.config['sound_tags_per_server']:
                raise bot_exception(EXCEPT_TYPE, "This server has hit the sound tag limit of {}".format(configmanager.config['sound_tags_per_server']))
            if 'url' not in kwargs:
                raise bot_exception(EXCEPT_TYPE, "Sound tag '{}' does not exist".format(sound_tag_name))
            if len(sound_tag_name) > 50:
                raise bot_exception(EXCEPT_TYPE, "Sound tag names cannot be larger than 50 characters long")
            if len(kwargs['url']) > 2000: # This shouldn't really happen, ever
                raise bot_exception(EXCEPT_TYPE, "Sound tag url cannot be larger than 2000 characters long (how did you do this)")
            # Edit safety
            if 'user_id' in kwargs:
                kwargs['author_id'] = kwargs['user_id']
                del kwargs['user_id']
            kwargs['full_name'] = full_name
            servers_data[server_id]['sound_tags'][sound_tag_name] = {**kwargs, 'hits':0, 'date_created':time.strftime("%c")}
            to_return += "Sound tag '{}' successfully created!".format(full_name)
    write_data()
    return to_return
예제 #44
0
async def parse(text, server_id, channel_id, author_id, voice_channel_id, is_private=False):
    """Parses the text to separate the command, options, and arguments, and passes them into the apropriate modules."""
    is_admin = servermanager.is_admin(server_id, author_id) if not is_private else False
    text_partition, _, text = text[1:].partition(' ') # Remove invoker character
    text = text.strip()
    raw_parameters = text # Used for some functions (mostly shortcuts)
    command = text_partition.lower()
    option_invoker = configmanager.config['option_invoker']
    
    # Look for options
    options = []
    while text.startswith(option_invoker):
        option_partition, _, text = text[1:].partition(' ')
        text = text.strip()
        if option_partition:
            options.append(option_partition)

    # Look for arguments
    arguments = []
    arguments_blocks = []
    while text:
        if len(arguments_blocks) < 4:
            arguments_blocks.append(text)
        if text.startswith('"'): # Search for end quote
            try:
                text = text[1:]
                closed_quote_index = 0
                next_quote_index = text[closed_quote_index:].index('"') # Maybe use do while?
                while text[closed_quote_index + next_quote_index - 1] == '\\': # Run until proper closed quote found
                    closed_quote_index += next_quote_index
                    text = text[:closed_quote_index - 1] + text[closed_quote_index:]
                    next_quote_index = text[closed_quote_index:].index('"')
            except ValueError:
                raise bot_exception(EXCEPT_TYPE, "An argument has an unclosed quote")
            closed_quote_index += next_quote_index
            if closed_quote_index > 0: # No null argument
                to_append = text[:closed_quote_index].strip()
                if to_append: # No empty string
                    arguments.append(to_append)
                text = text[closed_quote_index + 1:].strip()
            else: # If there is a null argument, remove it
                text = text[2:].strip()
        else: # Search for next space
            argument_partition, _, text = text.partition(' ')
            arguments.append(argument_partition)
            text = text.strip()
    
    # Debug printing
    print("DEBUG:\n{}\n{}\n{}\n{}\n------".format(command, options, arguments, arguments_blocks))
    
    # Get proper response
    to_return = ['', False] # Text response, TTS
    get_response_function = help # Function placeholder
    
    # TODO: See if there is an object representation of modules
    if command in tagmanager.commands: # Tag manager
        get_response_function = getattr(tagmanager, 'get_response')
    elif command in soundtagmanager.commands: # Sound tag manager
        get_response_function = getattr(soundtagmanager, 'get_response')
    elif command in usermanager.commands: # User manager
        get_response_function = getattr(usermanager, 'get_response')
    elif command in decider.commands: # Decider
        get_response_function = getattr(decider, 'get_response')
    elif command in utilities.commands: # Utilities
        get_response_function = getattr(utilities, 'get_response')
    elif command in base_commands: # Base commands
        get_response_function = get_response
    
    if get_response_function != help: # Found a response function
        to_return[0] = await get_response_function(
            command,
            options,
            arguments,
            arguments_blocks,
            raw_parameters,
            server_id,
            channel_id,
            voice_channel_id,
            author_id,
            is_admin,
            is_private)
    
    else:
        to_return[0] += "Sorry, I can't seem to do that right now. You should never see this, by the way. What is most likely going on is that you are hallucinating this error."
        
    if to_return[0] is not None and len(to_return[0]) > 1900: # Split responses up if it is too long
        to_return[0] = "```\n***Looks like the response is very large. It might look a little messed up because it's gettin' chopped up.***```\n" + to_return[0][:1900]
        
    return to_return
예제 #45
0
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private):
    """Responds to the basic commands that are always available."""
    
    to_return = ''
    
    # Base commands (sans admin)
    if command == 'ping':
        to_return += "Pong!\nOptions: {options}\nArguments: {arguments}\nArguments blocks: {arguments_blocks}".format(
            options = str(options),
            arguments = str(arguments),
            arguments_blocks = str(arguments_blocks))
    elif command == 'help':
        if len(arguments) > 1:
            raise bot_exception(BASE_EXCEPT_TYPE, "You can only get help on one command")
        to_return += help(arguments[0] if len(arguments) == 1 else '')
    
    # Admin commands
    elif command == 'admin' and len(options) == 1 and not is_private:
        if not is_admin:
            raise bot_exception(BASE_EXCEPT_TYPE, "You must be an admin or the bot owner for these commands")
        if len(arguments) == 1 and not is_private:
            if options[0] in ['ban', 'unban']: # All checks here are explicit to enforce strict syntax
                to_return += servermanager.add_ban(server_id, usermanager.get_user_id(server_id, arguments[0]), add=(options[0] == 'ban'))
            elif options[0] in ['add', 'remove']:
                if not servermanager.is_owner(user_id):
                    raise bot_exception(BASE_EXCEPT_TYPE, "You must be the bot owner for this command")
                to_return += servermanager.add_admin(server_id, usermanager.get_user_id(server_id, arguments[0]), add=(options[0] == 'add'))
            elif options[0] in ['mute', 'unmute']:
                to_mute = options[0] == 'mute'
                if arguments[0] in ['channel', 'voicechannel']:
                    to_return += servermanager.mute_channel(server_id, channel_id if arguments[0] == 'channel' else voice_channel_id, mute=to_mute)
                elif arguments[0] == 'server':
                    to_return += servermanager.mute_server(server_id, mute=to_mute)
            elif options[0] == 'info':
                if arguments[0] in ['channel', 'voicechannel']:
                    to_return += servermanager.get_channel_info(server_id, channel_id if arguments[0] == 'channel' else voice_channel_id)
                elif arguments[0] == 'server':
                    to_return += servermanager.get_server_info(server_id)
        elif len(arguments) == 0:
            if options[0] == 'version':
                to_return += '**`{version}`** ({date})'.format(version=botmanager.BOT_VERSION, date=botmanager.BOT_DATE)
            elif options[0] == 'ip':
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.connect(('8.8.8.8', 80)) # Thanks Google, you da real MVP
                ret = s.getsockname()[0]
                s.close()
                to_return += "Local IP: " + ret
            elif options[0] == 'halt':
                if not servermanager.is_owner(user_id):
                    raise bot_exception(BASE_EXCEPT_TYPE, "You must be the bot owner for this command")
                print("Halting execution...")
                if not is_private:
                    await botmanager.interrupt_broadcast(server_id, channel_id, "Going down...")
                await botmanager.disconnect_bot()
                await asyncio.sleep(2)
                sys.exit()
            elif options[0] == 'source':
                to_return += random.choice([
                    "It's shit. I'm sorry.", "You want to see what the Matrix is like?",
                    "Script kiddie level stuff in here.", "Beware the lack of PEP 8 guidelines inside!",
                    "Snarky comments inside and out.", "The last codebender. And he's shit at it.",
                    "Years down the road, this will all just be a really embarrassing but funny joke.",
                    "Made with ~~love~~ pure hatred.", "At least he's using version control."])
                to_return += "\nhttps://github.com/TheJsh/JshBot"
            elif options[0] == 'clear':
                to_return += '```\n'
                for i in range(0, 80):
                    to_return += '.\n'
                to_return += random.choice([
                    "Think twice before scrolling up.", "clear ver {}".format(botmanager.BOT_VERSION),
                    "Can you find the one comma?", "Are people watching? If so, best not to scroll up.",
                    "Don't worry, just censorship doing its thing.", "This is why we can't have nice things.",
                    "The only one who can spam is ME.", "That made me feel a bit queasy...",
                    "We need a better content filter. 18+ checks, maybe?", "You ANIMALS. At least I'm not one.",
                    "Scroll up if you want to be on a list.", "I'll bet the NSA will have a fun time scrolling up.",
                    "So much wasted space...", "This is pretty annoying, huh? Well TOO BAD.",
                    "No time to delete!"])
                to_return += '```\n'
            elif options[0] == 'ca':
                await botmanager.change_avatar()
                to_return += "Avatar changed"
            elif options[0] == 'cs':
                await botmanager.change_status()
                to_return += "Status changed"
            elif options[0] == 'uptime':
                uptime_total_seconds = int(time.time()) - botmanager.bot_turned_on_precise
                uptime_struct = time.gmtime(uptime_total_seconds)
                days = int(uptime_total_seconds / 86400)
                hours = uptime_struct.tm_hour
                minutes = uptime_struct.tm_min
                seconds = uptime_struct.tm_sec
                return "The bot has been on since **{initial}**\n{days} days\n{hours} hours\n{minutes} minutes\n{seconds} seconds".format(
                        initial=botmanager.bot_turned_on_date, days=days, hours=hours, minutes=minutes, seconds=seconds)
                
            
    if to_return:
        return to_return
    else: # Invalid command
        raise bot_exception(BASE_EXCEPT_TYPE, "Invalid syntax")
        
    return to_return
예제 #46
0
async def get_response(command, options, arguments, arguments_blocks,
                       raw_parameters, server_id, channel_id, voice_channel_id,
                       user_id, is_admin, is_private):
    """Gets a response from the command and parameters."""

    to_return = ''
    num_options = len(options)
    num_arguments = len(arguments)
    using_shortcut = command in commands_dictionary['shortcut_commands']

    # Play sounds
    if (not using_shortcut and num_options == 0 and num_arguments >= 1):
        try:  # For convenience, try with both raw parameters and single argument
            if num_arguments == 1:
                await play_sound_tag(server_id, voice_channel_id,
                                     arguments[0].lower().replace(' ',
                                                                  ''), user_id)
                return None
        except bot_exception:
            pass
        await play_sound_tag(server_id, voice_channel_id,
                             arguments_blocks[0].lower().replace(' ',
                                                                 ''), user_id)
        return None

    # Create sound tag
    elif (num_arguments == 2
          and ((command in ['stc'] and num_options == 0) or
               (not using_shortcut and
                (num_options == 1 or
                 (num_options == 2 and options[1] in ['p', 'private']))
                and options[0] in ['c', 'create']))):
        use_private = num_options == 2 and options[1] in ['p', 'private'
                                                          ]  # Private tag
        return update_sound_tag(server_id,
                                sound_tag_name=arguments[0].lower(),
                                url=arguments[1],
                                author_id=user_id,
                                private=use_private)

    # Remove sound tag
    elif (num_arguments >= 1 and ((command in ['str'] and num_options == 0) or
                                  (not using_shortcut and num_options == 1
                                   and options[0] in ['r', 'remove']))):
        sound_tag_name = (arguments[0].lower() if num_arguments == 1 else
                          arguments_blocks[0].lower()).replace(' ', '')
        return remove_sound_tag(server_id, sound_tag_name, user_id)

    # List sound tags
    elif ((command in ['stl'] and num_options == 0)
          or (not using_shortcut and num_options == 1 and num_arguments <= 1
              and options[0] in ['l', 'list'])):
        return list_sound_tags(
            server_id,
            user_id=usermanager.get_user_id(server_id, arguments[0])
            if num_arguments == 1 else '')

    # Search sound tags
    elif (num_arguments >= 1 and ((command in ['sts'] and num_options == 0) or
                                  (not using_shortcut and num_options == 1
                                   and options[0] in ['s', 'search']))):
        sound_tag_name = (arguments[0].lower() if num_arguments == 1 else
                          arguments_blocks[0].lower()).replace(' ', '')
        return search_sound_tags(server_id, sound_tag_name)

    # Sound tag info
    elif not using_shortcut and num_options == 1 and num_arguments >= 1 and options[
            0] in ['i', 'info']:
        sound_tag_name = (arguments[0].lower() if num_arguments == 1 else
                          arguments_blocks[0].lower()).replace(' ', '')
        return get_sound_info(server_id, sound_tag_name)

    # Edit sound tag
    elif not using_shortcut and (num_options in range(
            1, 3)) and num_arguments <= 2 and options[0] in ['e', 'edit']:
        if num_options == 2 and num_arguments == 1:  # Set private or public
            if options[1] == 'setpublic':  # Explicit to follow a strict syntax
                return update_sound_tag(server_id,
                                        sound_tag_name=arguments[0].lower(),
                                        user_id=user_id,
                                        private=False)
            elif options[1] == 'setprivate':
                return update_sound_tag(server_id,
                                        sound_tag_name=arguments[0].lower(),
                                        user_id=user_id,
                                        private=True)
        elif num_options == 1 and num_arguments == 2:  # Modify sound tag url
            return update_sound_tag(server_id,
                                    sound_tag_name=arguments[0].lower(),
                                    user_id=user_id,
                                    url=arguments[1],
                                    private=False)

    # Stop sounds
    elif ((command in ['stfu'] and num_arguments == 0 and num_options == 0)
          or (not using_shortcut and num_options == 1 and num_arguments == 0
              and options[0] in ['stop', 'silence', 'silent', 'fu'])):
        return await stop_sounds(server_id)

    # Invalid command
    raise bot_exception(EXCEPT_TYPE, "Invalid syntax",
                        get_formatted_usage_string())