async def _sub_adminlist(self, ctx, *, trainer=None): """**Usage**: `!sub adminlist/alist <member>` Receive a list of all current subscriptions the provided member has""" message = ctx.message channel = message.channel author = message.author if not trainer: response_msg = "Please provide a trainer name or id" response = await channel.send(response_msg) return await utils.sleep_and_cleanup([message, response], 10) if trainer.isdigit(): trainerid = trainer else: converter = commands.MemberConverter() try: trainer_member = await converter.convert(ctx, trainer) trainerid = trainer_member.id except: response_msg = f"Could not process trainer with name: {trainer}" await channel.send(response_msg) return await utils.sleep_and_cleanup([message, response_msg], 10) try: results = (SubscriptionTable.select( SubscriptionTable.type, SubscriptionTable.target).join( TrainerTable, on=(SubscriptionTable.trainer == TrainerTable.snowflake )).where(SubscriptionTable.trainer == trainerid).where( TrainerTable.guild == ctx.guild.id).where( SubscriptionTable.guild_id == ctx.guild.id)) results = results.execute() subscription_msg = '' types = set([s.type for s in results]) subscriptions = { t: [s.target for s in results if s.type == t] for t in types } for sub in subscriptions: subscription_msg += '**{category}**:\n\t{subs}\n\n'.format( category=sub.title(), subs='\n\t'.join(subscriptions[sub])) if len(subscription_msg) > 0: listmsg = "Listing subscriptions for user: {id}\n".format( id=trainer) listmsg += 'Current subscriptions are:\n\n{subscriptions}'.format( subscriptions=subscription_msg) await message.add_reaction(self.success_react) await author.send(listmsg) else: none_msg = await channel.send( f"No subscriptions found for user: {trainer}") await message.add_reaction(self.success_react) return await utils.sleep_and_cleanup([none_msg], 10) except: response_msg = f"Encountered an error while looking up subscriptions for trainer with name: {trainer}" await channel.send(response_msg) return await utils.sleep_and_cleanup([response_msg, message], 10)
async def send_notifications_async(self, notification_type, details, new_channel, exclusions=[]): valid_types = [ 'raid', 'research', 'wild', 'nest', 'gym', 'shiny', 'item', 'lure', 'hideout' ] if notification_type not in valid_types: return guild = new_channel.guild # get trainers try: results = (SubscriptionTable.select( SubscriptionTable.trainer, SubscriptionTable.target, SubscriptionTable.specific).join( TrainerTable, on=(SubscriptionTable.trainer == TrainerTable.snowflake )).where((SubscriptionTable.type == notification_type) | (SubscriptionTable.type == 'pokemon') | (SubscriptionTable.type == 'gym')). where(TrainerTable.guild == guild.id).where( SubscriptionTable.guild_id == guild.id)).execute() except: return # group targets by trainer trainers = set([s.trainer for s in results]) target_dict = { t: {s.target: s.specific for s in results if s.trainer == t} for t in trainers } regions = set(details.get('regions', [])) ex_eligible = details.get('ex-eligible', None) tier = details.get('tier', None) perfect = details.get('perfect', None) pokemon_list = details.get('pokemon', []) gym = details.get('location', None) item = details.get('item', None) lure_type = details.get('lure_type', None) if not isinstance(pokemon_list, list): pokemon_list = [pokemon_list] location = details.get('location', None) multi = details.get('multi', False) region_dict = self.bot.guild_dict[guild.id]['configure_dict'].get( 'regions', None) outbound_dict = {} # build final dict for trainer in target_dict: user = guild.get_member(trainer) if trainer in exclusions or not user: continue if region_dict and region_dict.get('enabled', False): matched_regions = [ n for n, o in region_dict.get('info', {}).items() if o['role'] in [r.name for r in user.roles] ] if regions and regions.isdisjoint(matched_regions): continue targets = target_dict[trainer] descriptors = [] target_matched = False if 'ex-eligible' in targets and ex_eligible: target_matched = True descriptors.append('ex-eligible') if tier and str(tier) in targets: tier = str(tier) if targets[tier]: try: current_gym_ids = targets[tier].strip('[').strip(']') split_id_string = current_gym_ids.split(', ') split_ids = [] for s in split_id_string: try: split_ids.append(int(s)) except ValueError: pass target_gyms = (GymTable.select( LocationTable.id, LocationTable.name, LocationTable.latitude, LocationTable.longitude, RegionTable.name.alias('region'), GymTable.ex_eligible, LocationNoteTable.note).join(LocationTable).join( LocationRegionRelation).join(RegionTable).join( LocationNoteTable, JOIN.LEFT_OUTER, on=(LocationNoteTable.location_id == LocationTable.id) ).where((LocationTable.guild == guild.id) & ( LocationTable.guild == RegionTable.guild) & (LocationTable.id << split_ids))) target_gyms = target_gyms.objects(Gym) found_gym_names = [r.name for r in target_gyms] if gym in found_gym_names: target_matched = True except: pass else: target_matched = True descriptors.append( 'level {level}'.format(level=details['tier'])) pkmn_adj = '' if perfect and 'perfect' in targets: target_matched = True pkmn_adj = 'perfect ' for pokemon in pokemon_list: if pokemon.name in targets: target_matched = True full_name = pkmn_adj + pokemon.name descriptors.append(full_name) if gym in targets: target_matched = True if item and item.lower() in targets: target_matched = True if 'shiny' in targets: target_matched = True if 'takeover' in targets or (lure_type and lure_type in targets): trainer_info = self.bot.guild_dict[ guild.id]['trainers'].setdefault('info', {}).setdefault( trainer, {}) t_location = trainer_info.setdefault('location', None) distance = trainer_info.setdefault('distance', None) stop = details['location'] if t_location is not None and distance is not None: if self.close_enough( (float(stop.latitude), float(stop.longitude)), (t_location[0], t_location[1]), distance): target_matched = True else: target_matched = False else: target_matched = True if not target_matched: continue description = ', '.join(descriptors) start = 'An' if re.match(r'^[aeiou]', description, re.I) else 'A' if notification_type == 'item': start = 'An' if re.match(r'^[aeiou]', item, re.I) else 'A' if multi: location = 'multiple locations' message = f'{start} **{item} research task** has been reported at **{location}**!' elif notification_type == 'lure': message = f'A **{lure_type.capitalize()}** lure has been dropped at {location.name}!' elif notification_type == 'hideout': message = f'A **Team Rocket Hideout** has been spotted at {location.name}!' elif notification_type == 'wild': message = f'A **wild {description} spawn** has been reported at **{location}**!' elif notification_type == 'research': if multi: location = 'multiple locations' message = f'{start} **{description} research task** has been reported at **{location}**!' elif 'hatching' in details and details['hatching']: message = f"The egg at **{location}** has hatched into {start.lower()} **{description}** raid!" else: message = f'{start} {description} {notification_type} at {location} has been reported!' outbound_dict[trainer] = {'discord_obj': user, 'message': message} pokemon_names = ' '.join([p.name for p in pokemon_list]) if notification_type == 'item': role_name = utils.sanitize_name(f"{item} {location}".title()) elif notification_type == 'lure': role_name = utils.sanitize_name( f'{lure_type} {location.name}'.title()) elif notification_type == 'hideout': role_name = utils.sanitize_name( f'Rocket Hideout {location.name}'.title()) else: role_name = utils.sanitize_name( f"{notification_type} {pokemon_names} {location}".title()) # starting to hit rate limit for role creation so explicitly mentioning all trainers in the meantime await self.notify_all_async(new_channel, outbound_dict) faves_cog = self.bot.cogs.get('Faves') return faves_cog.get_report_points(guild.id, pokemon_list, notification_type)
async def _sub_remove(self, ctx, *, content=None): """Remove a subscription **Usage**: `!sub remove <type> <target>` You will no longer be notified of the specified target for the given event type. You must remove subscriptions using the same type with which they were added. It may be helpful to do `!sub list` first to see your existing subscriptions. You can remove all subscriptions of a type: `!sub remove <type> all` Or remove all subscriptions: `!sub remove all all` Or started a guided session with: `!sub remove` **Valid types**: `pokemon, raid, research, wild, gym, item, lure` **Note**: 'pokemon' includes raid, research, and wild reports""" message = ctx.message channel = message.channel guild = message.guild trainer = message.author.id if content is None: return await self._guided_subscription(ctx, 'Remove') content = content.strip().lower() if content == 'shiny': sub_type, target = ['shiny', 'shiny'] else: error_message = self._get_subscription_command_error( content, self.valid_types) if error_message: response = await message.channel.send(error_message) return await utils.sleep_and_cleanup([message, response], 10) sub_type, target = content.split(' ', 1) candidate_list = [] error_list = [] not_found_list = [] remove_list = [] trainer_query = (TrainerTable.select( TrainerTable.snowflake).where((TrainerTable.snowflake == trainer) & (TrainerTable.guild == guild.id))) # check for special cases skip_parse = False if sub_type == 'all': if target == 'all': try: remove_count = SubscriptionTable.delete()\ .where((SubscriptionTable.trainer << trainer_query) & (SubscriptionTable.guild_id == ctx.guild.id)).execute() message = f'I removed your {remove_count} subscriptions!' except: message = 'I was unable to remove your subscriptions!' confirmation_msg = f'{message}' await channel.send(content=confirmation_msg) return else: target = target.split(',') if sub_type == 'pokemon': for name in target: pkmn = Pokemon.get_pokemon(self.bot, name) if pkmn: candidate_list.append( (sub_type, pkmn.name, pkmn.name)) else: error_list.append(name) if sub_type != "gym": skip_parse = True elif target == 'all': candidate_list.append((sub_type, target, target)) skip_parse = True elif target == 'shiny': candidate_list = [('shiny', 'shiny', 'shiny')] sub_type, target = ['shiny', 'shiny'] skip_parse = True if not skip_parse: candidate_list, error_list = await self._parse_subscription_content( content, 'remove', message) remove_count = 0 s_type = '' for sub in candidate_list: s_type = sub[0] s_target = sub[1] s_entry = sub[2] if len(sub) > 3: spec = sub[3] try: result, __ = SubscriptionTable.get_or_create( guild_id=ctx.guild.id, trainer=trainer, type='gym', target=s_target) current_gym_ids = result.specific split_ids = [] if current_gym_ids: current_gym_ids = current_gym_ids.strip('[]') split_id_string = current_gym_ids.split(', ') for s in split_id_string: try: split_ids.append(int(s)) except ValueError: pass for s in spec: if s in split_ids: remove_count += 1 split_ids.remove(s) result.specific = split_ids result.save() remove_list.append(s_entry) except: error_list.append(s_entry) else: try: if s_type == 'all': remove_count += SubscriptionTable.delete().where( (SubscriptionTable.trainer << trainer_query) & (SubscriptionTable.target == s_target) & (SubscriptionTable.guild_id == ctx.guild.id) ).execute() elif s_target == 'all': remove_count += SubscriptionTable.delete().where( (SubscriptionTable.trainer << trainer_query) & (SubscriptionTable.type == s_type) & (SubscriptionTable.guild_id == ctx.guild.id) ).execute() else: remove_count += SubscriptionTable.delete().where( (SubscriptionTable.trainer << trainer_query) & (SubscriptionTable.type == s_type) & (SubscriptionTable.target == s_target) & (SubscriptionTable.guild_id == ctx.guild.id) ).execute() if remove_count > 0: remove_list.append(s_entry) else: not_found_list.append(s_entry) except: error_list.append(s_entry) not_found_count = len(not_found_list) error_count = len(error_list) confirmation_msg = f'{ctx.author.mention}, successfully removed {remove_count} {s_type} subscriptions' if remove_count > 0: confirmation_msg += '\n**{remove_count} Removed:** \n\t{remove_list}'\ .format(remove_count=remove_count, remove_list=',\n\t'.join(remove_list)) if not_found_count > 0: confirmation_msg += '\n**{not_found_count} Not Found:** \n\t{not_found_list}'\ .format(not_found_count=not_found_count, not_found_list=', '.join(not_found_list)) if error_count > 0: confirmation_msg += '\n**{error_count} Errors:** \n\t{error_list}\n(Check the spelling and try again)'\ .format(error_count=error_count, error_list=', '.join(error_list)) await channel.send(content=confirmation_msg)
def _generate_sub_list_message(ctx, types): message = ctx.message guild = message.guild results = (SubscriptionTable.select( SubscriptionTable.type, SubscriptionTable.target, SubscriptionTable.specific).join( TrainerTable, on=(SubscriptionTable.trainer == TrainerTable.snowflake )).where(SubscriptionTable.trainer == ctx.author.id).where( TrainerTable.guild == ctx.guild.id).where( SubscriptionTable.guild_id == ctx.guild.id)) if len(types) > 0: results = results.where(SubscriptionTable.type << types) results = results.execute() types = set([s.type for s in results]) for r in results: if r.specific: current_gym_ids = r.specific.strip('[').strip(']') split_id_string = current_gym_ids.split(', ') split_ids = [] for s in split_id_string: try: split_ids.append(int(s)) except ValueError: pass gyms = ( GymTable.select( LocationTable.id, LocationTable.name, LocationTable.latitude, LocationTable.longitude, RegionTable.name.alias('region'), GymTable.ex_eligible, LocationNoteTable.note).join(LocationTable). join(LocationRegionRelation).join(RegionTable).join( LocationNoteTable, JOIN.LEFT_OUTER, on=(LocationNoteTable.location_id == LocationTable.id) ).where((LocationTable.guild == guild.id) & (LocationTable.guild == RegionTable.guild) & (LocationTable.id << split_ids))) result = gyms.objects(Gym) r.specific = ",\n\t".join([o.name for o in result]) subscriptions = {} for t in types: if t == 'gym': for r in results: if r.type == 'gym': if r.specific: subscriptions[ f"Level {r.target} Raids at"] = r.specific else: msg = subscriptions.get('gym', "") if len(msg) < 1: msg = r.target else: msg += f', {r.target}' subscriptions['gym'] = msg else: subscriptions[t] = [ s.target for s in results if s.type == t and t != 'gym' ] listmsg_list = [] subscription_msg = "" sep = '\n\t' for sub in subscriptions.keys(): if not isinstance(subscriptions[sub], list): subscriptions[sub] = [subscriptions[sub]] new_msg = f"**Subscription type - {sub.title()}**:\n\t{sep.join(subscriptions[sub])}\n\n" if len(subscription_msg) + len( new_msg) < constants.MAX_MESSAGE_LENGTH: subscription_msg += new_msg else: listmsg_list.append(subscription_msg) subscription_msg = new_msg listmsg_list.append(subscription_msg) return listmsg_list
async def _sub_add(self, ctx, *, content=None): """Create a subscription **Usage**: `!sub add <type> <target>` Kyogre will send you a notification if an event is generated matching the details of your subscription. **Valid types**: `pokemon, raid, research, wild, gym, item, lure` **Note**: 'pokemon' includes raid, research, and wild reports""" message = ctx.message channel = message.channel guild = message.guild trainer = message.author.id error_list = [] if content is None: return await self._guided_subscription(ctx, 'Add') content = content.strip().lower() if content == 'shiny': candidate_list = [('shiny', 'shiny', 'shiny')] else: error_message = self._get_subscription_command_error( content, self.valid_types) if error_message: response = await message.channel.send(error_message) return await utils.sleep_and_cleanup([message, response], 10) candidate_list, error_list = await self._parse_subscription_content( content, 'add', message) existing_list = [] sub_list = [] # don't remove. this makes sure the guild and trainer are in the db guild_obj, __ = GuildTable.get_or_create(snowflake=guild.id) trainer_obj, __ = TrainerTable.get_or_create(snowflake=trainer, guild=guild.id) if guild_obj is None or trainer_obj is None: pass s_type = '' for sub in candidate_list: s_type = sub[0] s_target = sub[1] s_entry = sub[2] if len(sub) > 3: spec = sub[3] try: result, __ = SubscriptionTable.get_or_create( guild_id=ctx.guild.id, trainer=trainer, type=s_type, target=s_target) current_gym_ids = result.specific split_ids = [] if current_gym_ids: current_gym_ids = current_gym_ids.strip('[]') split_id_string = current_gym_ids.split(', ') for s in split_id_string: try: split_ids.append(int(s)) except ValueError: pass spec = [int(s) for s in spec] new_ids = set(split_ids + spec) result.specific = list(new_ids) if len(result.specific) > 0: result.save() sub_list.append(s_entry) except: error_list.append(s_entry) else: try: SubscriptionTable.create(guild_id=ctx.guild.id, trainer=trainer, type=s_type, target=s_target) sub_list.append(s_entry) except IntegrityError: existing_list.append(s_entry) except: error_list.append(s_entry) sub_count = len(sub_list) existing_count = len(existing_list) error_count = len(error_list) confirmation_msg = f'{ctx.author.mention}, successfully added {sub_count} new {s_type} subscriptions' if sub_count > 0: confirmation_msg += '\n**{sub_count} Added:** \n\t{sub_list}'.format( sub_count=sub_count, sub_list=',\n\t'.join(sub_list)) if existing_count > 0: confirmation_msg += '\n**{existing_count} Already Existing:** \n\t{existing_list}'\ .format(existing_count=existing_count, existing_list=', '.join(existing_list)) if error_count > 0: confirmation_msg += '\n**{error_count} Errors:** \n\t{error_list}\n(Check the spelling and try again)'\ .format(error_count=error_count, error_list=', '.join(error_list)) await channel.send(content=confirmation_msg)