예제 #1
0
def precheck(user, villager):
        '''Precheck steps before processing requests.'''
        data_dict = utils.open_requestlog()
        # Precheck will fail when either one of these conditions is met:
        # - The user requested this villager before, so this is a duplicated check.
        # - The user has too many requests. See the default in REQUEST_LIMIT=1.
        message = ''
        villagers = []
        last_rejected = []
        for request_id, details in list(data_dict.items()):
            if details['name'] == str(user) and (details['villager'] == villager):
                message = 'You have requested a duplicated villager, check your application with ~status.'
                return message
            for k, v in list(details.items()):
                if (str(k) == 'name' and user in v and (details['status'] not in (
                        utils.Status.CLOSED.name, utils.Status.CANCEL.name, utils.Status.REJECTED.name))):
                    villagers.append(str(details['villager']))
                # if (str(k) == 'name' and user in v and (details['status']
        # Use re.match to search pattern here.
        pattern = re.compile(str(villager).lower())
        for v in villagers:
            if pattern.search(v):
                message = 'You requested **%s** before, please use *"~status"*'
                message += ' command to get previous application ID.'
                return message % villager
        if len(villagers) >= REQUEST_LIMIT:
            message = 'You hit the max application number (%d) per user.'
            message += '\nPlease work with the Villager Adoption Team to '
            message += 'fulfill your current application first.'
            return message % REQUEST_LIMIT
예제 #2
0
    async def cancel(self, ctx, req_id=None):
        '''Cancel an application.'''
        # Check if the requester matches, or a staff can cancel.
        data_dict = utils.open_requestlog()

        # Setup a Flow controller.
        flow = request.Flow(self.bot, ctx)
        found = False
        found_data = dict()
        # Since we only allow 1 application per user per time, do a quick search if
        # req_id = none and there is an open application of this user.
        if not req_id:
            req_id = self.auto_find(data_dict, ctx.message.author.name)
        for request_id, details in list(data_dict.items()):
            if str(request_id) == str(req_id):
                # user can only cancel their own request.
                for k, v in list(details.items()):
                    if k == u'name' and ctx.message.author.name in v:
                        message = 'You are about to cancel an application **%s**' % req_id
                        em = utils.get_embed('red',
                                             message,
                                             title='Cancel An Application')
                        await ctx.channel.send(embed=em)
                        found = True
                        details['status'] = utils.Status.CANCEL.name
                        # mark a last_modified timestring
                        tm = time.localtime(time.time())
                        timestring = time.strftime(TIME_FORMAT, tm)
                        details['last_modified'] = timestring
                        found_data[request_id] = details
                        break
        if not found:
            message = 'Your application %s was not found.' % req_id
            em = utils.get_embed('red', message, title='Application Not Found')
            await ctx.channel.send(embed=em)
            return

        are_you_sure = await ctx.send(
            f":information_source: Please confirm YES/NO to cancel"
            f" this application **%s**" % request_id)

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)
        if reaction is None:
            return
        if not reaction:
            em = utils.get_embed('red', 'Aborted.')
            return await ctx.channel.send(embed=em)
        elif reaction:
            utils.flush_requestlog(data_dict)
            await ctx.send(f"You\'ve cancelled this application.")
            await self.send_logs_user('Cancelled application %s by %s' %
                                      (req_id, ctx.message.author.name))
            await sheet.update_data(found_data)
            # Then hide the close row.
            await sheet.archive_column(req_id)
예제 #3
0
 async def archive(self, ctx, req_id, *input_args):
     '''Archive and hide a row in the sheet by an application ID.'''
     data_dict = utils.open_requestlog()
     toggle = list(input_args)
     if not toggle:
         # always hide
         await sheet.archive_column(req_id)
     else:
         # unhide
         await sheet.archive_column(req_id, False)
     server_msg = 'DreamieBot archived a row of request_id %s in the sheet.'
     # send to log channel
     return await self.send_logs(server_msg % (req_id))
예제 #4
0
    async def found(self, ctx, req_id):
        '''Indicates that you have found a requested villager.'''
        data_dict = utils.open_requestlog()
        user_id = 0
        staff = ctx.message.author.name
        staff += '#' + ctx.message.author.discriminator
        staff_obj = self.bot.get_user(ctx.message.author.id)
        dreamie = ''
        found_data = dict()
        for request_id, details in list(data_dict.items()):
            if str(request_id) == str(req_id):
                message = 'A villager of this application **%s** was found by %s!'
                await ctx.send(message % (req_id, staff))
                details['status'] = utils.Status.FOUND.name
                dreamie = details['villager']
                # only get the name
                dreamie = dreamie.split(',')[0]
                # mark a last_modified timestring
                tm = time.localtime(time.time())
                timestring = time.strftime(TIME_FORMAT, tm)
                details['last_modified'] = timestring
                details['staff'] = staff
                user_id = details['user_id']
                found_data[request_id] = details

        utils.flush_requestlog(data_dict)
        time.sleep(1)
        await sheet.update_data(found_data)
        # send to log channel
        await self.send_logs('%s found a requested villager: %s' %
                             (staff, dreamie))
        # send a DM to note the user.
        if user_id:
            user = self.bot.get_user(user_id)
            dm_chan = user.dm_channel or await user.create_dm()
            message = ('%s your dreamie %s has been found.\nYou have 72hrs'
                       ' to get an open plot ready and use the **~ready** '
                       'command to notify us. You will be contacted by '
                       '%s to coordinate the following steps during your '
                       'selected timeslot.\nRequest Id: %s\n\n'
                       'If you do not get ready in time, this application '
                       'will expire automatically. Your villager will not '
                       'be held for you and will be passed to the next '
                       'applicant.\n')
            await dm_chan.send(message % (user.mention, dreamie, staff, req_id)
                               )
예제 #5
0
 async def list(self, ctx, req_id=None):
     '''List all applications, or a single one by its ID.'''
     # Retrieve data_dict from the request log file.
     data_dict = utils.open_requestlog()
     found = ""
     for request_id, details in list(data_dict.items()):
         if not req_id or str(request_id).lower() == str(req_id).lower():
             # all requests.
             found += 'application_id: %s\n' % request_id
             found += utils.printadict(details)
             found += '\n'
     if found:
         time.sleep(1)
         pg_data = utils.paginate(found)
         embed = discord.Embed(title='List Applications')
         embed.color = utils.random_color()
         for message in pg_data:
             embed.description = ''.join(message)
             await ctx.send(embed=embed)
예제 #6
0
 async def claim(self, ctx, req_id):
     '''Claim an application to move its status to PROCESSING.'''
     data_dict = utils.open_requestlog()
     user_id = 0
     staff = ctx.message.author.name
     staff += '#' + ctx.message.author.discriminator
     staff_obj = self.bot.get_user(ctx.message.author.id)
     found_data = dict()
     for request_id, details in list(data_dict.items()):
         if str(request_id) == str(req_id):
             villager = details['villager'].split(',')[0]
             details['status'] = utils.Status.PROCESSING.name
             # mark a last_modified timestring
             tm = time.localtime(time.time())
             timestring = time.strftime(TIME_FORMAT, tm)
             details['last_modified'] = timestring
             details['staff'] = staff
             found_data[request_id] = details
             user_id = details['user_id']
             name = details['name']
             break
     # Send a note back to the staff.
     staff_msg = ('You claimed an application: %s (%s looks for %s).')
     await ctx.send(staff_msg % (req_id, name, villager))
     utils.flush_requestlog(data_dict)
     time.sleep(1)
     await sheet.update_data(found_data)
     # send to log channel
     await self.send_logs('%s claim an application: %s' % (staff, req_id))
     # send a DM to note the user.
     if user_id:
         user = self.bot.get_user(user_id)
         dm_chan = user.dm_channel or await user.create_dm()
         user_msg = (
             'Thank you for applying to the Dream Villager Adoption Program.'
             ' We have approved your application and a staff member of our '
             'team, _%s_, has begun looking for your dream villager.\n'
             'Please monitor your DMs for updates. You can use **~status** '
             'command to check the latest status.\n'
             'If you have any questions, please check the FAQ in '
             '_#villager-adoption-program, or ask questions in '
             '#villager-trading_, or ping us **@adoptionteam**.')
         await dm_chan.send(user_msg % staff)
예제 #7
0
    async def close(self, ctx, req_id):
        '''Close an application and pop some firework.'''
        data_dict = utils.open_requestlog()
        user_id = 0
        staff = ctx.message.author.name
        staff += '#' + ctx.message.author.discriminator
        staff_obj = self.bot.get_user(ctx.message.author.id)
        villager = None
        found_data = dict()
        for request_id, details in list(data_dict.items()):
            if str(request_id) == str(req_id):
                message = 'Congrats! Application **%s** is now closed by %s!'
                villager = details['villager'].split(',')[0]
                await ctx.send(message % (req_id, staff))
                details['status'] = utils.Status.CLOSED.name
                # mark a last_modified timestring
                tm = time.localtime(time.time())
                timestring = time.strftime(TIME_FORMAT, tm)
                details['last_modified'] = timestring
                details['staff'] = staff
                user_id = details['user_id']
                found_data[request_id] = details

        utils.flush_requestlog(data_dict)
        time.sleep(1)
        await sheet.update_data(found_data)
        # Then hide the close row.
        await sheet.archive_column(req_id)

        # send to log channel
        await self.send_logs('%s closed an application: %s' % (staff, req_id))
        # send a DM to note the user.
        if user_id:
            user = self.bot.get_user(user_id)
            dm_chan = user.dm_channel or await user.create_dm()
            message = 'Congrats, %s! You have found your dreamie, %s.\n'
            message += 'Your application **%s** is closed by a staff (_%s_).\n'
            message += 'After you\'ve settled down, please consider showing '
            message += 'appreciation in the #Player-Feedback channel!'
            await dm_chan.send(message %
                               (user.mention, villager, req_id, staff))
예제 #8
0
 async def status(self, ctx):
     '''Check the status of a user's application.'''
     # Retrieve data_dict from the request log file.
     data_dict = utils.open_requestlog()
     found = ""
     for request_id, details in list(data_dict.items()):
         for k, v in list(details.items()):
             if k == u'name' and ctx.message.author.name in v:
                 # also hide closed requests with status == 'cancel',
                 # 'rejected' or 'closed'
                 if details['status'] not in (utils.Status.CLOSED.name,
                                              utils.Status.CANCEL.name,
                                              utils.Status.REJECTED.name):
                     found += 'application_id: **%s**\n' % request_id
                     found += utils.printadict(details, hide_self=True)
                     found += '\n'
     if found:
         await ctx.send("Found your application:")
         color = utils.status_color(details)
         em = utils.get_embed(color, found)
         await ctx.channel.send(embed=em)
     else:
         em = utils.get_embed('red', "You don\'t have any application.")
         await ctx.channel.send(embed=em)
예제 #9
0
    async def apply(self, ctx, villager=None):
        '''Apply to request a villager and get an <application ID> in return.'''

        # Setup a Flow controller.
        flow = request.Flow(self.bot, ctx)
        # Find is_bot_locked from Staff cog.
        staff_cog = self.bot.get_cog('Staff')
        if staff_cog.is_bot_locked:
            text = 'I am sorry but the application queue is now locked and '
            text += 'cannot accept any application.\nPlease stay tuned in the '
            text += '#villager-adoption-program channel for future updates.'
            em = utils.get_embed('gold', text)
            return await ctx.channel.send(embed=em)
        if not villager:
            text = 'Greetings! Which villager would you like to request?\n'
            text += 'Use `~apply <villager>` command to create an application.'
            em = utils.get_embed('gray', text)
            return await ctx.channel.send(embed=em)
        # Before starting anything, check if this applicant was rejected in
        # the last two weeks.
        data_dict = utils.open_requestlog()
        rejected = []
        for request_id, details in list(data_dict.items()):
            for k, v in list(details.items()):
                if k == u'name' and ctx.message.author.name in v:
                    # also hide requests with status == 'cancel' or 'closed'
                    if details['status'] in (utils.Status.REJECTED.name):
                        rejected.append(details)
        current = datetime.datetime.utcnow()
        for detail in rejected:
            # Fixed the period to 2 weeks.
            then_ts = time.strptime(detail['last_modified'], TIME_FORMAT)
            then_dt = datetime.datetime.fromtimestamp(time.mktime(then_ts))
            period = datetime.timedelta(days=14)
            if (current - then_dt) < period:
                rejected_msg = (
                    'Sorry, your previous application was closed at '
                    '%s within a two weeks cooldown.\nPlease come back'
                    ' and reapply later.' % detail['last_modified'])
                em = utils.get_embed('red',
                                     rejected_msg,
                                     title='Still In Cooldown')
                await self.send_logs_user('%s attempted to re-apply a dreamie '
                                          'while in a 2 weeks cooldown.' %
                                          detail['name'])
                return await ctx.channel.send(embed=em)
        # Viallger name differeniation: some villagers have a space char
        # between its names:
        v = villager.lower()
        if v is not 'kidd' and v in ('kid', 'agent', 'big', 'wart'):
            if v == 'kid':
                villager = 'Kid Cat'
                villager_link = 'https://villagerdb.com/villager/kid-cat'
            if v == 'agent':
                villager = 'Agent S'
                villager_link = 'https://villagerdb.com/villager/agent-s'
            if v == 'big':
                villager = 'Big Top'
                villager_link = 'https://villagerdb.com/villager/big-top'
            if v == 'wart':
                villager = 'Wart Jr.'
                villager_link = 'https://villagerdb.com/villager/wart-jr'
        else:
            # Form a villager link
            villager_link = 'https://villagerdb.com/villager/{}'.format(
                villager.lower())
            villager = villager.capitalize()
        # Validate the villager's name before everything.
        result = checks.validate_name(villager)
        if result:
            return await ctx.send(result)
        villager_data = "{}, {}".format(villager, villager_link)

        request_id = utils.generate_id(data_dict)  # ctx.message.id
        name = ctx.message.author.name
        name += '#' + ctx.message.author.discriminator
        message = checks.precheck(name, villager)
        if message:
            em = utils.get_embed('red', message, title='Precheck Failed')
            return await ctx.channel.send(embed=em)

        tm = time.localtime(time.time())
        timestring = time.strftime(TIME_FORMAT, tm)
        # init data and data_dict
        # data might be redundant because I use data_dict mostly.
        data = ""
        data_dict = dict()
        data = u"Request_Id: **{}**\n".format(request_id)
        details = dict()
        data += u"Name: {}\n".format(name)
        details['name'] = name
        details['user_id'] = ctx.message.author.id
        data += 'Villager: {}\n'.format(villager_data)
        details['villager'] = villager_data
        data += u"CreatedTime: {}\n".format(timestring)
        details['created_time'] = timestring

        em = utils.get_embed('gray', 'Your Application Details:')
        await ctx.channel.send(embed=em)
        await ctx.channel.send(utils.printadict(details, hide_self=True))
        time.sleep(1)
        # default null for these two. Added after showing details to users,
        # so they won't know.
        details['last_modified'] = ''
        details['staff'] = ''
        # Flow control.
        tt_or_not = await ctx.send(
            f":information_source: Are you willing to do Time Travel in order to "
            f"make the process quicker?")
        tt_reaction = await flow.get_yes_no_reaction_confirm(tt_or_not, 200)
        if tt_reaction is None:
            return ctx.send(
                "Application cancelled. You may apply again at any time.")
        if not tt_reaction:
            details['can_time_travel'] = False
            # Only accept non-tter with an open plot in 72 hours to apply.
            within_72_hr = await ctx.send(
                f":question: Will you have an open plot with 72 hours? ")
            reaction = await flow.get_yes_no_reaction_confirm(
                within_72_hr, 200)
            if reaction is None:
                return ctx.send(
                    "Application cancelled. You may apply again when an open plot is ready."
                )
            if not reaction:
                rejected_text = (
                    "We’re sorry, your application cannot be accepted at this time.\n"
                    "Please apply again when you are within a 72 hour window "
                    "of an open plot.")
                em = utils.get_embed('red',
                                     rejected_text,
                                     title="Application Denied")
                return await ctx.channel.send(embed=em)
        else:
            details['can_time_travel'] = True

        # Available timeslot selection
        timeslot = await ctx.send(
            f":information_source: Which time slot is the best choice to contact you"
            " if we have to? \n"
            "NOTE: Please refer to https://www.thetimezoneconverter.com/ or https://time.is/UTC\n"
            " and select your most available timeslot in **UTC**.\n"
            ":sparkles: Slot 1: 00:00 - 03:59 UTC.\n:eight_spoked_asterisk: "
            "Slot 2: 04:00 - 07:59 UTC.\n"
            ":heart: Slot 3: 08:00 - 11:59 UTC.\n:rocket: Slot 4: 12:00 - 15:59 UTC.\n"
            ":crescent_moon: Slot 5: 16:00 - 19:59 UTC.\n:full_moon: Slot 6: "
            "20:00 - 23:59 UTC.\n")
        time.sleep(1)
        slot_reaction = await flow.get_timeslot_reaction_confirm(timeslot, 600)
        if slot_reaction is None:
            return ctx.send(
                "Application cancelled. You may apply again at any time.")
        if slot_reaction == 1:
            details['avail_time'] = '00:00-03:59 UTC'
        if slot_reaction == 2:
            details['avail_time'] = '04:00-07:59 UTC'
        if slot_reaction == 3:
            details['avail_time'] = '08:00-11:59 UTC'
        if slot_reaction == 4:
            details['avail_time'] = '12:00-15:59 UTC'
        if slot_reaction == 5:
            details['avail_time'] = '16:00-19:59 UTC'
        if slot_reaction == 6:
            details['avail_time'] = '20:00-23:59 UTC'

        # Final confirmation
        are_you_sure = await ctx.send(
            f":question: Please confirm YES/NO to create a new "
            f"application of finding **%s**." % villager)

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)
        if reaction is None:
            return ctx.send(
                "Application cancelled. You may apply again at any time.")
        if not reaction:
            return await ctx.send("Aborted your application.")
        elif reaction:
            user_obj = self.bot.get_user(ctx.message.author.id)
            await ctx.send("This application has been logged for review.")
            await ctx.send(
                "%s Please take a note of your application ID:\n**%s**" %
                (user_obj.mention, request_id))
            time.sleep(1)
            # Add a default status.
            data += u"Status: {}".format(utils.Status.PENDING.name)
            details['status'] = utils.Status.PENDING.name

            # data_dict is keyed by request_id, and its value contains the rest details as a dictionary.
            data_dict[request_id] = details
            # Open request log file and append to the end.
            with open(RECORD_FILE, mode="a") as f:
                json.dump(data_dict, f, indent=None)
                f.write('\n')
            await sheet.update_data(data_dict)
            await self.send_logs_user('%s requested a dreamie (%s) at %s' %
                                      (name, villager.capitalize(), timestring)
                                      )
예제 #10
0
    async def ready(self, ctx, req_id=None):
        '''Send a note to the staff team when you are ready to accept a new villager.'''
        # Check if the requester matches.
        data_dict = utils.open_requestlog()
        dreamie = None
        found = None
        found_data = dict()

        # Setup a Flow controller.
        flow = request.Flow(self.bot, ctx)
        # Since we only allow 1 application per user per time, do a quick search if
        # req_id = none and there is an open application of this user.
        if not req_id:
            req_id = self.auto_find(data_dict, ctx.message.author.name)
        for request_id, details in list(data_dict.items()):
            if str(request_id) == str(req_id):
                # user can only mark their own request.
                for k, v in list(details.items()):
                    if k == u'name' and ctx.message.author.name in v:
                        found = (
                            '%s has marked the application **%s** as ready to '
                            'accept the dreamie!' %
                            (ctx.message.author.name, req_id))
                        details['status'] = utils.Status.READY.name
                        dreamie = details['villager']
                        dreamie = dreamie.split(',')[0]
                        # mark a last_modified timestring
                        tm = time.localtime(time.time())
                        timestring = time.strftime(TIME_FORMAT, tm)
                        details['last_modified'] = timestring
                        found_data[request_id] = details
                        break

        if not found:
            em = utils.get_embed('red',
                                 'Cannot found your application %s.' % req_id)
            return await ctx.channel.send(embed=em)

        are_you_sure = await ctx.send(
            f":information_source: Please confirm YES/NO to indicate"
            f" that you have an open plot ready to receive your dreamie.\n"
            f"Application ID:**%s**" % req_id)
        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return
        if not reaction:
            em = utils.get_embed('red', 'Aborted.')
            return await ctx.channel.send(embed=em)
        elif reaction:
            color = utils.status_color(details)
            em = utils.get_embed(color, found)
            await ctx.channel.send(embed=em)
            utils.flush_requestlog(data_dict)
            user_obj = self.bot.get_user(found_data[req_id]['user_id'])
            await self.send_logs_user('%s is ready to accept a dreamie (%s).' %
                                      (found_data[req_id]['name'], dreamie))
            await sheet.update_data(found_data)
            staff_lst = found_data[req_id]['staff'].split('#')
            staff_id = discord.utils.get(self.bot.get_all_members(),
                                         name=staff_lst[0],
                                         discriminator=staff_lst[1]).id
            staff_obj = self.bot.get_user(staff_id)
            staff_dm = staff_obj.dm_channel or await staff_obj.create_dm()
            staff_msg = '%s: %s is ready to accept a dreamie (%s).' % (
                staff_obj.mention, user_obj.mention, dreamie)
            await staff_dm.send(staff_msg)
            """
예제 #11
0
 async def review(self, ctx, req_id, denied=None):
     '''Review an application. The result is either approved or denied.'''
     data_dict = utils.open_requestlog()
     user_id = 0
     staff = None
     found = False
     found_data = dict()
     rejected = dict()
     for request_id, details in list(data_dict.items()):
         if str(request_id) == str(req_id):
             found = True
             staff = ctx.message.author.name
             staff += '#' + ctx.message.author.discriminator
             staff_obj = self.bot.get_user(ctx.message.author.id)
             user_id = details['user_id']
             if str(denied) == 'denied':
                 # The message is sent back to a user.
                 message = ':disappointed_relieved: Thank you for submitting an application for '
                 message += 'your dream villager! Unfortunately, your application has been '
                 message += 'rejected after being reviewed by the Beyond Stalks Villager Adoption Team.'
                 message += '\n\nThe reason for your application\'s rejection is inactivity within '
                 message += 'the server community. Our adoption program is designed to be a reward '
                 message += 'for consistently active and positive contributers to the server. '
                 message += 'Please strive to meet these conditions and we will look forward to '
                 message += 'a follow-up application from you in due time. You may submit a new '
                 message += 'application in **2 weeks**.\n'
                 message += '\nYour application has been closed.\n\n'
                 message += 'Thanks!\nThe Villager Adoption Team'
                 # A server message for reference.
                 server_message = '%s denied an application: %s' % (staff,
                                                                    req_id)
                 details['status'] = utils.Status.REJECTED.name
                 # mark a last_modified timestring
                 tm = time.localtime(time.time())
                 timestring = time.strftime(TIME_FORMAT, tm)
                 details['last_modified'] = timestring
                 details['staff'] = staff
                 rejected[request_id] = details
                 # send to log channel
                 await self.send_logs(server_message)
                 # send a DM to note the user.
                 if user_id:
                     user = self.bot.get_user(user_id)
                     dm_chan = user.dm_channel or await user.create_dm()
                     await dm_chan.send(message)
             else:
                 message = 'Application **%s** is now approved by %s!'
                 await ctx.send(message % (req_id, staff))
                 details['status'] = utils.Status.APPROVED.name
                 # mark a last_modified timestring
                 tm = time.localtime(time.time())
                 timestring = time.strftime(TIME_FORMAT, tm)
                 details['last_modified'] = timestring
                 details['staff'] = staff
                 # send to log channel
                 await self.send_logs('%s approved an application: %s' %
                                      (staff, req_id))
                 # send a DM to note the user.
                 if user_id:
                     user = self.bot.get_user(user_id)
                     dm_chan = user.dm_channel or await user.create_dm()
                     user_msg = ('Your application **{}** is approved by a '
                                 'staff (_{}_).\nPlease use the `~status` '
                                 'command to check current status.'.format(
                                     req_id, staff))
                     await dm_chan.send(user_msg)
             # Save changes to found_data
             found_data[request_id] = details
     if not found:
         message = 'Cannot find application **%s**' % req_id
         await ctx.send(message)
         # send to log channel
         return await self.send_logs(message)
     # Write changes back.
     utils.flush_requestlog(data_dict)
     time.sleep(1)
     await sheet.update_data(found_data)
     if rejected:
         for k, _ in rejected.items():
             await sheet.archive_column(k)
예제 #12
0
 async def search(self, ctx, *input_args):
     '''Search for a summary, status or name in all applications.'''
     data_dict = utils.open_requestlog()
     user_id = 0
     # Since this function is shared with a background task.
     # When ctx = None, it will not send the result messages to the user,
     # instead, it returns a dictionary.
     all_args = [a.lower() for a in list(input_args)]
     all_status = [t.name for t in utils.Status]
     tmp_dict = dict()
     target = all_args[0]
     if target == 'summary':
         total = len(data_dict)
         # We don't care about closed, cancel or rejected applications.
         for status in all_status:  # expected_status:
             tmp_dict[status] = 0
             for _, details in list(data_dict.items()):
                 if status == details['status']:
                     tmp_dict[status] += 1
             if tmp_dict[status]:
                 ratio = '{} ({:.3f}{})'.format(
                     tmp_dict[status], (tmp_dict[status] / total * 100),
                     '%')
                 tmp_dict[status] = ratio
             else:
                 tmp_dict[status] = '0 (------)'
         if not ctx:
             return tmp_dict
         embed = discord.Embed()
         embed.title = 'Applications Summary:'
         embed.color = utils.random_color()
         embed.description = 'Total Applications: %d' % total
         for k, v in tmp_dict.items():
             embed.add_field(name=k, value=v, inline=True)
         return await ctx.send(embed=embed)
     elif target.upper() in all_status:
         # Search by status
         target = target.upper()
         tmp_dict = dict()
         for request_id, details in list(data_dict.items()):
             if target == details['status']:
                 villager = details['villager'].split(', ')[0]
                 tmp_dict[request_id] = '**{}** looks for _{}_'.format(
                     details['name'], villager)
         # Prepare for background reporting task.
         if not ctx and 'background' in all_args:
             return tmp_dict
         if not ctx and 'countdown' in all_args:
             # For coutndown background tasks, we need to return a raw
             # details dict.
             raw_dict = dict()
             for request_id, details in list(data_dict.items()):
                 if target == details['status']:
                     raw_dict[request_id] = details
             return raw_dict
         title = '_%s_ Application' % target.capitalize()
         if len(tmp_dict) > 1:
             title += 's: *%d*' % len(tmp_dict)
         else:
             title += ': *%d*' % len(tmp_dict)
         if tmp_dict:
             embed = discord.Embed(title=title)
             embed.color = utils.random_color()
             for k, v in tmp_dict.items():
                 embed.add_field(name=k, value=v, inline=True)
             return await ctx.send(embed=embed)
         else:
             return await ctx.send('Found nothing for status=%s' % target)
     else:
         # Search by user, could be multiple names.
         # fix the guild if nothing found.
         # 704875142496649256 is Beyond Stalks.
         # guild_id = ctx.message.guild if ctx.message.guild else '704875142496649256'
         tmp_list = []
         for target in all_args:
             title = 'Search By Name: %s' % target
             found = dict()
             # user_id = discord.utils.get(client.get_all_members(), name=name_list[0],
             #                            discriminator=name_list[1]).id
             # pattern = re.search(target, details['name'], re.IGNORECASE)
             for request_id, details in list(data_dict.items()):
                 if re.search(target, details['name'], re.IGNORECASE):
                     villager = details['villager'].split(', ')[0]
                     status = 'Status: {}'.format(
                         details['status'].capitalize())
                     found[request_id] = '**{}**\n{}'.format(
                         villager, status)
             if found:
                 embed = discord.Embed(title=title)
                 embed.color = utils.random_color()
                 for k, v in found.items():
                     embed.add_field(name=k, value=v, inline=True)
                 return await ctx.send(embed=embed)
             else:
                 return await ctx.send('Found nothing for this name: %s' %
                                       target)
예제 #13
0
 async def monitoring(self):
     '''Monitoring applications and report it back to #adoption-team's channel.'''
     # For testing, lets just spam foxfair.
     user = self.bot.get_user(self.bot.owner_id)
     dm_chan = user.dm_channel or await user.create_dm()
     # Send message to #bot-logs channel.
     # chan = self.bot.get_channel(auth_config.SEND_MSG_CHANNELS[0])
     staff_cog = self.bot.get_cog('Staff')
     for status in ('pending', 'found', 'approved', 'ready', 'processing'):
         report = await staff_cog.search(None, status, 'background')
         title = '_%s_ Application' % status.capitalize()
         if len(report) > 1:
             title += 's: *%d*' % len(report)
         else:
             title += ': *%d*' % len(report)
         if report:
             embed = discord.Embed(title=title)
             embed.color = utils.random_color()
             for k, v in report.items():
                 embed.add_field(name=k, value=v, inline=True)
             # Save to self.last_status, and only report different/new info.
             # "if status not in self.last_status" means this is a new status.
             # "if report != self.last_status[status]" means data of this status
             # has changed.
             if status not in self.last_status or report != self.last_status[
                     status]:
                 self.last_status[status] = report
                 await dm_chan.send(embed=embed)
                 # await chan.send(embed=embed)
     # To guarantee the first task will monitor this 'countdown' task.
     self.loop_counter += 1
     if (self.loop_counter % 4) == 0:
         status = 'ready'
         report = await staff_cog.search(None, status, 'countdown')
         if report:
             for req_id, details in report.items():
                 # TODO: test only.
                 # if 'foxfair' not in details['name']:
                 #    break
                 current = datetime.datetime.utcnow()
                 for exp_min in [360, 180, 60]:
                     app_user = self.bot.get_user(details['user_id'])
                     # Prepare to send a DM to remind the applicant
                     reminder_chan = app_user.dm_channel or await app_user.create_dm(
                     )
                     then_ts = time.strptime(details['last_modified'],
                                             TIME_FORMAT)
                     then_dt = datetime.datetime.fromtimestamp(
                         time.mktime(then_ts))
                     deadline = datetime.timedelta(hours=COUNTDOWN_HOURS)
                     reminder_period = datetime.timedelta(minutes=exp_min)
                     time_left = then_dt + deadline - current
                     if (then_dt + deadline) <= current:
                         user_msg = ('{} Your application ID: {}, was '
                                     'expired after 72 hours, and it was '
                                     'closed automatically.'.format(
                                         app_user.mention, req_id))
                         # Expired; closed by DreamieBot.
                         data_dict = utils.open_requestlog()
                         found_data = dict()
                         for request_id, details in data_dict.items():
                             if request_id == req_id:
                                 details[
                                     'status'] = utils.Status.CLOSED.name
                                 # mark a last_modified timestring
                                 tm = time.localtime(time.time())
                                 timestring = time.strftime(TIME_FORMAT, tm)
                                 details['last_modified'] = timestring
                                 details['staff'] = 'DreamieBot#1424'
                                 found_data[request_id] = details
                         utils.flush_requestlog(data_dict)
                         server_msg = ('DreamitBot closed an expired '
                                       'application {} at {}'.format(
                                           req_id, timestring))
                         await reminder_chan.send(user_msg)
                         await staff_cog.send_logs(server_msg)
                         time.sleep(1)
                         await sheet.update_data(found_data)
                         return await sheet.archive_column(req_id)
                     elif time_left <= reminder_period:
                         # remove microsecond, users dont care.
                         time_left = utils.chop_microseconds(time_left)
                         message = (
                             '{} Your application has been ready but '
                             'the timer is approaching to the {} '
                             'hours limit.\nRemaining time is {}.\n'
                             'Please use `~status` to see the details'
                             'and contact your staff, {}, to complete'
                             'your application ASAP.'.format(
                                 app_user.mention, COUNTDOWN_HOURS,
                                 time_left, details['staff']))
                         self.key = 'reminder-{}-{}'.format(
                             exp_min, user.name)
                         # Put into self.last_status and avoid spammy messages.
                         if self.key not in self.last_status:
                             self.last_status[self.key] = req_id
                             await reminder_chan.send(message)
                             # send logs to log channel
                             log_msg = 'Send a reminder to {}: {}'.format(
                                 app_user.mention, self.key)
                             await staff_cog.send_logs(log_msg)
                     else:
                         # debug
                         print('current time: %s' % current)
                         print('timer left: %s' %
                               (then_dt + deadline - current))