Query Open5E API in Silverbullet with space script

Silverbullet excels as a place to organize your notes as a ttrpg/D&D dungeon master. Being able to link worlds and adventures and players and player characters and monsters and locations – and being able to query between them all – can help the most scatter-brained forever-DM’s among us keep track of our many adventures and all the myriad ways our players run amuck.
Open5E is an open source project aiming to provide a usable database and API of open-licensed 5th edition and 5e compatible content.

Enter: Space Script. This in-progress project aims to create a set of space script functions that call Open5E, and accompanying snippets that make it easy to slash-command add spells, monsters, items – whatever – right into your notes. I’d certainly appreciate any help along the way, but the proof of concept is already here and very promising.

To start off, wherever you’re saving your space-script functions (I have a “space-script-index” page), let’s add the following:

Open 5e Fetch Functions:

Fetch Spell Function:

silverbullet.registerFunction("fetchSpell", async (spellName) => {
  const url = `https://api.open5e.com/spells/${spellName}`;
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`Error fetching spell: ${response.statusText}`);
  }
  const spellData = await response.json();
  return spellData;
});

Fetch Monster Function:

silverbullet.registerFunction("fetchMonster", async (monsterSlug) => {
  const url = `https://api.open5e.com/monsters/${monsterSlug}`;
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`Error fetching monster: ${response.statusText}`);
  }
  const monsterData = await response.json();
  return monsterData;
});

Open5E Snippets

Now, let’s create a snippet for spells:

---
tags: template
description: inserts a spell from Open5E
hooks.snippet.slashCommand: 5e-spell-snippet
---

```template
{{escapeDirective("#let @spell = fetchSpell('|^|')")}}

# {{escapeDirective "@spell.name"}}

- **Description**: {{escapeDirective "@spell.desc"}}
- **Range**: {{escapeDirective "@spell.range"}}
- **Components**: {{escapeDirective "@spell.components"}}
- **Duration**: {{escapeDirective "@spell.duration"}}
- **Casting Time**: {{escapeDirective "@spell.casting_time"}}
- **Level**: {{escapeDirective "@spell.level"}}
- **School**: {{escapeDirective "@spell.school"}}

{{escapeDirective("/let")}}
```

Now, once you reload your system you should be able to call the function with, e.g. {{fetchSpell(“fireball”)}} or {{fetchMonster(“kobold”)}} in a template codeblock, or use the snippet to get a more neatly formatted spell by typing “/5espellsnippet”.

Monster Snippet/statblock:

Now, here’s where you come in, if you know more about this than I do. The monster statblock is a WIP, and currently is having trouble rendering actions, abilities and legendary actions (I think it’s because the API returns an array, and I can’t figure out how to get that to display correctly…). But here’s what I’m working with now:

---
tags: template
description: Inserts comprehensive monster information from Open5E
hooks.snippet.slashCommand: 5e-monster-snippet
---

{{escapeDirective("#let @monster = fetchMonster('|^|')")}}

# {{escapeDirective("@monster.name")}}
*Size*: {{escapeDirective("@monster.size")}}, *Type*: {{escapeDirective("@monster.type")}}, *Alignment*: {{escapeDirective("@monster.alignment")}}

- **Armor Class (AC)**: {{escapeDirective("@monster.armor_class")}}
- **Hit Points (HP)**: {{escapeDirective("@monster.hit_points")}} ({{escapeDirective("@monster.hit_dice")}})
- **Speed**: {{escapeDirective("@monster.speed")}}

**Abilities:**
| STR | DEX | CON | INT | WIS | CHA |
|-----|-----|-----|-----|-----|-----|
| {{escapeDirective("@monster.strength")}} | {{escapeDirective("@monster.dexterity")}} | {{escapeDirective("@monster.constitution")}} | {{escapeDirective("@monster.intelligence")}} | {{escapeDirective("@monster.wisdom")}} | {{escapeDirective("@monster.charisma")}} |

**Senses**: {{escapeDirective("@monster.senses")}}
**Languages**: {{escapeDirective("@monster.languages")}}
**Challenge Rating (CR)**: {{escapeDirective("@monster.challenge_rating")}}

{{escapeDirective("#if @monster.special_abilities")}}
## Special Abilities
{{escapeDirective("#each @monster.special_abilities")}}
{{escapeDirective("#if this")}}
- **{{escapeDirective("this.name")}}**: {{escapeDirective("this.desc")}}
{{escapeDirective("/each")}}
{{escapeDirective("/if")}}

{{escapeDirective("#if @monster.actions")}}
## Actions
{{escapeDirective("#each @monster.actions")}}
- **{{escapeDirective("this.name")}}**: {{escapeDirective("this.desc")}}
{{escapeDirective("/each")}}
{{escapeDirective("/if")}}

{{escapeDirective("#if @monster.legendary_actions")}}
## Legendary Actions
{{escapeDirective("#each @monster.legendary_actions")}}
- **{{escapeDirective("this.name")}}**: {{escapeDirective("this.desc")}} (Attack bonus: {{escapeDirective("this.attack_bonus")}}, Damage: {{escapeDirective("this.damage_dice")}} + {{escapeDirective("this.damage_bonus")}})
{{escapeDirective("/each")}}
{{escapeDirective("/if")}}

**Source**: {{escapeDirective("@monster.document__title")}} (Page {{escapeDirective("@monster.page_no")}})
*{{escapeDirective("@monster.document__url")}}*

{{escapeDirective("/let")}}

Next Steps:

Obviously there’s a lot that can be done here. The Open5E API is full of great information that would be wonderful to implement in snippets/scripts. I think nailing down the monster snippet/statblock will make things move a lot quicker, but next I’d like to replicate this approach for magic items, equipment, character facing mechanics - anything I might want in Open5E.

It might also be nice if, instead of dropping in a live-updating template block it instead prompted you for a spell/monster/whatever name and then just pasted in theactual markdown text.

If there’s a way to autofill the names that might also be a nice feature, given that many of these items have long fantasy ass names and getting it right on the first swing might be difficult.

Anyways, long story short, I think that Silverbullet+space script+Open5E really represents an awesome potential for DM’s everywhere. I can’t be the only one coming to SB from Obsidian or Notion primarily as a DM first and coder/regular productive note taker second. Would love to help work with other users to refine some DM facing SB tools.

5 Likes

We now have “Edit” and “Reload” buttons, maybe we add a third one like “Save” or “Bake” to all templates across SilverBullet, which would replace the template with its current content. I think I saw another use for that in some thread, but can’t remember now. Might help with debugging Markdown?

My first idea about that was that you get autofill for pages from Federation, like when you start typing [[!silverbullet.md/. But the scale of Open5e looks too large to keep it as a public SilverBullet instance.

I learned early how to make a Plug and now I see them as a silver bullet solution :wink:, but the hint could fetch the actual content from Open5e through their Resource Search. The way to hook up completions is really simple, I saw it in emoji Plug. I’ll try to make a demo this weekend.

I also saw that GitHub Plug has this idea of query sources, do you think that would be worth trying out? The result would make something like this possible:

```query
open5e-monster where cr = 3 and environments = "Jungle"
```
1 Like

Related to “bake” button idea @Maarrk mentioned:

I think having a button to do this per block would be good.

2 Likes

Plugs! I hadn’t looked into those! A query-able plug would be great, I was trying to figure out specifically how one could do exactly that — display any monster/spell/whatever that meet a threshold or a condition. Would love to see your demo when you’ve got it up and running

I threw together a Github repository to track scripts/snippets:

Monsters are now looking great and with the kinks of the proof of concept ironed out, I should be able to power ahead with other content types.

2 Likes