async def handle_alias_required_licenses(ctx, err): embed = EmbedWithAuthor(ctx) if not err.has_connected_ddb: # was the user blocked from nSRD by a feature flag? ddb_user = await ctx.bot.ddb.get_ddb_user(ctx, ctx.author.id) if ddb_user is None: blocked_by_ff = False else: blocked_by_ff = not (await ctx.bot.ldclient.variation( "entitlements-enabled", ddb_user.to_ld_dict(), False)) if blocked_by_ff: embed.title = "D&D Beyond is currently unavailable" embed.description = f"I was unable to communicate with D&D Beyond to confirm access to:\n" \ f"{', '.join(e.name for e in err.entities)}" else: embed.title = f"Connect your D&D Beyond account to use this customization!" embed.url = "https://www.dndbeyond.com/account" embed.description = \ "This customization requires access to one or more entities that are not in the SRD.\n" \ "Linking your account means that you'll be able to use everything you own on " \ "D&D Beyond in Avrae for free - you can link your accounts " \ "[here](https://www.dndbeyond.com/account)." embed.set_footer( text= "Already linked your account? It may take up to a minute for Avrae to recognize the " "link.") else: missing_source_ids = {e.source for e in err.entities} if len(err.entities) == 1: # 1 entity, display entity piecemeal embed.title = f"Purchase {err.entities[0].name} on D&D Beyond to use this customization!" marketplace_url = err.entities[0].marketplace_url elif len(missing_source_ids ) == 1: # 1 source, recommend purchasing source missing_source = next(iter(missing_source_ids)) embed.title = f"Purchase {long_source_name(missing_source)} on D&D Beyond to use this customization!" marketplace_url = f"https://www.dndbeyond.com/marketplace?utm_source=avrae&utm_medium=marketplacelink" else: # more than 1 source embed.title = f"Purchase {len(missing_source_ids)} sources on D&D Beyond to use this customization!" marketplace_url = "https://www.dndbeyond.com/marketplace?utm_source=avrae&utm_medium=marketplacelink" missing = natural_join( [f"[{e.name}]({e.marketplace_url})" for e in err.entities], "and") if len(missing) > 1400: missing = f"{len(err.entities)} items" missing_sources = natural_join( [long_source_name(e) for e in missing_source_ids], "and") embed.description = \ f"To use this customization and gain access to more integrations in Avrae, unlock **{missing}** by " \ f"purchasing {missing_sources} on D&D Beyond.\n\n" \ f"[Go to Marketplace]({marketplace_url})" embed.url = marketplace_url embed.set_footer( text= "Already purchased? It may take up to a minute for Avrae to recognize the " "purchase.") await ctx.send(embed=embed)
async def get_content(self): embed = disnake.Embed( title=f"Server Settings ({self.guild.name}) / Lookup Settings", colour=disnake.Colour.blurple(), description= "These settings affect how lookup results are displayed on this server." ) if not self.settings.dm_roles: embed.add_field( name="DM Roles", value=f"**Dungeon Master, DM, Game Master, or GM**\n" f"*Any user with a role named one of these will be considered a DM. This lets them look up a " f"monster's full stat block if `Monsters Require DM` is enabled, skip other players' turns in " f"initiative, and more.*", inline=False) else: dm_roles = natural_join( [f'<@&{role_id}>' for role_id in self.settings.dm_roles], 'or') embed.add_field( name="DM Roles", value=f"**{dm_roles}**\n" f"*Any user with at least one of these roles will be considered a DM. This lets them look up a " f"monster's full stat block if `Monsters Require DM` is enabled, skip turns in initiative, and " f"more.*", inline=False) embed.add_field( name="Monsters Require DM", value=f"**{self.settings.lookup_dm_required}**\n" f"*If this is enabled, monster lookups will display hidden stats for any user without " f"a role named DM, GM, Dungeon Master, Game Master, or the DM role configured above.*", inline=False) embed.add_field( name="Direct Message DMs", value=f"**{self.settings.lookup_pm_dm}**\n" f"*If this is enabled, the result of monster lookups will be direct messaged to the user who looked " f"it up, rather than being printed to the channel, if the user is a DM.*", inline=False) embed.add_field( name="Direct Message Results", value=f"**{self.settings.lookup_pm_result}**\n" f"*If this is enabled, the result of all lookups will be direct messaged to the user who looked " f"it up, rather than being printed to the channel.*", inline=False) return {"embed": embed}
async def get_content(self): embed = disnake.Embed(title=f"Server Settings for {self.guild.name}", colour=disnake.Colour.blurple()) if self.settings.dm_roles: dm_roles = natural_join( [f'<@&{role_id}>' for role_id in self.settings.dm_roles], 'or') else: dm_roles = "Dungeon Master, DM, Game Master, or GM" embed.add_field( name="Lookup Settings", value=f"**DM Roles**: {dm_roles}\n" f"**Monsters Require DM**: {self.settings.lookup_dm_required}\n" f"**Direct Message DM**: {self.settings.lookup_pm_dm}\n" f"**Direct Message Results**: {self.settings.lookup_pm_result}", inline=False) embed.add_field(name="Inline Rolling Settings", value=await self.get_inline_rolling_desc(), inline=False) return {"embed": embed}
async def send_action_list(ctx, caster, destination=None, attacks=None, actions=None, embed=None, args=None): """ Sends the list of actions and attacks given to the given destination. :type ctx: discord.ext.commands.Context :type caster: cogs5e.models.sheet.statblock.StatBlock :type destination: discord.abc.Messageable :type attacks: cogs5e.models.sheet.attack.AttackList :type actions: cogs5e.models.sheet.action.Actions :type embed: discord.Embed :type args: Iterable[str] """ if destination is None: destination = ctx if embed is None: embed = discord.Embed(color=caster.get_color(), title=f"{caster.get_title_name()}'s Actions") if args is None: args = () await destination.trigger_typing() # long embed builder ep = embeds.EmbedPaginator(embed) fields = [] # arg setup verbose = '-v' in args display_attacks = 'attack' in args display_actions = 'action' in args display_bonus = 'bonus' in args display_reactions = 'reaction' in args display_other = 'other' in args is_display_filtered = any((display_attacks, display_actions, display_bonus, display_reactions, display_other)) filtered_action_type_strs = list( itertools.compress(('attacks', 'actions', 'bonus actions', 'reactions', 'other actions'), (display_attacks, display_actions, display_bonus, display_reactions, display_other))) # helpers non_automated_count = 0 non_e10s_count = 0 e10s_map = {} source_names = set() # action display if attacks and (display_attacks or not is_display_filtered): atk_str = attacks.build_str(caster) fields.append({'name': 'Attacks', 'value': atk_str}) # since the sheet displays the description regardless of entitlements, we do here too async def add_action_field(title, action_source): nonlocal non_automated_count, non_e10s_count # eh action_texts = [] for action in sorted(action_source, key=lambda a: a.name): has_automation = action.gamedata is not None has_e10s = True # entitlement stuff if has_automation: source_feature = action.gamedata.source_feature if source_feature.entitlement_entity_type not in e10s_map: e10s_map[ source_feature. entitlement_entity_type] = await ctx.bot.ddb.get_accessible_entities( ctx, ctx.author.id, source_feature.entitlement_entity_type) source_feature_type_e10s = e10s_map[ source_feature.entitlement_entity_type] has_e10s = lookuputils.can_access(source_feature, source_feature_type_e10s) if not has_e10s: non_e10s_count += 1 source_names.add(source_feature.source) if verbose: name = f"**{action.name}**" if has_automation and has_e10s else f"***{action.name}***" action_texts.append( f"{name}: {action.build_str(caster=caster, snippet=True)}") elif has_automation: name = f"**{action.name}**" if has_e10s else f"***{action.name}***" action_texts.append( f"**{name}**: {action.build_str(caster=caster, snippet=False)}" ) # count these for extra display if not has_automation: non_automated_count += 1 if not action_texts: return action_text = '\n'.join(action_texts) fields.append({'name': title, 'value': action_text}) if actions is not None: if actions.full_actions and (display_actions or not is_display_filtered): await add_action_field("Actions", actions.full_actions) if actions.bonus_actions and (display_bonus or not is_display_filtered): await add_action_field("Bonus Actions", actions.bonus_actions) if actions.reactions and (display_reactions or not is_display_filtered): await add_action_field("Reactions", actions.reactions) if actions.other_actions and (display_other or not is_display_filtered): await add_action_field("Other", actions.other_actions) # build embed # description: filtering help description = "" if not fields: if is_display_filtered: description = f"{caster.get_title_name()} has no {natural_join(filtered_action_type_strs, 'or')}." else: description = f"{caster.get_title_name()} has no actions." elif is_display_filtered: description = f"Only displaying {natural_join(filtered_action_type_strs, 'and')}." # description: entitlements help if not verbose and actions and non_e10s_count: has_ddb_link = any(v is not None for v in e10s_map.values()) if not has_ddb_link: description = ( f"{description}\nItalicized actions are for display only and cannot be run. " f"Connect your D&D Beyond account to unlock the full potential of these actions!" ) else: source_names = natural_join( [lookuputils.long_source_name(s) for s in source_names], 'and') description = ( f"{description}\nItalicized actions are for display only and cannot be run. Unlock " f"{source_names} on your D&D Beyond account to unlock the full potential of these actions!" ) if description: ep.add_description(description.strip()) # fields for field in fields: ep.add_field(**field) # footer if not verbose and actions: if non_automated_count: ep.set_footer( value= f"Use the -v argument to view each action's full description " f"and {non_automated_count} display-only actions.") else: ep.set_footer( value= f"Use the -v argument to view each action's full description.") elif verbose and (non_automated_count or non_e10s_count): ep.set_footer( value="Italicized actions are for display only and cannot be run.") await ep.send_to(destination)