async def participantsNotifyer(self): for orgEvent in self.client.orgEvents.events: # Alerts to be removed. discardAlerts = [] for alert in orgEvent.notifications.participantAlerts: if( datetime.utcnow() > alert.time and datetime.utcnow() - alert.margin < alert.time ): roles = [] if orgEvent.roles: roles.append(orgEvent.roles['participant']) self.handleParticipantNotification( alert, orgEvent.dateAndTime, orgEvent.participants, roles ) if datetime.utcnow() > alert.time: discardAlerts.append(alert) for alert in discardAlerts: orgEvent.notifications.participantAlerts.remove(alert) if discardAlerts: utility.saveData( Constants.EVENT_DATA_FILENAME, self.client.orgEvents.json(indent=2) )
def clearEventData(self, orgEvent): # Remove event from event list if found. self.client.orgEvents.events.remove(orgEvent) # Save data eventData = self.client.orgEvents.json(indent=2) saveData(Constants.EVENT_DATA_FILENAME, eventData)
async def set_rsi_handle(self, ctx, *, rsiHandle=''): """Sets the RSI handle of the user invoking the command. :param rsiHandle: RSI handle :type rsiHandle: str example: !eb.set_rsi_handle myRSIhandle """ if rsiHandle == '': await ctx.send( 'Please specify your RSI handle by typing:\n' f'{Constants.CMD_PREFIX}set_rsi_handle <your rsi handle> ' 'without the <>') return if len(rsiHandle) < 3: await ctx.send( 'RSI handles must be at least 3 characters long. Please enter ' 'a valid RSI handle.') return if ' ' in rsiHandle: await ctx.send( 'RSI handles cannot contain spaces. Please enter a valid ' 'RSI handle.') return # Search for user in guild members guildMembers = self.client.guildMembers.members for m in guildMembers: if m.id == ctx.author.id: m.rsiHandle = rsiHandle break else: m = event.GuildMember(id=ctx.author.id, name=ctx.author.name, rsiHandle=rsiHandle) guildMembers.append(m) guildMemberData = self.client.guildMembers.json(indent=2) utility.saveData(Constants.GUILD_MEMBER_DATA_FILENAME, guildMemberData) # Add to sheet if in daymar event, and participant is active. orgEvents = self.client.orgEvents.events for e in orgEvents: if e.eventType == event.EventType.daymar: p = e.getParticipant(ctx.author.id) if p: if p.active is True: self.addParticipant(m) break await ctx.send( f'Your RSI handle has been set to: **{rsiHandle}**\n' 'You may change your registered RSI handle name at any time by ' 'running this command again.')
async def setEventManagerId(self, ctx, id): """Sets the ID of the event manager. """ user = self.client.get_user(int(id)) if user: await ctx.send(f'Event manager id ({id}) set successfully.') self.client.config.eventManagerId = int(id) configData = self.client.config.json(indent=2) saveData(Constants.CONFIG_DATA_FILENAME, configData) else: await ctx.send('Could not find any user with that ID.')
async def setGuildId(self, ctx): """Sets the ID of the current guild as the event bot guild ID. """ # Get the id of the guild from where the command was invoked. guildId = ctx.guild.id # Set the id in config object self.client.config.guildId = guildId # Save config to file configData = self.client.config.json(indent=2) saveData(Constants.CONFIG_DATA_FILENAME, configData) await ctx.send(f'Guild ID ({guildId}) has been set.')
async def handleCancellation( self, payload ): # TODO: generalize method to handle more emojis and roles. """ messageId = payload.message_id # Check which event was reacted to orgEvent = self.getEvent(messageId) # Get channel object channel = self.client.get_channel(payload.channel_id) # Get the Message object. message = await channel.fetch_message(messageId) # Get the Guild object. guild = message.guild # Get the user who reacted member = guild.get_member(payload.user_id) """ data = await self.gatherPayloadData(payload) orgEvent, member, message, memeberRole, collabRole, emoji = data guild = message.guild # No action should be taken if the bot made the reaction. if member.bot: return # Remove reaction await message.remove_reaction(emoji, member) person = orgEvent.getParticipant(member.id) if person is not None: person.active = False if person.roles: roleId = orgEvent.roles['participant'].id person.removeRole(roleId) # Get the discord role. participantRole = guild.get_role(roleId) await member.remove_roles(participantRole) utility.saveData(Constants.EVENT_DATA_FILENAME, self.client.orgEvents.json(indent=2)) # Update embed in discord. self.makeUpdate(orgEvent) # Do daymar specific actions if daymar event. if orgEvent.eventType == event.EventType.daymar: cog = self.client.get_cog('Daymar') cog.clearParticipant(member)
async def forceNewMessage(self, ctx, oldId): # TODO: REFACTOR THIS HOTFIX # Convert input to int oldId = int(oldId) for orgEvent in self.client.orgEvents.events: if orgEvent.id == oldId: myEvent = orgEvent break else: return guild = self.client.get_guild(self.client.config.guildId) channel = guild.get_channel(self.client.config.signupChannelId) # Delete old message if exists. try: oldMsg = await channel.fetch_message(oldId) await oldMsg.delete() except discord.NotFound: pass user = self.client.get_user(myEvent.organizer.id) vc = guild.get_channel(self.client.config.defaultVoiceChannelId) embed = myEvent.makeEmbed(True, user, mainVoiceName=vc.name) # Post the new embed. msg = await channel.send(embed=embed) # Register the new event id. myEvent.id = msg.id # Add reactions to the message. #TODO refactor wet code (make a method for adding the reactions in evntCog) await msg.add_reaction(Constants.REACTION_EMOJIS['participate']) await msg.add_reaction(Constants.REACTION_EMOJIS['cancel']) if True in orgEvent.privateIndication.values(): await msg.add_reaction(Constants.REACTION_EMOJIS['info']) await msg.add_reaction(Constants.REACTION_EMOJIS['help']) eventData = self.client.orgEvents.json(indent=2) saveData(Constants.EVENT_DATA_FILENAME, eventData)
async def deadlineNotifyer(self): for orgEvent in self.client.orgEvents.events: discardAlerts = [] for alert in orgEvent.notifications.deadlineAlerts: if( datetime.utcnow() > alert.time and datetime.utcnow() - alert.margin < alert.time ): self.handleDeadlineNotification(alert, orgEvent.deadline) if datetime.utcnow() > alert.time: discardAlerts.append(alert) for alert in discardAlerts: orgEvent.notifications.deadlineAlerts.remove(alert) if discardAlerts: utility.saveData( Constants.EVENT_DATA_FILENAME, self.client.orgEvents.json(indent=2) )
async def eventNotifyer(self): for orgEvent in self.client.orgEvents.events: # Indices of alerts to be removed. discardAlerts = [] for alert in orgEvent.notifications.generalAlerts: if( datetime.utcnow() > alert.time and datetime.utcnow() - alert.margin < alert.time ): self.handleEventNotification(alert, orgEvent.dateAndTime) if datetime.utcnow() > alert.time: discardAlerts.append(alert) for alert in discardAlerts: orgEvent.notifications.generalAlerts.remove(alert) if discardAlerts: utility.saveData( Constants.EVENT_DATA_FILENAME, self.client.orgEvents.json(indent=2) )
async def handleParticipationRequest(self, payload): """ messageId = payload.message_id # Check which event was reacted to orgEvent = self.getEvent(messageId) # Get the Channel object. channel = self.client.get_channel(payload.channel_id) # Get the Message object. message = await channel.fetch_message(messageId) # Get the Guild object. guild = message.guild # Get the user who reacted member = guild.get_member(payload.user_id) # Get the role that official members will have. memberRole = guild.get_role(Constants.MEMBER_ROLE_ID) """ data = await self.gatherPayloadData(payload) orgEvent = data[0] member = data[1] # No action should be taken if the bot made the reaction. if member.bot: return # Remove reaction message = data[2] emoji = data[5] await message.remove_reaction(emoji, member) # Check that event was found in the internal record. if not orgEvent: self.client.logger.warning( 'Someone tried to sign up for an untracked event.') return # Check that event is not over, and handle rejection if required. if await self.eventHasPassed(data): return # Check that the deadline has not passed, and handle rejection if req. if await self.deadlineExceeded(data): return # If event is private, check if user has proper authorization. if orgEvent.data['Members Only'].upper() == 'YES': # Check that user has member role, handle rejection if not. if await self.noMemberRole(data): return # Attempt to get person object person = orgEvent.getParticipant(member.id) if person is not None: person.active = True # Check if user already has role, handle rejection if necessary. if await self.userAlreadyHasRole(data): return orgEvent.moveToBottom(person) # If user has passed all flags, add user to event and update # the embed. # Add role/person with role to internal event object. if 'participant' in orgEvent.roles.keys(): pRole = orgEvent.roles['participant'] else: pRole = None if person is None: person = event.Person(id=member.id, name=member.name, active=True) orgEvent.participants.append(person) if pRole is not None: person.roles.append(pRole) # if person is None: # person = event.Person(id=member.id, name=member.name) # if pRole: # person.roles.append(pRole) # orgEvent.participants.append(person) # elif pRole: # person.roles.append(orgEvent.roles['participant']) # Add discord role. if pRole is not None: await self.addDiscordRole(member, orgEvent.roles['participant']) # Serialize data. utility.saveData(Constants.EVENT_DATA_FILENAME, self.client.orgEvents.json(indent=2)) # Update embed in discord. self.makeUpdate(orgEvent) # Send welcome message to the discussion channel. if 'discussion' in orgEvent.channels.keys(): await self.sendWelcomeMsg(member, orgEvent.channels['discussion']) # Do extra actions if the event is a daymar event. if orgEvent.eventType == event.EventType.daymar: # Search through guild member record and add person if not found. guildMembers = self.client.guildMembers.members for m in guildMembers: if m.id == member.id: guildMember = m break else: guildMember = None if guildMember is None: guildMember = event.GuildMember( id=member.id, name=member.name, ) guildMembers.append(guildMember) utility.saveData(Constants.GUILD_MEMBER_DATA_FILENAME, self.client.guildMembers.json(indent=2)) cog = self.client.get_cog('Daymar') if guildMember.rsiHandle is not None: cog.addParticipant(guildMember) else: # Ask user to confirm rsi handle. # TODO: Link to command more robustly await member.send( 'In order for Daymar organizers to invite you to their ' 'server they need to know your in game name (RSI handle).' '\nPlease specify your RSI handle by using the ' '**!eb.set_rsi_handle** command.\n' 'For detailed help with this command type ' '!eb.help set_rsi_handle') cog.addParticipant(guildMember)
async def processData(self, eventData, keys): # TODO: Rename? processEventCreation # Get the event type. eventType = self.getEventType(eventData) # Get the event organizer organizer = self.getEventOrganizer(eventData) # Create roles for event discordRoles = await self.createRoles(eventData) # Convert discordRoles to internal roles for persistant storage. if discordRoles: roles = { 'spectator': event.Role(id=discordRoles['spectator'].id, name=discordRoles['spectator'].name), 'participant': event.Role(id=discordRoles['participant'].id, name=discordRoles['participant'].name) } else: roles = {} discordChannels = await self.createChannels(eventData, discordRoles, eventType=eventType) # Convert discordChannels to internal channels for persistent storage. channels = {} for key, channel in discordChannels.items(): channels[key] = event.Channel(id=channel.id, name=channel.name, channelType=event.ChannelType( channel.type.value)) # Convert the dates from string to datetime objects dateAndTime, deadline = self.convertDates(eventData, 'Date Time', 'Deadline') # Get the image url if found in resource channel. imageUrl = await self.getImageUrl(eventData['Event']) # Create default general alerts list. generalAlerts = self.makeAlerts(eventData['Event'], dateAndTime, deadline, channels, general=True) strings = ['The following general alerts were created:\n'] [strings.append(str(a.time) + '\n') for a in generalAlerts] self.client.logger.debug(''.join(strings)) if deadline is not None: deadlineAlerts = self.makeAlerts(eventData['Event'], dateAndTime, deadline, channels, deadlineAlerts=True) strings = ['The following deadline alerts were created:\n'] [strings.append(str(a.time) + '\n') for a in deadlineAlerts] self.client.logger.debug(''.join(strings)) else: deadlineAlerts = [] # Create default participant alerts list. participantAlerts = self.makeAlerts(eventData['Event'], dateAndTime, deadline, channels, participant=True) strings = ['The following participant alerts was created:\n'] [strings.append(str(a.time) + '\n') for a in participantAlerts] self.client.logger.debug(''.join(strings)) # Create notifications for event. notifications = event.Notifications( generalAlerts=generalAlerts, deadlineAlerts=deadlineAlerts, participantAlerts=participantAlerts) # Instanciate event object. eventInstance = event.Event(data=eventData, eventType=eventType, dateAndTime=dateAndTime, deadline=deadline, keys=keys, organizer=organizer, imageUrl=imageUrl, roles=roles, channels=channels, lastUpdate=datetime.utcnow(), notifications=notifications) # TODO: Line below should be done in class initialisation, but idk how # to do that correctly with pydantic BaseModel classes. # Maybe inherit from datamodel instead? eventInstance.privateIndication = eventInstance.decodePrivate( eventData['Color Code']) # Assign ID by posting message to discord and saving the returned ID # in the event object. registeredEvent = await self.assignId(eventInstance) # If event contains private information, post the uncensored embed in # the newly opened channel. # TODO: Make sure that private channel embed is also updated when the # main embed is updated. if 'briefing' in discordChannels: user = self.client.get_user(registeredEvent.organizer.id) embed = registeredEvent.makeEmbed(False, user, includeAuthor=False, includeFooter=False, includePreamble=False, includeRollCall=False, includeVoiceChnl=False) channel = discordChannels['briefing'] privateMsg = await channel.send(embed=embed) if eventData['Additional Info'] != '': await channel.send(eventData['Additional Info']) # TODO: Figure out how to store privateMsg. This is the embed # msg sent to private briefing channels. In order to update, the # d.py message object id must be stored. # Write IDs back to google sheets. self.writeIdToSheets(eventInstance) # If event is a daymar event the daymar spreadsheet is cleared. if eventInstance.eventType == event.EventType.daymar: try: cog = self.client.get_cog('Daymar') cog.clearDaymarSheet() except Exception: # TODO: Specify exception. self.client.logger.warning( 'Not able to connect to Spreadsheet') # Append the event to the clients list of events self.client.orgEvents.events.append(registeredEvent) utility.saveData(Constants.EVENT_DATA_FILENAME, self.client.orgEvents.json(indent=2))
def test_saveData_fileCreatedForValidData(self): testString = 'something' success = utility.saveData('saveDataTest.json', testString) self.assertTrue(os.path.exists('saveDataTest.json')) if success: os.remove('saveDataTest.json')
maxBytes=8 * 1024 * 1024, # Max size is 8MB backupCount=1, encoding='utf-8') handler.setFormatter( logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s')) logger.addHandler(handler) client.logger = logger # TODO: Clean up wet code. Deserialization should done in a function. # Deserialize configuration data. configData = loadData(Constants.CONFIG_DATA_FILENAME) if configData is None: client.logger.info('Config data not found.') client.config = configuration.Configuration() configData = client.config.json(indent=2) saveData(Constants.CONFIG_DATA_FILENAME, configData) else: try: # Attempt to parse persistent config data to config. client.config = configuration.Configuration.parse_obj(configData) client.logger.info('Config data successfully parsed.') except ValidationError as e: client.logger.warning( 'Exception thrown, error message is as follows:\n' f'{e}\n' 'Config data was found, but could not be loaded. ' 'Starting clean') client.config = configuration.Configuration() configData = client.config.json(indent=2) saveData(Constants.CONFIG_DATA_FILENAME, configData)
async def serializeConfig(self, ctx, attribute, id): setattr(self.client.config, attribute, id) configData = self.client.config.json(indent=2) saveData(Constants.CONFIG_DATA_FILENAME, configData) await ctx.send(f'{attribute} successfully set, ID is {id}')