示例#1
0
async def verification_karma(bot, context):
    """Checks if the given user has karma in the r/furry subreddit."""
    verified_role = _get_verified_role(bot, context.guild)
    karma = await _check_reddit_karma(bot, context.arguments[0])
    threshold = configurations.get(bot, __name__, 'karma_threshold')

    # Replace karma amount with limits if it is past that
    limits = [
        configurations.get(bot, __name__, 'submission_karma_limit'),
        configurations.get(bot, __name__, 'comment_karma_limit')
    ]
    zipped = zip(karma, limits)
    karma_strings = [(str(it) if it <= ts else (str(ts) + '+'))
                     for it, ts in zipped]

    if sum(karma) >= threshold:
        response = ':white_check_mark: {} submission karma, {} comment karma'
        qualifies = 'qualifies'
        color = discord.Color(0x77b255)
    else:
        response = ':x: {} submission karma, {} comment karma'
        qualifies = 'needs at least {} combined karma'.format(threshold)
        color = discord.Color(0xdd2e44)

    description = '{0}\n[u/{1}](https://www.reddit.com/user/{1}) {2} for {3}'.format(
        response.format(*karma_strings), context.arguments[0], qualifies,
        verified_role.mention)
    return Response(embed=discord.Embed(description=description, color=color))
示例#2
0
async def _get_status(bot, raised_only=False):
    """Gets the stream status and information."""
    api_url = configurations.get(bot, __name__, 'api_url')
    client_id = configurations.get(bot, __name__, 'client_id')
    if not raised_only:
        try:
            stream_json = (await
                           utilities.future(requests.get,
                                            api_url,
                                            headers={'Client-ID':
                                                     client_id})).text
            stream_dictionary = json.loads(stream_json)
        except Exception as e:
            raise CBException("Failed to retrieve stream data.", e=e)
        stream_data = stream_dictionary['stream']
        status = "Online" if stream_data else "Offline"
        viewers = stream_data['viewers'] if stream_data else 0
    donation_stats = await _get_buffered_donation_stats(bot)
    if raised_only:
        return "**Total raised:** {}".format(total_raised)
    else:
        return ("**Stream:** {0}\n"
                "**Viewers:** {1}\n"
                "**Total raised:** {2}\n"
                "**Total donations:** {3}\n"
                "**Max / Average donation:** {4}").format(
                    status, viewers, *donation_stats)
示例#3
0
async def verification_karma(bot, context):
    """Checks if the given user has karma in the r/furry subreddit."""
    verified_role = _get_verified_role(bot, context.guild)
    karma = await _check_reddit_karma(bot, context.arguments[0])
    threshold = configurations.get(bot, __name__, 'karma_threshold')

    # Replace karma amount with limits if it is past that
    limits = [
        configurations.get(bot, __name__, 'submission_karma_limit'),
        configurations.get(bot, __name__, 'comment_karma_limit')
    ]
    zipped = zip(karma, limits)
    karma_strings = [(str(it) if it <= ts else (str(ts) + '+')) for it, ts in zipped]

    if sum(karma) >= threshold:
        response = ':white_check_mark: {} submission karma, {} comment karma'
        qualifies = 'qualifies'
        color = discord.Color(0x77b255)
    else:
        response = ':x: {} submission karma, {} comment karma'
        qualifies = 'needs at least {} combined karma'.format(threshold)
        color = discord.Color(0xdd2e44)

    description = '{0}\n[u/{1}](https://www.reddit.com/user/{1}) {2} for {3}'.format(
        response.format(*karma_strings), context.arguments[0], qualifies, verified_role.mention)
    return Response(embed=discord.Embed(description=description, color=color))
示例#4
0
async def _get_buffered_donation_stats(bot):
    """Pulls buffered donation information if it is 1 minute old or less."""
    last_pull = data.get(bot, __name__, 'last_pull', volatile=True, default=0)
    buffer_time = configurations.get(bot, __name__, 'stats_buffer_time')
    if time.time() - last_pull > buffer_time:  # Pull information
        data.add(bot, __name__, 'last_pull', time.time(), volatile=True)
        tracker_url = configurations.get(bot, __name__, 'tracker_url')
        try:
            donate_html = (await utilities.future(requests.get,
                                                  tracker_url)).text
            soup = BeautifulSoup(donate_html, 'html.parser')
            donation_text = soup.find('small').text.splitlines()[1:]
            total_raised, total_donations, _unused = donation_text[1].split()
            total_donations = total_donations.strip('()')
            max_average = donation_text[3]
        except Exception as e:
            raise CBException("Failed to retrieve donation data.", e=e)
        donation_stats = [total_raised, total_donations, max_average]
        data.add(bot,
                 __name__,
                 'donation_stats',
                 donation_stats,
                 volatile=True)
    else:
        donation_stats = data.get(bot,
                                  __name__,
                                  'donation_stats',
                                  volatile=True)
    return donation_stats
示例#5
0
async def get_response(bot, context):
    if 'discrank.py' not in bot.plugins:
        raise CBException("Discrank plugin not detected.")
    discrank_plugin = bot.plugins['discrank.py']
    champions, spells = discrank_plugin.CHAMPIONS, discrank_plugin.SPELLS

    chunks = [bot.get_guild(it).emojis for it in configurations.get(bot, __name__, 'guilds')]
    emojis = [it for chunk in chunks for it in chunk]

    final = {
        'champions': {'id': {}, 'name': {}},
        'spells': {'id': {}, 'name': {}},
        'bdt': {'blue': {}, 'red': {}}
    }
    for emoji in emojis:

        if emoji.name.startswith('Champion'):
            clean_name = emoji.name.split('_')[1].lower()
            if clean_name not in champions:
                raise CBException("Champion {} not found.".format(clean_name))
            item_id = champions[clean_name]['id']
            final['champions']['id'][str(item_id)] = str(emoji)
            final['champions']['name'][clean_name] = str(emoji)

        elif emoji.name.startswith('Spell'):
            clean_name = emoji.name.split('_')[1].lower()
            if clean_name not in spells:
                raise CBException("Spell {} not found.".format(clean_name))
            item_id = spells[clean_name]['id']
            final['spells']['id'][str(item_id)] = str(emoji)
            final['spells']['name'][clean_name] = str(emoji)

        elif emoji.name.startswith(('Red', 'Blue')):
            color, name = emoji.name.split('_')
            final['bdt'][color.lower()][name.lower()] = str(emoji)

        else:
            raise CBException("Invalid emoji detected: {}".format(emoji.name))

    final_json = json.dumps(final, sort_keys=True, indent=4)
    json_file = utilities.get_text_as_file(final_json)

    file_url = await utilities.upload_to_discord(
        bot, json_file, filename='lol_emojis.json', close=True)
    embed = discord.Embed(
        description='[Click here to download]({})'.format(file_url),
        colour=discord.Colour(0x4CAF50),
        timestamp=datetime.datetime.utcnow())
    embed.set_footer(text="Updated")

    try:
        update_channel = bot.get_channel(configurations.get(bot, __name__, 'update_channel'))
        message_id = configurations.get(bot, __name__, 'update_message')
        update_message = await update_channel.fetch_message(message_id)
        await update_message.edit(content='', embed=embed)
    except Exception as e:
        raise CBException("Failed to edit the update message.", e=e)

    return Response(content="Updated!")
示例#6
0
async def update_schedule_loop(bot):
    """Constantly updates the schedule data."""
    use_plugin = configurations.get(bot, __name__, key='enable')
    while use_plugin:
        try:
            await _update_schedule(bot)
        except Exception as e:
            logger.warn("Failed to update the GDQ schedule. %s", e)
            await asyncio.sleep(10*60)
        await asyncio.sleep(configurations.get(bot, __name__, key='schedule_refresh_time'))
示例#7
0
async def update_schedule_loop(bot):
    """Constantly updates the schedule data."""
    use_plugin = configurations.get(bot, __name__, key='enable')
    while use_plugin:
        try:
            await _update_schedule(bot)
        except Exception as e:
            logger.warn("Failed to update the GDQ schedule. %s", e)
            await asyncio.sleep(10 * 60)
        await asyncio.sleep(
            configurations.get(bot, __name__, key='schedule_refresh_time'))
示例#8
0
async def setup_globals(bot):
    global statements, substitutions, fine
    statements = configurations.get(bot,
                                    __name__,
                                    extra='statements',
                                    extension='json')
    substitutions = configurations.get(bot,
                                       __name__,
                                       extra='substitutions',
                                       extension='json')
    fine = configurations.get(bot, __name__, 'fine')
示例#9
0
async def get_response(bot, context):

    if context.index == 0:  # Change avatar, status, or both
        if len(context.options) == 0:
            raise CBException(
                "Either the avatar, status, or both options must be used.")
        if 'avatar' in context.options:
            text = configurations.get(bot,
                                      __name__,
                                      extra='avatars',
                                      extension='txt')
            url = random.choice(text.splitlines()).rstrip()
            await _change_avatar(bot, url=url)
        if 'status' in context.options:
            text = configurations.get(bot,
                                      __name__,
                                      extra='statuses',
                                      extension='txt')
            status = random.choice(text.splitlines()).rstrip()
            try:
                await bot.change_presence(activity=discord.Game(name=status))
            except Exception as e:
                raise CBException("Failed to update the status.", e=e)

    elif context.index == 1:  # Change nickname
        try:
            await context.guild.me.edit(nick=context.arguments[0])
        except Exception as e:
            raise CBException("Failed to change the nickname.", e=e)

    elif context.index == 2:  # Change name
        if len(context.arguments[0]) > 20:
            raise CBException("Name is longer than 20 characters.")
        try:
            await bot.user.edit(username=context.arguments[0])
        except Exception as e:
            raise CBException("Failed to update the name.", e=e)

    elif context.index == 3:  # Change status
        try:
            if context.arguments[0]:
                activity = discord.Game(name=context.arguments[0])
            else:
                activity = None
            await bot.change_presence(activity=activity)
            data.add(bot, __name__, 'status', context.arguments[0])
        except Exception as e:
            raise CBException("Failed to update the status.", e=e)

    elif context.index == 4:  # Change avatar
        await _change_avatar(bot, url=context.arguments[0])

    return Response(content="Bot updated.")
示例#10
0
async def setup_globals(bot):
    """Sets up the DATA_CHANNEL global"""
    global DATA_CHANNEL
    DATA_CHANNEL = data.get_channel(bot, configurations.get(bot, __name__, key='data_channel'))
    if not DATA_CHANNEL:
        logger.warn("Failed to find the data channel. Defaulting to the upload channel.")
        DATA_CHANNEL = data.get_channel(bot, configurations.get(bot, 'core', key='upload_channel'))

    # Clear any webhooks (debug)
    webhooks = await DATA_CHANNEL.webhooks()
    for webhook in webhooks:
        logger.debug("Deleting webhook %s", webhook)
        await webhook.delete()
示例#11
0
async def _check_reddit_karma(bot, username):
    """Returns the amount of karma on the r/furry the given user has."""

    verification_period = configurations.get(bot, __name__,
                                             'verification_period')
    time_threshold = time.time() - (verification_period * 86400)

    async def _get_next(generator):
        def _f():
            try:
                while True:
                    check = next(generator)
                    if check.created_utc <= time_threshold:
                        return check
                return next(generator)
            except StopIteration:
                return None
            except NotFound:
                raise CBException_vc("User u/{} not found.".format(username))
            except Exception as e:
                raise CBException_vc("Failed to retrieve data.", e=e)

        return await utilities.future(_f)

    # Submission karma using built-in search
    submission_limit = configurations.get(bot, __name__,
                                          'submission_karma_limit')
    submissions = REDDIT_CLIENT.subreddit('furry').search(
        'author:{}'.format(username))
    submission_karma = 0
    while True:
        submission = await _get_next(submissions)
        submission_karma += submission.score if submission else 0
        if not submission or submission_karma > submission_limit:
            break

    # Comment karma
    comment_limit = configurations.get(bot, __name__, 'comment_karma_limit')
    comments = REDDIT_CLIENT.redditor(username).comments.new(limit=None)
    comment_karma = 0
    while True:
        comment = await _get_next(comments)
        if comment and comment.subreddit.display_name == 'furry':
            comment_karma += comment.score if comment else 0
        if not comment or comment_karma > comment_limit:
            break

    return (submission_karma, comment_karma)
示例#12
0
async def run_query(bot, context):
    results = context.options['results']
    text_result = 'text' in context.options
    query_url, result, warning = await get_query_result(
        bot,
        context.guild,
        context.arguments[0],
        text_result=text_result,
        result_lines=results)
    if configurations.get(bot, __name__, 'ads'):
        location = context.channel if context.direct else context.guild
        advertisement = get_advertisement(bot, location)
    else:
        advertisement = None

    embed = discord.Embed(
        description=advertisement if advertisement else discord.Embed.Empty,
        colour=discord.Colour(0xdd1100))
    embed.set_author(name='Query',
                     url=query_url,
                     icon_url='https://i.imgur.com/mFfV1zk.png')

    if text_result:
        for name, value in result:
            embed.add_field(name=name, value=value, inline=False)
    else:
        embed.set_image(url=result)

    if warning:
        embed.set_footer(text=warning,
                         icon_url='https://i.imgur.com/CGl7njZ.png')

    return Response(embed=embed)
示例#13
0
async def commission_configure(bot, context):
    """Configures the channel and cooldown for the commission channel rules."""
    rules = data.get(bot,
                     __name__,
                     'rules',
                     guild_id=context.guild.id,
                     default={})
    default_cooldown = configurations.get(bot, __name__, 'default_cooldown')
    replace_channel = context.options.get('channel', rules.get('channel'))
    replace_cooldown = context.options.get(
        'cooldown', rules.get('cooldown', default_cooldown))
    if not replace_channel:
        raise CBException("No commission channel configured.")

    # Reset advertisement data
    rules = {'channel': replace_channel, 'cooldown': replace_cooldown}
    data.add(bot, __name__, 'rules', rules, guild_id=context.guild.id)
    data.remove(bot,
                __name__,
                'advertisements',
                guild_id=context.guild.id,
                volatile=True,
                safe=True)
    await _get_advertisement_data(bot, context.guild)

    description = 'Channel: {0.mention}\nCooldown: {1}'.format(
        data.get_channel(bot, replace_channel),
        utilities.get_time_string(replace_cooldown, text=True, full=True))
    embed = discord.Embed(title='Commission channel configuration:',
                          description=description)
    return Response(embed=embed)
示例#14
0
async def run_query(bot, context):
    results = context.options['results']
    text_result = 'text' in context.options
    query_url, result, warning = await get_query_result(
        bot, context.guild, context.arguments[0], text_result=text_result, result_lines=results)
    if configurations.get(bot, __name__, 'ads'):
        location = context.channel if context.direct else context.guild
        advertisement = get_advertisement(bot, location)
    else:
        advertisement = None

    embed = discord.Embed(
        description=advertisement if advertisement else discord.Embed.Empty,
        colour=discord.Colour(0xdd1100))
    embed.set_author(name='Query', url=query_url, icon_url='https://i.imgur.com/mFfV1zk.png')

    if text_result:
        for name, value in result:
            embed.add_field(name=name, value=value, inline=False)
    else:
        embed.set_image(url=result)

    if warning:
        embed.set_footer(text=warning, icon_url='https://i.imgur.com/CGl7njZ.png')

    return Response(embed=embed)
示例#15
0
async def check_warns(bot, message):
    """Checks for issued warnings."""
    if (not message.guild or
            message.guild.id != configurations.get(bot, __name__, 'guild_id')
            or not message.content.lower().startswith('!warn ')
            or not data.is_mod(bot, member=message.author)
            or 'autolog.py' not in bot.plugins):
        return

    split, quotes = parser.split_parameters(message.content,
                                            include_quotes=True,
                                            quote_list=True)
    if len(split) < 3:  # Not enough arguments
        return
    name = split[2][1:-1] if 2 in quotes else split[2]
    member = data.get_member(bot,
                             name,
                             guild=message.guild,
                             safe=True,
                             strict=True)
    if not member or data.is_mod(
            bot, member=member):  # Member not found or is another mod
        return

    details = '{0} (<@{0.id}>) was warned by {1} (<@{1.id}>): {2}'.format(
        member, message.author, ''.join(split[4:]) or "No warn reason given")
    await bot.plugins['autolog.py'].automated_dump_message(
        bot,
        message.guild,
        details,
        query=member.id,
        moderator_id=message.author.id)
示例#16
0
文件: data.py 项目: jkchen2/JshBot
async def add_to_cache(bot, url, name=None, file_location=None):
    """Downloads the URL and saves to the audio cache folder.

    If the cache folder has surpassed the cache size, this will continually
    remove the least used file (by date) until there is enough space. If the
    downloaded file is more than half the size of the total cache, it will not
    be stored. Returns the final location of the downloaded file.

    If name is specified, it will be stored under that name instead of the url.
    If file_location is specified, it will move that file instead of
    downloading the URL.
    """
    if file_location:
        cleaned_name = utilities.get_cleaned_filename(file_location)
    else:
        file_location, cleaned_name = await utilities.download_url(bot, url, include_name=True)
    if name:
        cleaned_name = utilities.get_cleaned_filename(name)
    try:
        download_stat = os.stat(file_location)
    except FileNotFoundError:
        raise CBException("The audio could not be saved. Please try again later.")
    cache_limit = configurations.get(bot, 'core', 'cache_size_limit') * 1000 * 1000
    store = cache_limit > 0 and download_stat.st_size < cache_limit / 2

    # Ensure that the target destination does not exist (remove it if it does)
    if store:
        cached_location = '{0}/audio_cache/{1}'.format(bot.path, cleaned_name)
    else:
        cached_location = '{}/temp/tempsound'.format(bot.path)
    try:
        os.remove(cached_location)
    except:  # Doesn't matter if file doesn't exist
        pass
    os.rename(file_location, cached_location)
    os.utime(cached_location)

    if store:
        cache_entries = []
        total_size = 0
        for entry in os.scandir('{}/audio_cache'.format(bot.path)):
            stat = entry.stat()
            cache_entries.append((stat.st_atime, stat.st_size, entry.path))
            total_size += stat.st_size
        cache_entries.sort(reverse=True)

        while total_size > cache_limit:
            _, size, path = cache_entries.pop()
            logger.info("Removing from cache: %s", path)
            os.remove(path)
            total_size -= size
    else:
        logger.info("Not storing %s. Download size: %s", file_location, download_stat.st_size)

    try:
        download_stat = os.stat(cached_location)
    except FileNotFoundError:
        raise CBException("The file was not moved properly.")
    return cached_location
示例#17
0
async def death_response(bot, message):
    """Adds an emoji reaction to a Minecraft death."""
    configuration = configurations.get(bot, __name__)
    if (message.channel.id == configuration['minecraft_channel_id'] and
            message.author.id == configuration['admin_bot_id'] and
            message.content.startswith('**') and
            message.content.endswith('**')):
        await message.add_reaction(bot.get_emoji(configuration['death_reaction_id']))
示例#18
0
async def set_ip_address(bot, context):
    if context.arguments[0]:
        data.add(bot, __name__, 'server_ip', context.arguments[0], guild_id=context.guild.id)
        response = "IP address set!"
    else:  # Get current IP
        default_ip = configurations.get(bot, __name__, key='default_ip')
        response = "The current IP address is: {}".format(
            data.get(bot, __name__, 'server_ip', guild_id=context.guild.id, default=default_ip))
    return Response(content=response)
示例#19
0
async def create_client(bot):
    """Create a new wolframalpha client object and store in volatile data."""
    config = configurations.get(bot, __name__)
    client = wap.WolframAlphaEngine(config['api_key'], config['server'])
    client.ScanTimeout = config['scan_timeout']
    client.PodTimeout = config['pod_timeout']
    client.FormatTimeout = config['format_timeout']
    data.add(bot, __name__, 'client', client, volatile=True)
    data.add(bot, __name__, 'uses', {}, volatile=True)
示例#20
0
async def death_response(bot, message):
    """Adds an emoji reaction to a Minecraft death."""
    configuration = configurations.get(bot, __name__)
    if (message.channel.id == configuration['minecraft_channel_id']
            and message.author.id == configuration['admin_bot_id']
            and message.content.startswith('**')
            and message.content.endswith('**')):
        await message.add_reaction(
            bot.get_emoji(configuration['death_reaction_id']))
示例#21
0
async def create_client(bot):
    """Create a new wolframalpha client object and store in volatile data."""
    config = configurations.get(bot, __name__)
    client = wap.WolframAlphaEngine(config['api_key'], config['server'])
    client.ScanTimeout = config['scan_timeout']
    client.PodTimeout = config['pod_timeout']
    client.FormatTimeout = config['format_timeout']
    data.add(bot, __name__, 'client', client, volatile=True)
    data.add(bot, __name__, 'uses', {}, volatile=True)
示例#22
0
def _set_logger(bot, channel):
    """Sets a logger for the given channel."""
    default_message_limit = configurations.get(bot, __name__, key='message_limit')
    message_limit = data.get(
        bot, __name__, 'message_limit', guild_id=channel.guild.id, default=default_message_limit)
    logs = data.get(
        bot, __name__, 'logs', guild_id=channel.guild.id,
        default={}, create=True, volatile=True)
    logs[channel.id] = collections.deque(maxlen=message_limit)
示例#23
0
async def _check_reddit_karma(bot, username):
    """Returns the amount of karma on the r/furry the given user has."""

    verification_period = configurations.get(bot, __name__, 'verification_period')
    time_threshold = time.time() - (verification_period * 86400)

    async def _get_next(generator):
        def _f():
            try:
                while True:
                    check = next(generator)
                    if check.created_utc <= time_threshold:
                        return check
                return next(generator)
            except StopIteration:
                return None
            except NotFound:
                raise CBException_vc("User u/{} not found.".format(username))
            except Exception as e:
                raise CBException_vc("Failed to retrieve data.", e=e)
        return await utilities.future(_f)

    # Submission karma using built-in search
    submission_limit = configurations.get(bot, __name__, 'submission_karma_limit')
    submissions = REDDIT_CLIENT.subreddit('furry').search('author:{}'.format(username))
    submission_karma = 0
    while True:
        submission = await _get_next(submissions)
        submission_karma += submission.score if submission else 0
        if not submission or submission_karma > submission_limit:
            break

    # Comment karma
    comment_limit = configurations.get(bot, __name__, 'comment_karma_limit')
    comments = REDDIT_CLIENT.redditor(username).comments.new(limit=None)
    comment_karma = 0
    while True:
        comment = await _get_next(comments)
        if comment and comment.subreddit.display_name == 'furry':
            comment_karma += comment.score if comment else 0
        if not comment or comment_karma > comment_limit:
            break

    return (submission_karma, comment_karma)
示例#24
0
async def create_reddit_client(bot):
    global REDDIT_CLIENT
    credential_data = configurations.get(bot, __name__)
    REDDIT_CLIENT = praw.Reddit(
        client_id=credential_data['reddit_client_id'],
        client_secret=credential_data['reddit_client_secret'],
        user_agent=credential_data['reddit_user_agent'])
    configurations.redact(bot, __name__, 'reddit_client_id')
    configurations.redact(bot, __name__, 'reddit_client_secret')
    configurations.redact(bot, __name__, 'reddit_user_agent')
示例#25
0
async def create_reddit_client(bot):
    global REDDIT_CLIENT
    credential_data = configurations.get(bot, __name__)
    REDDIT_CLIENT = praw.Reddit(
        client_id=credential_data['reddit_client_id'],
        client_secret=credential_data['reddit_client_secret'],
        user_agent=credential_data['reddit_user_agent'])
    configurations.redact(bot, __name__, 'reddit_client_id')
    configurations.redact(bot, __name__, 'reddit_client_secret')
    configurations.redact(bot, __name__, 'reddit_user_agent')
示例#26
0
def _toggle_notification(bot, game, context, use_channel=False):
    """Adds the user or channel to the notifications list."""
    if use_channel:
        destination = 'c{}'.format(context.channel.id)
        destination_text = "This channel"
    else:
        destination = 'u{}'.format(context.author.id)
        destination_text = "You"
    key = game['key']
    game_text = '{} ({})'.format(game['game'], game['type'])
    pending_notification = utilities.get_schedule_entries(
        bot, __name__, search=key, destination=destination)
    if pending_notification:  # Remove from schedule
        utilities.remove_schedule_entries(bot,
                                          __name__,
                                          search=key,
                                          destination=destination)
        return "{} will no longer be notified when {} is about to be streamed.".format(
            destination_text, game_text)
    else:  # Add to schedule
        stream_url = configurations.get(bot, __name__, 'stream_url')
        current_time = datetime.datetime.utcnow()
        start_time, end_time = game['scheduled'], game['end']
        setup_delta = datetime.timedelta(seconds=game['setup_seconds'])

        if current_time < start_time:
            scheduled_seconds = start_time.replace(
                tzinfo=datetime.timezone.utc).timestamp()
            delta = utilities.get_time_string(scheduled_seconds - time.time(),
                                              text=True)
            info = 'GDQ game notification: {}'.format(game_text)
            payload = {
                'text': game_text,
                'end': scheduled_seconds + game['seconds']
            }
            utilities.schedule(bot,
                               __name__,
                               scheduled_seconds,
                               _notify,
                               payload=payload,
                               search=key,
                               destination=destination,
                               info=info)
            return ("{} will be notified when {} is about to be streamed!\n"
                    "(In approximately {})".format(destination_text, game_text,
                                                   delta))

        elif current_time < start_time + setup_delta:
            return "The game is scheduled to start soon!\nWatch it at {}".format(
                stream_url)
        elif current_time < end_time:
            return "The game has already started!\nWatch it at {}".format(
                stream_url)
        else:
            return "Sorry, this game has already been finished."
示例#27
0
def get_advertisement(bot, location):
    """Retrieves an advertisement if one is scheduled."""
    all_uses = data.get(bot, __name__, 'uses', volatile=True)
    ad_uses = configurations.get(bot, __name__, 'ad_uses')
    ad_uses = ad_uses if ad_uses > 0 else 30
    current_uses = all_uses.get(location.id, 0)
    if current_uses >= ad_uses - 1:  # Show advertisement
        if location.id in all_uses:
            del all_uses[location.id]
        content = random.choice((
            "Consider supporting Wolfram|Alpha by trying out Wolfram|Alpha "
            "Pro! It helps keep Wolfram|Alpha free, and provides you with "
            "a much more complete knowledge database experience.",
            "Do you work/study in a STEM field? Wolfram|Alpha Pro can help!",
            "Need help with STEM homework? Wolfram|Alpha Pro has you covered "
            "with step-by-step instructions on how to solve almost any "
            "calculus problems.",
            "Experience professional-grade computational knowledge with "
            "Wolfram|Alpha Pro.",
            "Student or educator in STEM? Wolfram|Alpha brings you the "
            "professional features you need to excel.",
            "Love Wolfram|Alpha? Get more out of your Wolfram|Alpha "
            "experience by going pro!",
            "Need beautifully crafted interactive data visuals? Wolfram|Alpha "
            "Pro can do that for you!",
            "Professional-grade data analysis and visualization can "
            "greatly expedite completing projects and presentations.",
            "Need help with math homework? Get step-by-step solutions for "
            "complexity ranging from arithmetic to calculus and beyind!",
            "Having trouble with learning mathematics? It doesn't matter "
            "if it's algebra or differential equations, Wolfram|Alpha Pro "
            "gives you step-by-step solutions.",
            "Need extra math practice? Wolfram|Alpha Pro can generate an "
            "infinite number of practice problems with step-by-step "
            "solutions to help you ace your exams.",
            "Frequent Wolfram|Alpha user? Tailor your experience for your own "
            "needs with Wolfram|Alpha Pro!",
            "Are your queries timing out? Wolfram|Alpha Pro extends "
            "computation times.",
            "Need powerful visualization and analysis tools for your data? "
            "Wolfram|Alpha Pro is for you!",
            "Directly interact with and download computed data with "
            "Wolfram|Alpha Pro."
            ))
        link = random.choice((
            'See more at', 'For more information, visit',
            'See what upgrading can do at', 'Interested? Check out',
            'Click here for more:', 'Ready to upgrade? See',
            'Curious? Learn more at',
            'Check it out at')) + ' <https://www.wolframalpha.com/pro/>'
        return content + ' ' + link
    else:
        all_uses[location.id] = current_uses + 1
示例#28
0
async def set_units(bot, context):
    default_units = configurations.get(bot, __name__, key='default_units')
    units = data.get(
        bot, __name__, 'server_units', guild_id=context.guild.id, default=default_units)
    if units == 'metric':
        new_units = 'nonmetric'
        response = 'US standard'
    else:
        new_units = 'metric'
        response = 'Metric'
    data.add(bot, __name__, 'server_units', new_units, guild_id=context.guild.id)
    return Response(content=response + ' units are now set as the default.')
示例#29
0
async def _get_buffered_donation_stats(bot):
    """Pulls buffered donation information if it is 1 minute old or less."""
    last_pull = data.get(bot, __name__, 'last_pull', volatile=True, default=0)
    buffer_time = configurations.get(bot, __name__, 'stats_buffer_time')
    if time.time() - last_pull > buffer_time:  # Pull information
        data.add(bot, __name__, 'last_pull', time.time(), volatile=True)
        tracker_url = configurations.get(bot, __name__, 'tracker_url')
        try:
            donate_html = (await utilities.future(requests.get, tracker_url)).text
            soup = BeautifulSoup(donate_html, 'html.parser')
            donation_text = soup.find('small').text.splitlines()[1:]
            total_raised, total_donations, _unused = donation_text[1].split()
            total_donations = total_donations.strip('()')
            max_average = donation_text[3]
        except Exception as e:
            raise CBException("Failed to retrieve donation data.", e=e)
        donation_stats = [total_raised, total_donations, max_average]
        data.add(bot, __name__, 'donation_stats', donation_stats, volatile=True)
    else:
        donation_stats = data.get(bot, __name__, 'donation_stats', volatile=True)
    return donation_stats
示例#30
0
async def autolog_channels(bot, context):
    """Sets the channels that will be logged."""
    log_channel = _check_log_channel(bot, context.guild)

    # Toggle channels for logging
    if context.arguments[0]:
        changes = []
        for channel in context.arguments:
            appended = data.list_data_toggle(
                bot, __name__, 'channels', channel.id, guild_id=context.guild.id)
            if appended:
                changes.append('Now logging {}'.format(channel.mention))
                _set_logger(bot, channel)
            else:
                changes.append('No longer logging {}'.format(channel.mention))
                _delete_logger(bot, channel)
        embed = discord.Embed(title='Logging changes', description='\n'.join(changes))

    # Show channels that are currently logged
    else:
        default_message_limit = configurations.get(bot, __name__, key='message_limit')
        message_limit = data.get(
            bot, __name__, 'message_limit', guild_id=context.guild.id,
            default=default_message_limit)
        logged_channel_ids = data.get(bot, __name__, 'channels', guild_id=context.guild.id)
        if not logged_channel_ids:
            raise CBException("No channels are currently logged.")

        # Check logged channels
        # Removes channels that were deleted and have no logged messages
        logged_channels = []
        logs = data.get(bot, __name__, 'logs', guild_id=context.guild.id, volatile=True)
        for channel_id in logged_channel_ids:
            channel = data.get_channel(bot, channel_id, safe=True)
            if channel:
                logged_channels.append(channel)
            else:
                if len(logs[channel_id]):
                    channel = discord.Object(id=channel_id)
                    channel.mention = 'Deleted channel ({})'.format(channel_id)
                    logged_channels.append(channel)
                else:  # No logged messages for removed channel. Delete log.
                    del logs[channel_id]

        embed = discord.Embed(title='Logging info')
        embed.add_field(
            inline=False, name='Logged channels',
            value=', '.join(it.mention for it in logged_channels))
        embed.add_field(name='Dump channel', value=log_channel.mention)
        embed.add_field(name='Logged messages', value=message_limit)

    return Response(embed=embed)
示例#31
0
def get_advertisement(bot, location):
    """Retrieves an advertisement if one is scheduled."""
    all_uses = data.get(bot, __name__, 'uses', volatile=True)
    ad_uses = configurations.get(bot, __name__, 'ad_uses')
    ad_uses = ad_uses if ad_uses > 0 else 30
    current_uses = all_uses.get(location.id, 0)
    if current_uses >= ad_uses - 1:  # Show advertisement
        if location.id in all_uses:
            del all_uses[location.id]
        content = random.choice((
            "Consider supporting Wolfram|Alpha by trying out Wolfram|Alpha "
            "Pro! It helps keep Wolfram|Alpha free, and provides you with "
            "a much more complete knowledge database experience.",
            "Do you work/study in a STEM field? Wolfram|Alpha Pro can help!",
            "Need help with STEM homework? Wolfram|Alpha Pro has you covered "
            "with step-by-step instructions on how to solve almost any "
            "calculus problems.",
            "Experience professional-grade computational knowledge with "
            "Wolfram|Alpha Pro.",
            "Student or educator in STEM? Wolfram|Alpha brings you the "
            "professional features you need to excel.",
            "Love Wolfram|Alpha? Get more out of your Wolfram|Alpha "
            "experience by going pro!",
            "Need beautifully crafted interactive data visuals? Wolfram|Alpha "
            "Pro can do that for you!",
            "Professional-grade data analysis and visualization can "
            "greatly expedite completing projects and presentations.",
            "Need help with math homework? Get step-by-step solutions for "
            "complexity ranging from arithmetic to calculus and beyind!",
            "Having trouble with learning mathematics? It doesn't matter "
            "if it's algebra or differential equations, Wolfram|Alpha Pro "
            "gives you step-by-step solutions.",
            "Need extra math practice? Wolfram|Alpha Pro can generate an "
            "infinite number of practice problems with step-by-step "
            "solutions to help you ace your exams.",
            "Frequent Wolfram|Alpha user? Tailor your experience for your own "
            "needs with Wolfram|Alpha Pro!",
            "Are your queries timing out? Wolfram|Alpha Pro extends "
            "computation times.",
            "Need powerful visualization and analysis tools for your data? "
            "Wolfram|Alpha Pro is for you!",
            "Directly interact with and download computed data with "
            "Wolfram|Alpha Pro."))
        link = random.choice(
            ('See more at', 'For more information, visit',
             'See what upgrading can do at', 'Interested? Check out',
             'Click here for more:', 'Ready to upgrade? See',
             'Curious? Learn more at',
             'Check it out at')) + ' <https://www.wolframalpha.com/pro/>'
        return content + ' ' + link
    else:
        all_uses[location.id] = current_uses + 1
示例#32
0
async def _get_donation_data(bot):
    """Gets the current donation information, like total raised."""
    tracker_url = configurations.get(bot, __name__, 'tracker_url')
    try:
        donate_html = (await utilities.future(requests.get, tracker_url)).text
        soup = BeautifulSoup(donate_html, 'html.parser')
        donation_text = soup.find('small').text.splitlines()[1:]
        total_raised, total_donations, _unused = donation_text[1].split()
        total_donations = total_donations.strip('()')
        max_average = donation_text[3]
    except Exception as e:
        raise CBException("Failed to retrieve donation data.", e=e)
    return (total_raised, total_donations, max_average)
示例#33
0
async def _get_donation_data(bot):
    """Gets the current donation information, like total raised."""
    tracker_url = configurations.get(bot, __name__, 'tracker_url')
    try:
        donate_html = (await utilities.future(requests.get, tracker_url)).text
        soup = BeautifulSoup(donate_html, 'html.parser')
        donation_text = soup.find('small').text.splitlines()[1:]
        total_raised, total_donations, _unused = donation_text[1].split()
        total_donations = total_donations.strip('()')
        max_average = donation_text[3]
    except Exception as e:
        raise CBException("Failed to retrieve donation data.", e=e)
    return (total_raised, total_donations, max_average)
示例#34
0
async def _get_status(bot, raised_only=False):
    """Gets the stream status and information."""
    api_url = configurations.get(bot, __name__, 'api_url')
    client_id = configurations.get(bot, __name__, 'client_id')
    if not raised_only:
        try:
            stream_json = (await utilities.future(
                requests.get, api_url, headers={'Client-ID': client_id})).text
            stream_dictionary = json.loads(stream_json)
        except Exception as e:
            raise CBException("Failed to retrieve stream data.", e=e)
        stream_data = stream_dictionary['stream']
        status = "Online" if stream_data else "Offline"
        viewers = stream_data['viewers'] if stream_data else 0
    donation_stats = await _get_buffered_donation_stats(bot)
    if raised_only:
        return "**Total raised:** {}".format(total_raised)
    else:
        return (
            "**Stream:** {0}\n"
            "**Viewers:** {1}\n"
            "**Total raised:** {2}\n"
            "**Total donations:** {3}\n"
            "**Max / Average donation:** {4}").format(status, viewers, *donation_stats)
示例#35
0
async def _notify(bot, scheduled_time, payload, search, destination, late, info, id, *args):
    messageable = utilities.get_messageable(bot, destination)
    if 'error' in payload:
        text = payload['error']
    elif time.time() > payload['end']:
        text = (
            "Sorry! I missed the notification for {}. You can "
            "watch the VOD in a few days.").format(payload['text'])
    else:
        stream_url = configurations.get(bot, __name__, 'stream_url')
        notify_message = [
            "Heads up,", "Get ready,", "Good news!", "It's time!",
            "Just so you know,", "Just letting you know,", "Attention,", "Ping!", "Hey,"]
        text = "{} {} is about to be played soon. Watch the speedrun live at {}".format(
            random.choice(notify_message), payload['text'], stream_url)
    await messageable.send(content=text)
示例#36
0
async def set_ip_address(bot, context):
    if context.arguments[0]:
        data.add(bot,
                 __name__,
                 'server_ip',
                 context.arguments[0],
                 guild_id=context.guild.id)
        response = "IP address set!"
    else:  # Get current IP
        default_ip = configurations.get(bot, __name__, key='default_ip')
        response = "The current IP address is: {}".format(
            data.get(bot,
                     __name__,
                     'server_ip',
                     guild_id=context.guild.id,
                     default=default_ip))
    return Response(content=response)
示例#37
0
async def _notify(bot, scheduled_time, payload, search, destination, late):
    messageable = utilities.get_messageable(bot, destination)
    if 'error' in payload:
        text = payload['error']
    elif time.time() > payload['end']:
        text = ("Sorry! I missed the notification for {}. You can "
                "watch the VOD in a few days.").format(payload['text'])
    else:
        stream_url = configurations.get(bot, __name__, 'stream_url')
        notify_message = [
            "Heads up,", "Get ready,", "Good news!", "It's time!",
            "Just so you know,", "Just letting you know,", "Attention,",
            "Ping!", "Hey,"
        ]
        text = "{} {} is about to be played soon. Watch the speedrun live at {}".format(
            random.choice(notify_message), payload['text'], stream_url)
    await messageable.send(content=text)
示例#38
0
async def upload_to_discord(bot, fp, filename=None, rewind=True, close=False):
    """Uploads the given file-like object to the upload channel.

    If the upload channel is specified in the configuration files, files
    will be uploaded there. Otherwise, a new server will be created, and
    used as the upload channel."""
    channel_id = configurations.get(bot, 'core', 'upload_channel')
    if not channel_id:  # Check to see if a server was already created
        channel_id = data.get(bot, 'core', 'upload_channel')
    channel = bot.get_channel(channel_id)

    if channel is None:  # Create server
        logging.debug("Creating server for upload channel...")
        try:
            server = await bot.create_server('uploads')
        except Exception as e:
            raise BotException(
                EXCEPTION,
                "Failed to create upload server. This bot is not whitelisted "
                "to create servers.",
                e=e)
        data.add(bot, 'core', 'upload_channel', server.id)
        channel = bot.get_channel(server.id)

    if channel is None:  # Shouldn't happen
        raise BotException(EXCEPTION, "Failed to get upload channel.")

    try:
        message = await bot.send_file(channel, fp, filename=filename)
        upload_url = message.attachments[0]['url']
    except Exception as e:
        raise BotException(EXCEPTION, "Failed to upload file.", e=e)

    if close:
        try:
            fp.close()
        except:
            pass
    elif rewind:
        try:
            fp.seek(0)
        except:
            pass

    return upload_url
示例#39
0
def _set_logger(bot, channel):
    """Sets a logger for the given channel."""
    default_message_limit = configurations.get(bot,
                                               __name__,
                                               key='message_limit')
    message_limit = data.get(bot,
                             __name__,
                             'message_limit',
                             guild_id=channel.guild.id,
                             default=default_message_limit)
    logs = data.get(bot,
                    __name__,
                    'logs',
                    guild_id=channel.guild.id,
                    default={},
                    create=True,
                    volatile=True)
    logs[channel.id] = collections.deque(maxlen=message_limit)
示例#40
0
async def set_units(bot, context):
    default_units = configurations.get(bot, __name__, key='default_units')
    units = data.get(bot,
                     __name__,
                     'server_units',
                     guild_id=context.guild.id,
                     default=default_units)
    if units == 'metric':
        new_units = 'nonmetric'
        response = 'US standard'
    else:
        new_units = 'metric'
        response = 'Metric'
    data.add(bot,
             __name__,
             'server_units',
             new_units,
             guild_id=context.guild.id)
    return Response(content=response + ' units are now set as the default.')
示例#41
0
def _toggle_notification(bot, game, context, use_channel=False):
    """Adds the user or channel to the notifications list."""
    if use_channel:
        destination = 'c{}'.format(context.channel.id)
        destination_text = "This channel"
    else:
        destination = 'u{}'.format(context.author.id)
        destination_text = "You"
    key = game['key']
    game_text = '{} ({})'.format(game['game'], game['type'])
    pending_notification = utilities.get_schedule_entries(
        bot, __name__, search=key, destination=destination)
    if pending_notification:  # Remove from schedule
        utilities.remove_schedule_entries(bot, __name__, search=key, destination=destination)
        return "{} will no longer be notified when {} is about to be streamed.".format(
            destination_text, game_text)
    else:  # Add to schedule
        stream_url = configurations.get(bot, __name__, 'stream_url')
        current_time = datetime.datetime.utcnow()
        start_time, end_time = game['scheduled'], game['end']
        setup_delta = datetime.timedelta(seconds=game['setup_seconds'])

        if current_time < start_time:
            scheduled_seconds = start_time.replace(tzinfo=datetime.timezone.utc).timestamp()
            delta = utilities.get_time_string(scheduled_seconds - time.time(), text=True)
            info = 'GDQ game notification: {}'.format(game_text)
            payload = {
                'text': game_text,
                'end': scheduled_seconds + game['seconds']}
            utilities.schedule(
                bot, __name__, scheduled_seconds, _notify, payload=payload,
                search=key, destination=destination, info=info)
            return (
                "{} will be notified when {} is about to be streamed!\n"
                "(In approximately {})".format(destination_text, game_text, delta))

        elif current_time < start_time + setup_delta:
            return "The game is scheduled to start soon!\nWatch it at {}".format(stream_url)
        elif current_time < end_time:
            return "The game has already started!\nWatch it at {}".format(stream_url)
        else:
            return "Sorry, this game has already been finished."
示例#42
0
文件: utilities.py 项目: sasma/JshBot
async def upload_to_discord(bot, fp, filename=None, rewind=True, close=False):
    """Uploads the given file-like object to the upload channel.

    If the upload channel is specified in the configuration files, files
    will be uploaded there. Otherwise, a new guild will be created, and
    used as the upload channel."""
    channel_id = configurations.get(bot, 'core', 'upload_channel')
    if not channel_id:  # Check to see if a guild was already created
        channel_id = data.get(bot, 'core', 'upload_channel')
    channel = data.get_channel(bot, channel_id, safe=True)

    if channel is None:  # Create guild
        logger.debug("Creating guild for upload channel...")
        try:
            guild = await bot.create_guild('uploads')
        except Exception as e:
            raise CBException(
                "Failed to create upload guild. This bot is not whitelisted "
                "to create guilds.",
                e=e)
        data.add(bot, 'core', 'upload_channel', guild.id)
        channel = bot.get_channel(guild.id)

    if channel is None:  # Shouldn't happen
        raise CBException("Failed to get upload channel.")

    try:
        discord_file = discord.File(fp, filename=filename)
        message = await channel.send(file=discord_file)
        upload_url = message.attachments[0].url
    except Exception as e:
        raise CBException("Failed to upload file.", e=e)

    try:
        if close:
            fp.close()
        elif rewind:
            fp.seek(0)
    except:
        pass

    return upload_url
示例#43
0
async def upload_to_discord(bot, fp, filename=None, rewind=True, close=False):
    """Uploads the given file-like object to the upload channel.

    If the upload channel is specified in the configuration files, files
    will be uploaded there. Otherwise, a new guild will be created, and
    used as the upload channel."""
    channel_id = configurations.get(bot, 'core', 'upload_channel')
    if not channel_id:  # Check to see if a guild was already created
        channel_id = data.get(bot, 'core', 'upload_channel')
    channel = data.get_channel(bot, channel_id, safe=True)

    # TODO: Remove. Guild creation via bots is a whitelisted process
    if channel is None:  # Create guild
        logger.debug("Creating guild for upload channel...")
        try:
            guild = await bot.create_guild('uploads')
        except Exception as e:
            raise CBException(
                "Failed to create upload guild. This bot is not whitelisted "
                "to create guilds.", e=e)
        data.add(bot, 'core', 'upload_channel', guild.id)
        channel = bot.get_channel(guild.id)

    if channel is None:  # Shouldn't happen
        raise CBException("Failed to get upload channel.")

    try:
        discord_file = discord.File(fp, filename=filename)
        message = await channel.send(file=discord_file)
        upload_url = message.attachments[0].url
    except Exception as e:
        raise CBException("Failed to upload file.", e=e)

    try:
        if close:
            fp.close()
        elif rewind:
            fp.seek(0)
    except:
        pass

    return upload_url
示例#44
0
def _get_tag_dictionary(bot, guild):
    """Retrieves the tag dictionary of the server."""
    if configurations.get(bot, 'tags.py', 'global_tags'):
        table_suffix = 'global'
    else:
        table_suffix = str(guild.id)
    tags_plugin = bot.plugins['tags.py']
    sound_bit = tags_plugin._get_flag_bits(['sound'])
    private_bit = tags_plugin._get_flag_bits(['private'])
    cursor = data.db_select(
        bot, from_arg='tags', table_suffix=table_suffix,
        where_arg='flags & %s = %s AND flags & %s = 0',
        input_args=[sound_bit, sound_bit, private_bit])
    raw_tag_list = cursor.fetchall() if cursor else []
    if not raw_tag_list:
        raise CBException("No sound tags available.")
    tag_dictionary = {}
    for tag in raw_tag_list:
        tag_dictionary[tag.key] = {'name': tag.name, 'hits': tag.hits}
    return tag_dictionary
示例#45
0
async def check_warns(bot, message):
    """Checks for issued warnings."""
    if (not message.guild or
            message.guild.id != configurations.get(bot, __name__, 'guild_id') or
            not message.content.lower().startswith('!warn ') or
            not data.is_mod(bot, member=message.author) or
            'autolog.py' not in bot.plugins):
        return

    split, quotes = parser.split_parameters(message.content, include_quotes=True, quote_list=True)
    if len(split) < 3:  # Not enough arguments
        return
    name = split[2][1:-1] if 2 in quotes else split[2]
    member = data.get_member(bot, name, guild=message.guild, safe=True, strict=True)
    if not member or data.is_mod(bot, member=member):  # Member not found or is another mod
        return

    details = '{0} (<@{0.id}>) was warned by {1} (<@{1.id}>): {2}'.format(
        member, message.author, ''.join(split[4:]) or "No warn reason given")
    await bot.plugins['autolog.py'].automated_dump_message(
        bot, message.guild, details, query=member.id, moderator_id=message.author.id)
示例#46
0
async def verification_check(bot, context):
    """Checks if the given user qualifies for verification by date alone."""
    member = context.arguments[0] or context.author
    verified_role = _get_verified_role(bot, context.guild, member=member)
    verification_period = configurations.get(bot, __name__, 'verification_period')

    # Check that the user has been here for at least the verification period
    age = (datetime.now() - member.joined_at).days
    plural = '' if age == 1 else 's'
    if age >= verification_period:
        response = ':white_check_mark: Member for {} day{}'
        qualifies = 'qualifies'
        color = discord.Color(0x77b255)
    else:
        response = ':x: Member for {} day{}'
        qualifies = 'needs to be a member for at least {} days'.format(verification_period)
        color = discord.Color(0xdd2e44)

    description = '{}\n{} {} for {}'.format(
        response.format(age, plural), member.mention, qualifies, verified_role.mention)
    return Response(embed=discord.Embed(description=description, color=color))
示例#47
0
async def upload_logs(bot):
    """Uploads any log files to the debug channel."""
    log_zip_location = '{0}/temp/debug_log_files.zip'.format(bot.path)
    log_zip_file = zipfile.ZipFile(log_zip_location, mode='w')
    log_location = '{0}/temp/debug_logs.txt'.format(bot.path)
    compression = zipfile.ZIP_DEFLATED
    if os.path.exists(log_location):
        log_zip_file.write(
            log_location, arcname=os.path.basename(log_location),
            compress_type=compression)
    for log_number in range(5):
        next_location = log_location + '.{}'.format(log_number + 1)
        if os.path.exists(next_location):
            log_zip_file.write(
                next_location, arcname=os.path.basename(next_location),
                compress_type=compression)
    log_zip_file.close()

    debug_channel = bot.get_channel(configurations.get(bot, 'core', 'debug_channel'))
    discord_file = discord.File(log_zip_location, filename='all_logs.zip')
    await debug_channel.send(content='All logs:', file=discord_file)
示例#48
0
async def verification_check(bot, context):
    """Checks if the given user qualifies for verification by date alone."""
    member = context.arguments[0] or context.author
    verified_role = _get_verified_role(bot, context.guild, member=member)
    verification_period = configurations.get(bot, __name__,
                                             'verification_period')

    # Check that the user has been here for at least the verification period
    age = (datetime.now() - member.joined_at).days
    plural = '' if age == 1 else 's'
    if age >= verification_period:
        response = ':white_check_mark: Member for {} day{}'
        qualifies = 'qualifies'
        color = discord.Color(0x77b255)
    else:
        response = ':x: Member for {} day{}'
        qualifies = 'needs to be a member for at least {} days'.format(
            verification_period)
        color = discord.Color(0xdd2e44)

    description = '{}\n{} {} for {}'.format(response.format(age, plural),
                                            member.mention, qualifies,
                                            verified_role.mention)
    return Response(embed=discord.Embed(description=description, color=color))
示例#49
0
async def show_edits(bot, before, after):
    if (before.author != bot.user and
            configurations.get(bot, __name__, key='show_edited_messages')):
        logger.info("Somebody edited their message from '{0}' to '{1}'.".format(
            before.content, after.content))
示例#50
0
async def get_query_result(
        bot, guild, query, text_result=False, result_lines=0):
    """Gets a query result and formats it."""
    default_ip = configurations.get(bot, __name__, key='default_ip')
    default_units = configurations.get(bot, __name__, key='default_units')
    if guild is None:
        server_ip = default_ip
        units = default_units
    else:
        server_ip = data.get(
            bot, __name__, 'server_ip',
            guild_id=guild.id, default=default_ip)
        units = data.get(
            bot, __name__, 'server_units',
            guild_id=guild.id, default=default_units)

    indices = ','.join((str(index) for index in range(1, result_lines + 2)))
    format_param = 'plaintext' + ('' if text_result else ',image')
    root = await wolfram_alpha_query(
        bot, query, server_ip, indices=indices, format_param=format_param, units=units)
    pods = root.findall('pod')
    warning = None
    query_url = 'http://www.wolframalpha.com/input/?i={}'.format(urllib.parse.quote_plus(query))

    # Error handling
    if root.get('success') == 'false':

        suggestions = root.find('didyoumeans')
        if suggestions:
            suggestions = suggestions.findall('didyoumean')
            suggestion_text = [suggestion.text for suggestion in suggestions]
        raise CBException(
            "Wolfram|Alpha could not interpret your query.{}".format(
                '' if suggestions is None else ' Suggestion(s): {}'.format(
                    ', '.join(suggestion_text[:3]))))
    elif root.get('timedout'):
        if len(pods) == 0:
            raise CBException("Query timed out.", query_url)
        elif len(pods) < result_lines:
            warning = "Query timed out but returned some results"
    elif len(pods) == 0:
        raise CBException("No result given (general error).", query_url)

    # Format answer
    result_list = []
    if root.find('pod').get('id') != 'Input':
        result_lines -= 1
    if root.find('warnings') is not None:
        spellchecks = root.find('warnings').findall('spellcheck')
        for spellcheck in spellchecks:
            result_list.append(('spellcheck', None, spellcheck.get('text')))
    for pod in root.findall('pod')[:1 + result_lines]:
        for index, sub_pod in enumerate(pod.findall('subpod')):
            image = sub_pod.find('img')
            image_url = '' if image is None else image.get('src')
            text = sub_pod.find('plaintext').text
            title = pod.get('title')
            if index > 0:
                title = None
            result_list.append((title, image_url, text))

    if text_result:
        result = []
        for query_result in result_list:
            text = query_result[2]
            if text:
                if query_result[0] == 'spellcheck':
                    result.append(('Spell check', text))
                elif query_result[0]:
                    result.append((query_result[0], text))
                else:
                    result.append(('\u200b', text))
            else:
                result.append((query_result[0], '[`Image`]({})'.format(query_result[1])))
    else:  # Get the image
        result = await get_result_as_image(bot, result_list)

    return query_url, result, warning
示例#51
0
async def autolog_channels(bot, context):
    """Sets the channels that will be logged."""
    log_channel = _check_log_channel(bot, context.guild)

    # Toggle channels for logging
    if context.arguments[0]:
        changes = []
        for channel in context.arguments:
            appended = data.list_data_toggle(bot,
                                             __name__,
                                             'channels',
                                             channel.id,
                                             guild_id=context.guild.id)
            if appended:
                changes.append('Now logging {}'.format(channel.mention))
                _set_logger(bot, channel)
            else:
                changes.append('No longer logging {}'.format(channel.mention))
                _delete_logger(bot, channel)
        embed = discord.Embed(title='Logging changes',
                              description='\n'.join(changes))

    # Show channels that are currently logged
    else:
        default_message_limit = configurations.get(bot,
                                                   __name__,
                                                   key='message_limit')
        message_limit = data.get(bot,
                                 __name__,
                                 'message_limit',
                                 guild_id=context.guild.id,
                                 default=default_message_limit)
        logged_channel_ids = data.get(bot,
                                      __name__,
                                      'channels',
                                      guild_id=context.guild.id)
        if not logged_channel_ids:
            raise CBException("No channels are currently logged.")

        # Check logged channels
        # Removes channels that were deleted and have no logged messages
        logged_channels = []
        logs = data.get(bot,
                        __name__,
                        'logs',
                        guild_id=context.guild.id,
                        volatile=True)
        for channel_id in logged_channel_ids:
            channel = data.get_channel(bot, channel_id, safe=True)
            if channel:
                logged_channels.append(channel)
            else:
                if len(logs[channel_id]):
                    channel = discord.Object(id=channel_id)
                    channel.mention = 'Deleted channel ({})'.format(channel_id)
                    logged_channels.append(channel)
                else:  # No logged messages for removed channel. Delete log.
                    del logs[channel_id]

        embed = discord.Embed(title='Logging info')
        embed.add_field(inline=False,
                        name='Logged channels',
                        value=', '.join(it.mention for it in logged_channels))
        embed.add_field(name='Dump channel', value=log_channel.mention)
        embed.add_field(name='Logged messages', value=message_limit)

    return Response(embed=embed)
示例#52
0
async def setup_globals(bot):
    global statements, substitutions, fine
    statements = configurations.get(bot, __name__, extra='statements', extension='json')
    substitutions = configurations.get(bot, __name__, extra='substitutions', extension='json')
    fine = configurations.get(bot, __name__, 'fine')
示例#53
0
async def on_message_edit(bot, before, after):
    if (before.author != bot.user
            and configurations.get(bot, __name__, key='show_edited_messages')):
        logger.info(
            "Somebody edited their message from '{0}' to '{1}'.".format(
                before.content, after.content))
示例#54
0
async def get_response(bot, context):
    response = Response()
    use_plugin = configurations.get(bot, __name__, key='enable')
    if not use_plugin:
        response.content = (
            "The GDQ plugin is currently disabled. (GDQ is probably over or hasn't started yet)")
        return response

    embed_template = discord.Embed(
        title='Games Done Quick', url='https://gamesdonequick.com/',
        colour=discord.Colour(0x00aff0),
        description='\[ [Stream]({}) ] \[ [Schedule]({}) ] \[ [Donate]({}) ]'.format(
            configurations.get(bot, __name__, 'stream_url'),
            configurations.get(bot, __name__, 'schedule_url'),
            configurations.get(bot, __name__, 'donate_url')))
    embed_template.set_thumbnail(url='http://i.imgur.com/GcdqhUR.png')
    guild_id = context.guild.id if context.guild else None
    if context.index == 0:
        embed_template.add_field(name='Donation stats', value='Loading...', inline=False)
        response.game_index = data.get(bot, __name__, 'current_index', volatile=True, default=0)
        schedule_data = data.get(bot, __name__, 'schedule', volatile=True)
        games_list = schedule_data[response.game_index:response.game_index + 5]
        game_data = _embed_games_information(bot, games_list, guild_id)
        value = '\n\n'.join('**{}**\n{}'.format(*it) for it in game_data)
        embed_template.add_field(name='Schedule', value=value, inline=False)
        response.update_stats = True
        response.update_task = None
        response.message_type = MessageTypes.INTERACTIVE
        response.extra_function = gdq_menu
        response.extra = {'buttons': ['⬅', '⏺', '➡']}

    elif context.index == 1:  # About
        embed_template.add_field(name='About', value=(
            "Games Done Quick (GDQ) is a week-long charity gaming marathon that "
            "brings together speedrunners from around the globe to raise money on a "
            "livestream. They are currently supporting {0}, and all donations go "
            "directly to the charity.\n\nCheck out the links above for the Twitch "
            "stream, games schedule, and the donation portal!").format(
                configurations.get(bot, __name__, 'charity')))

    elif context.index == 2:  # Status
        status_text = await _get_status(bot)
        embed_template.add_field(name='Status', value=status_text, inline=False)

    elif context.index == 3:  # Current game
        embed_data = _get_current_game(bot, guild_id)[0]
        embed_template.add_field(name=embed_data[0], value=embed_data[1], inline=False)

    elif context.index == 4:  # Next game(s)
        embed_data = _get_next_games(bot, context.arguments[0], guild_id)
        for name, value in embed_data:
            embed_template.add_field(name=name, value=value, inline=False)

    elif context.index == 5:  # Search
        embed_data = _search_games(bot, context.arguments[0], guild_id=guild_id)
        embed_template.add_field(name=embed_data[0], value=embed_data[1], inline=False)

    elif context.index == 6:  # Notify
        game = _search_games(bot, context.arguments[0], return_game=True)
        response.content = _toggle_notification(
            bot, game, context, use_channel='channel' in context.options)
        embed_template = None

    response.embed = embed_template
    return response
示例#55
0
async def _update_schedule(bot):
    """Reads the GDQ schedule and updates the information in the database."""
    schedule_url = configurations.get(bot, __name__, 'schedule_url')
    html_data = (await utilities.future(requests.get, schedule_url)).text
    soup = BeautifulSoup(html_data, 'html.parser')
    run_table = soup.find('table', {'id': 'runTable'})
    schedule_data = []
    game_list = []

    if run_table is None:
        raise CBException('Run table not found!')

    debug_weeks = data.get(bot, __name__, 'debug_weeks', default=0, volatile=True)
    current_data = {}
    for entry in run_table.find_all('tr'):
        entry_class = entry.get('class', [''])[0]

        if entry_class == 'day-split':
            continue

        subentries = [subentry.text for subentry in entry.find_all('td')]
        if subentries[0].startswith(' '):  # Second column
            subentries = subentries[:2]
        if entry_class == 'second-row':  # Extra data for the last game
            estimation, run_type = subentries
            split_estimate = estimation.split(':')
            estimation_seconds = (int(split_estimate[0])*3600 +
                                  int(split_estimate[1])*60 +
                                  int(split_estimate[2]))
            end_time = (
                current_data['scheduled'] + datetime.timedelta(
                    seconds=(estimation_seconds + current_data['setup_seconds'])))
            key_name = utilities.get_cleaned_filename(
                current_data['game'] + run_type, cleaner=True)
            current_data.update({
                'estimation': estimation.strip(),
                'seconds': estimation_seconds,
                'type': run_type,
                'end': end_time,
                'key': key_name
            })
            game_list.append(key_name)
            schedule_data.append(current_data)

        else:  # Happens first
            while len(subentries) < 4:
                subentries.append('')
            start_time_string, game, runners, setup_time = subentries
            start_time = datetime.datetime.strptime(start_time_string, '%Y-%m-%dT%H:%M:%SZ')
            setup_time = setup_time.strip()
            split_setup = setup_time.split(':')
            if len(split_setup) > 1:
                setup_seconds = (int(split_setup[0])*3600 +
                                 int(split_setup[1])*60 +
                                 int(split_setup[2]))
            else:
                setup_seconds = 0
            current_data = {
                'scheduled': start_time - datetime.timedelta(weeks=debug_weeks),
                'game': game,
                'runners': runners,
                'setup': setup_time,
                'setup_seconds': setup_seconds
            }

    # Add finale entry
    run_type = 'Party%'
    end_time = current_data['scheduled'] + datetime.timedelta(minutes=30)
    key_name = utilities.get_cleaned_filename(current_data['game'] + run_type, cleaner=True)
    current_data.update({
        'estimation': '0:30:00',
        'seconds': 60*30,
        'end': end_time,
        'type': run_type,
        'key': key_name
    })
    game_list.append(key_name)
    schedule_data.append(current_data)

    # Update scheduled notifications
    entries = utilities.get_schedule_entries(bot, __name__)
    for entry in entries:
        payload, key = entry[3:5]
        if key not in game_list:  # Not found error
            error_message = (
                ":warning: Warning: The game {} has been removed, renamed, or "
                "recategorized. You have been removed from the notification list "
                "for this game. Please check the schedule at {}.".format(
                    payload['text'], configurations.get(bot, __name__, 'schedule_url')))
            utilities.update_schedule_entries(
                bot, __name__, search=key, payload={'error': error_message}, new_time=time.time())
        else:
            game = schedule_data[game_list.index(key)]
            start_time, end_time = game['scheduled'], game['end']
            setup_delta = datetime.timedelta(seconds=game['setup_seconds'])
            scheduled = start_time.replace(tzinfo=datetime.timezone.utc).timestamp()
            current_time = datetime.datetime.utcnow()
            if start_time + setup_delta < current_time < end_time:
                stream_url = configurations.get(bot, __name__, 'stream_url')
                payload = {'error': (
                        "Uh oh. The schedule shifted drastically and I didn't notice "
                        "fast enough - sorry! {} is live right now at {}").format(
                            payload['text'], stream_url)}
            else:
                payload.update({'end': scheduled + game['seconds']})
            utilities.update_schedule_entries(
                bot, __name__, search=key, payload=payload, new_time=scheduled)

    # Save data
    data.add(bot, __name__, 'schedule', schedule_data, volatile=True)
    try:
        _update_current_game(bot)
    except:
        pass
示例#56
0
async def get_response(bot, context):
    if 'discrank.py' not in bot.plugins:
        raise CBException("Discrank plugin not detected.")
    discrank_plugin = bot.plugins['discrank.py']
    champions, spells = discrank_plugin.CHAMPIONS, discrank_plugin.SPELLS

    chunks = [
        bot.get_guild(it).emojis
        for it in configurations.get(bot, __name__, 'guilds')
    ]
    emojis = [it for chunk in chunks for it in chunk]

    final = {
        'champions': {
            'id': {},
            'name': {}
        },
        'spells': {
            'id': {},
            'name': {}
        },
        'bdt': {
            'blue': {},
            'red': {}
        }
    }
    for emoji in emojis:

        if emoji.name.startswith('Champion'):
            clean_name = emoji.name.split('_')[1].lower()
            if clean_name not in champions:
                raise CBException("Champion {} not found.".format(clean_name))
            item_id = champions[clean_name]['id']
            final['champions']['id'][str(item_id)] = str(emoji)
            final['champions']['name'][clean_name] = str(emoji)

        elif emoji.name.startswith('Spell'):
            clean_name = emoji.name.split('_')[1].lower()
            if clean_name not in spells:
                raise CBException("Spell {} not found.".format(clean_name))
            item_id = spells[clean_name]['id']
            final['spells']['id'][str(item_id)] = str(emoji)
            final['spells']['name'][clean_name] = str(emoji)

        elif emoji.name.startswith(('Red', 'Blue')):
            color, name = emoji.name.split('_')
            final['bdt'][color.lower()][name.lower()] = str(emoji)

        else:
            raise CBException("Invalid emoji detected: {}".format(emoji.name))

    final_json = json.dumps(final, sort_keys=True, indent=4)
    json_file = utilities.get_text_as_file(final_json)

    file_url = await utilities.upload_to_discord(bot,
                                                 json_file,
                                                 filename='lol_emojis.json',
                                                 close=True)
    embed = discord.Embed(
        description='[Click here to download]({})'.format(file_url),
        colour=discord.Colour(0x4CAF50),
        timestamp=datetime.datetime.utcnow())
    embed.set_footer(text="Updated")

    try:
        update_channel = bot.get_channel(
            configurations.get(bot, __name__, 'update_channel'))
        message_id = configurations.get(bot, __name__, 'update_message')
        update_message = await update_channel.get_message(message_id)
        await update_message.edit(content='', embed=embed)
    except Exception as e:
        raise CBException("Failed to edit the update message.", e=e)

    return Response(content="Updated!")
示例#57
0
async def _update_schedule(bot):
    """Reads the GDQ schedule and updates the information in the database."""
    schedule_url = configurations.get(bot, __name__, 'schedule_url')
    html_data = (await utilities.future(requests.get, schedule_url)).text
    soup = BeautifulSoup(html_data, 'html.parser')
    run_table = soup.find('table', {'id': 'runTable'})
    schedule_data = []
    game_list = []

    if run_table is None:
        raise CBException('Run table not found!')

    debug_weeks = data.get(bot,
                           __name__,
                           'debug_weeks',
                           default=0,
                           volatile=True)
    current_data = {}
    for entry in run_table.find_all('tr'):
        entry_class = entry.get('class', [''])[0]

        if entry_class == 'day-split':
            continue

        subentries = [subentry.text for subentry in entry.find_all('td')]
        if entry_class == 'second-row':  # Extra data for the last game
            estimation, run_type = subentries
            split_estimate = estimation.split(':')
            estimation_seconds = (int(split_estimate[0]) * 3600 +
                                  int(split_estimate[1]) * 60 +
                                  int(split_estimate[2]))
            end_time = (current_data['scheduled'] + datetime.timedelta(
                seconds=(estimation_seconds + current_data['setup_seconds'])))
            key_name = utilities.get_cleaned_filename(current_data['game'] +
                                                      run_type,
                                                      cleaner=True)
            current_data.update({
                'estimation': estimation.strip(),
                'seconds': estimation_seconds,
                'type': run_type,
                'end': end_time,
                'key': key_name
            })
            game_list.append(key_name)
            schedule_data.append(current_data)

        else:  # Happens first
            while len(subentries) < 4:
                subentries.append('')
            start_time_string, game, runners, setup_time = subentries
            start_time = datetime.datetime.strptime(start_time_string,
                                                    '%Y-%m-%dT%H:%M:%SZ')
            setup_time = setup_time.strip()
            split_setup = setup_time.split(':')
            if len(split_setup) > 1:
                setup_seconds = (int(split_setup[0]) * 3600 +
                                 int(split_setup[1]) * 60 +
                                 int(split_setup[2]))
            else:
                setup_seconds = 0
            current_data = {
                'scheduled':
                start_time - datetime.timedelta(weeks=debug_weeks),
                'game': game,
                'runners': runners,
                'setup': setup_time,
                'setup_seconds': setup_seconds
            }

    # Add finale entry
    run_type = 'Party%'
    end_time = current_data['scheduled'] + datetime.timedelta(minutes=30)
    key_name = utilities.get_cleaned_filename(current_data['game'] + run_type,
                                              cleaner=True)
    current_data.update({
        'estimation': '0:30:00',
        'seconds': 60 * 30,
        'end': end_time,
        'type': run_type,
        'key': key_name
    })
    game_list.append(key_name)
    schedule_data.append(current_data)

    # Update scheduled notifications
    entries = utilities.get_schedule_entries(bot, __name__)
    for entry in entries:
        payload, key = entry[3:5]
        if key not in game_list:  # Not found error
            error_message = (
                ":warning: Warning: The game {} has been removed, renamed, or "
                "recategorized. You have been removed from the notification list "
                "for this game. Please check the schedule at {}.".format(
                    payload['text'],
                    configurations.get(bot, __name__, 'schedule_url')))
            utilities.update_schedule_entries(bot,
                                              __name__,
                                              search=key,
                                              payload={'error': error_message},
                                              time=time.time())
        else:
            game = schedule_data[game_list.index(key)]
            start_time, end_time = game['scheduled'], game['end']
            setup_delta = datetime.timedelta(seconds=game['setup_seconds'])
            scheduled = start_time.replace(
                tzinfo=datetime.timezone.utc).timestamp()
            current_time = datetime.datetime.utcnow()
            if start_time + setup_delta < current_time < end_time:
                stream_url = configurations.get(bot, __name__, 'stream_url')
                payload = {
                    'error':
                    ("Uh oh. The schedule shifted drastically and I didn't notice "
                     "fast enough - sorry! {} is live right now at {}").format(
                         payload['text'], stream_url)
                }
            else:
                payload.update({'end': scheduled + game['seconds']})
            utilities.update_schedule_entries(bot,
                                              __name__,
                                              search=key,
                                              payload=payload,
                                              time=scheduled)

    # Save data
    data.add(bot, __name__, 'schedule', schedule_data, volatile=True)
    try:
        _update_current_game(bot)
    except:
        pass
示例#58
0
async def get_response(bot, context):
    response = Response()
    use_plugin = configurations.get(bot, __name__, key='enable')
    if not use_plugin:
        response.content = (
            "The GDQ plugin is currently disabled. (GDQ is probably over or hasn't started yet)"
        )
        return response

    embed_template = discord.Embed(
        title='Games Done Quick',
        url='https://gamesdonequick.com/',
        colour=discord.Colour(0x00aff0),
        description='\[ [Stream]({}) ] \[ [Schedule]({}) ] \[ [Donate]({}) ]'.
        format(configurations.get(bot, __name__, 'stream_url'),
               configurations.get(bot, __name__, 'schedule_url'),
               configurations.get(bot, __name__, 'donate_url')))
    embed_template.set_thumbnail(url='http://i.imgur.com/GcdqhUR.png')
    guild_id = context.guild.id if context.guild else None
    if context.index == 0:
        embed_template.add_field(name='Donation stats',
                                 value='Loading...',
                                 inline=False)
        response.game_index = data.get(bot,
                                       __name__,
                                       'current_index',
                                       volatile=True,
                                       default=0)
        schedule_data = data.get(bot, __name__, 'schedule', volatile=True)
        games_list = schedule_data[response.game_index:response.game_index + 5]
        game_data = _embed_games_information(bot, games_list, guild_id)
        value = '\n\n'.join('**{}**\n{}'.format(*it) for it in game_data)
        embed_template.add_field(name='Schedule', value=value, inline=False)
        response.update_stats = True
        response.update_task = None
        response.message_type = MessageTypes.INTERACTIVE
        response.extra_function = gdq_menu
        response.extra = {'buttons': ['⬅', '⏺', '➡']}

    elif context.index == 1:  # About
        embed_template.add_field(
            name='About',
            value=
            ("Games Done Quick (GDQ) is a week-long charity gaming marathon that "
             "brings together speedrunners from around the globe to raise money on a "
             "livestream. They are currently supporting {0}, and all donations go "
             "directly to the charity.\n\nCheck out the links above for the Twitch "
             "stream, games schedule, and the donation portal!").format(
                 configurations.get(bot, __name__, 'charity')))

    elif context.index == 2:  # Status
        status_text = await _get_status(bot)
        embed_template.add_field(name='Status',
                                 value=status_text,
                                 inline=False)

    elif context.index == 3:  # Current game
        embed_data = _get_current_game(bot, guild_id)[0]
        embed_template.add_field(name=embed_data[0],
                                 value=embed_data[1],
                                 inline=False)

    elif context.index == 4:  # Next game(s)
        embed_data = _get_next_games(bot, context.arguments[0], guild_id)
        for name, value in embed_data:
            embed_template.add_field(name=name, value=value, inline=False)

    elif context.index == 5:  # Search
        embed_data = _search_games(bot,
                                   context.arguments[0],
                                   guild_id=guild_id)
        embed_template.add_field(name=embed_data[0],
                                 value=embed_data[1],
                                 inline=False)

    elif context.index == 6:  # Notify
        game = _search_games(bot, context.arguments[0], return_game=True)
        response.content = _toggle_notification(bot,
                                                game,
                                                context,
                                                use_channel='channel'
                                                in context.options)
        embed_template = None

    response.embed = embed_template
    return response