Compare commits
No commits in common. "e327c5339016d3357be8833c07f415805caf3ae6" and "bfa01456443a04973ccd13d6396e4801883ca362" have entirely different histories.
e327c53390
...
bfa0145644
97
README.md
97
README.md
|
|
@ -1,5 +1,96 @@
|
||||||
plugin to handle herolab.online connections so that you can import characters from an active stage.
|
# Obsidian Sample Plugin
|
||||||
|
|
||||||
it requires an usertoken and a campaign token.
|
This is a sample plugin for Obsidian (https://obsidian.md).
|
||||||
|
|
||||||
afterwards you can hit the ribbon button "Sync Stage" to pull characters off the stage into the designated folder (defaults to /characters)
|
This project uses TypeScript to provide type checking and documentation.
|
||||||
|
The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does.
|
||||||
|
|
||||||
|
**Note:** The Obsidian API is still in early alpha and is subject to change at any time!
|
||||||
|
|
||||||
|
This sample plugin demonstrates some of the basic functionality the plugin API can do.
|
||||||
|
- Adds a ribbon icon, which shows a Notice when clicked.
|
||||||
|
- Adds a command "Open Sample Modal" which opens a Modal.
|
||||||
|
- Adds a plugin setting tab to the settings page.
|
||||||
|
- Registers a global click event and output 'click' to the console.
|
||||||
|
- Registers a global interval which logs 'setInterval' to the console.
|
||||||
|
|
||||||
|
## First time developing plugins?
|
||||||
|
|
||||||
|
Quick starting guide for new plugin devs:
|
||||||
|
|
||||||
|
- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with.
|
||||||
|
- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it).
|
||||||
|
- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder.
|
||||||
|
- Install NodeJS, then run `npm i` in the command line under your repo folder.
|
||||||
|
- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`.
|
||||||
|
- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`.
|
||||||
|
- Reload Obsidian to load the new version of your plugin.
|
||||||
|
- Enable plugin in settings window.
|
||||||
|
- For updates to the Obsidian API run `npm update` in the command line under your repo folder.
|
||||||
|
|
||||||
|
## Releasing new releases
|
||||||
|
|
||||||
|
- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release.
|
||||||
|
- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible.
|
||||||
|
- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases
|
||||||
|
- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release.
|
||||||
|
- Publish the release.
|
||||||
|
|
||||||
|
> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`.
|
||||||
|
> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json`
|
||||||
|
|
||||||
|
## Adding your plugin to the community plugin list
|
||||||
|
|
||||||
|
- Check the [plugin guidelines](https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines).
|
||||||
|
- Publish an initial version.
|
||||||
|
- Make sure you have a `README.md` file in the root of your repo.
|
||||||
|
- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin.
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
- Clone this repo.
|
||||||
|
- Make sure your NodeJS is at least v16 (`node --version`).
|
||||||
|
- `npm i` or `yarn` to install dependencies.
|
||||||
|
- `npm run dev` to start compilation in watch mode.
|
||||||
|
|
||||||
|
## Manually installing the plugin
|
||||||
|
|
||||||
|
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
|
||||||
|
|
||||||
|
## Improve code quality with eslint (optional)
|
||||||
|
- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code.
|
||||||
|
- To use eslint with this project, make sure to install eslint from terminal:
|
||||||
|
- `npm install -g eslint`
|
||||||
|
- To use eslint to analyze this project use this command:
|
||||||
|
- `eslint main.ts`
|
||||||
|
- eslint will then create a report with suggestions for code improvement by file and line number.
|
||||||
|
- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder:
|
||||||
|
- `eslint .\src\`
|
||||||
|
|
||||||
|
## Funding URL
|
||||||
|
|
||||||
|
You can include funding URLs where people who use your plugin can financially support it.
|
||||||
|
|
||||||
|
The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fundingUrl": "https://buymeacoffee.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have multiple URLs, you can also do:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fundingUrl": {
|
||||||
|
"Buy Me a Coffee": "https://buymeacoffee.com",
|
||||||
|
"GitHub Sponsor": "https://github.com/sponsors",
|
||||||
|
"Patreon": "https://www.patreon.com/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
See https://github.com/obsidianmd/obsidian-api
|
||||||
|
|
|
||||||
553
main.ts
553
main.ts
|
|
@ -1,474 +1,25 @@
|
||||||
import * as Handlebars from 'handlebars';
|
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
|
||||||
import { App, Vault, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, TAbstractFile } from 'obsidian';
|
|
||||||
|
|
||||||
// Remember to rename these classes and interfaces!
|
// Remember to rename these classes and interfaces!
|
||||||
|
|
||||||
interface P2HLOSettings {
|
interface MyPluginSettings {
|
||||||
userToken: string;
|
mySetting: string;
|
||||||
campaignToken: string;
|
|
||||||
notesFolder: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_SETTINGS: P2HLOSettings = {
|
const DEFAULT_SETTINGS: MyPluginSettings = {
|
||||||
userToken: '',
|
mySetting: 'default'
|
||||||
campaignToken: '',
|
|
||||||
notesFolder: '/'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolName = "p2hlo-bridge"
|
export default class MyPlugin extends Plugin {
|
||||||
|
settings: MyPluginSettings;
|
||||||
const SYMBOLS = {
|
|
||||||
FREE: "⭓",
|
|
||||||
ACTION1: "⬻",
|
|
||||||
ACTION2: "⬺",
|
|
||||||
ACTION3: "⬽",
|
|
||||||
REACTION: "⬲"
|
|
||||||
}
|
|
||||||
|
|
||||||
const Endpoints = {
|
|
||||||
'acquire-access-token' : "https://api.herolab.online/v1/access/acquire-access-token",
|
|
||||||
'get-stage': "https://api.herolab.online/v1/campaign/get-stage",
|
|
||||||
'get-bulk': "https://api.herolab.online/v1/character/get-bulk"
|
|
||||||
}
|
|
||||||
|
|
||||||
interface convertReturn {
|
|
||||||
output: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class P2HLO extends Plugin {
|
|
||||||
settings: P2HLOSettings;
|
|
||||||
|
|
||||||
private byPrefixCode(obj: object, prefix: string) {
|
|
||||||
return obj[Object.keys(obj).find( predicate => predicate.startsWith(prefix))]
|
|
||||||
}
|
|
||||||
|
|
||||||
private filterKeysByPrefixCode(obj: object, prefix: string) {
|
|
||||||
const keys = [...Object.keys(obj).filter( predicate => predicate.startsWith(prefix))]
|
|
||||||
return keys.map (key => {
|
|
||||||
return {
|
|
||||||
...obj[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private prefixNumber(n: number) {
|
|
||||||
if (n < 0) {
|
|
||||||
return `-${n}`
|
|
||||||
} else if (n > 0) {
|
|
||||||
return `+${n}`
|
|
||||||
} else {
|
|
||||||
return "+0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private hloTextToMarkdown(str: string) {
|
|
||||||
return str.replace(/\{b\}/gi, "**").replace(/\{\/b\}/gi, "**").replace(/\{br\}\{br\}/gi, "{br}").replace(/\{br\}/gi, "\\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
convert(json: Object): convertReturn|null {
|
|
||||||
|
|
||||||
const actor = this.byPrefixCode(json, "actor");
|
|
||||||
|
|
||||||
const name = actor["name"];
|
|
||||||
|
|
||||||
if (actor == undefined) {
|
|
||||||
console.error("\tskipping unknown")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const characterData = actor["gameValues"];
|
|
||||||
|
|
||||||
const gameStatistics = actor["items"];
|
|
||||||
|
|
||||||
const perception = this.byPrefixCode(gameStatistics, "Perception");
|
|
||||||
|
|
||||||
const actions = this.filterKeysByPrefixCode(gameStatistics, "ab")
|
|
||||||
|
|
||||||
const skills = this.filterKeysByPrefixCode(gameStatistics,"sk")
|
|
||||||
|
|
||||||
const feats = this.filterKeysByPrefixCode(gameStatistics,"ft")
|
|
||||||
|
|
||||||
const armorClass = this.byPrefixCode(gameStatistics, "ac")
|
|
||||||
|
|
||||||
const clazz = this.filterKeysByPrefixCode(gameStatistics, "cl")
|
|
||||||
|
|
||||||
const gear = this.filterKeysByPrefixCode(gameStatistics,"gr")
|
|
||||||
|
|
||||||
const alchemicalItems = this.filterKeysByPrefixCode(gameStatistics,"ai")
|
|
||||||
|
|
||||||
const ancestry = this.byPrefixCode(gameStatistics, "an")
|
|
||||||
|
|
||||||
const languages = this.filterKeysByPrefixCode(gameStatistics,"ln")
|
|
||||||
|
|
||||||
const naturalWeapons = this.filterKeysByPrefixCode(gameStatistics,"nw")
|
|
||||||
|
|
||||||
const resources = this.filterKeysByPrefixCode(gameStatistics,"rv")
|
|
||||||
|
|
||||||
const savingThrows = this.filterKeysByPrefixCode(gameStatistics,"sv")
|
|
||||||
|
|
||||||
const weapons = this.filterKeysByPrefixCode(gameStatistics,"wp")
|
|
||||||
|
|
||||||
const focusSpells = this.filterKeysByPrefixCode(gameStatistics, "fs")
|
|
||||||
|
|
||||||
const spells = this.filterKeysByPrefixCode(gameStatistics, "sp")
|
|
||||||
|
|
||||||
// gathering done, now we print
|
|
||||||
|
|
||||||
const src = `
|
|
||||||
\`\`\`statblock
|
|
||||||
columns: 2
|
|
||||||
forcecolumns: true
|
|
||||||
layout: Basic Pathfinder 2e Layout
|
|
||||||
modifier: {{perception}}
|
|
||||||
hp: {{hp}}
|
|
||||||
ac: {{ac}}
|
|
||||||
name: "{{character_name}}"
|
|
||||||
size: {{size}}
|
|
||||||
level: {{classes}}
|
|
||||||
trait_03: "Player"
|
|
||||||
trait_04: "{{ancestry}}"
|
|
||||||
languages: "{{#languages}}{{this}}, {{/languages}}; "
|
|
||||||
perception:
|
|
||||||
- name: "Perception"
|
|
||||||
- desc: "Perception {{perception}}"
|
|
||||||
skills:
|
|
||||||
- name: "Skills"
|
|
||||||
- desc: "{{#skills}}__{{name}}__: {{mod}} (1d20{{mod}}); {{/skills}}"
|
|
||||||
abilitiyMods: [{{str}}, {{con}}, {{dex}}, {{int}}, {{cha}}, {{wis}}]
|
|
||||||
|
|
||||||
abilities_top:
|
|
||||||
{{#feats}}
|
|
||||||
- name: "[[{{link}}|{{name}}]]"
|
|
||||||
desc: "{{actions}} {{desc}}"
|
|
||||||
{{/feats}}
|
|
||||||
|
|
||||||
abilities_bot:
|
|
||||||
{{#actions}}
|
|
||||||
- name: "[[{{link}}|{{name}}]]"
|
|
||||||
desc: "{{actions}} {{desc}}"
|
|
||||||
{{/actions}}
|
|
||||||
|
|
||||||
armorclass:
|
|
||||||
- name: AC
|
|
||||||
desc: "{{ac}}; __Fort__: {{fortitude}} (1d20{{fortitude}}); __Ref__: {{reflex}} (1d20{{reflex}}); __Will__: {{will}} (1d20{{will}})"
|
|
||||||
speed: "{{speeds}}"
|
|
||||||
|
|
||||||
attacks:
|
|
||||||
{{#weapons}}
|
|
||||||
- name: "[[{{link}}|{{name}}]]"
|
|
||||||
desc: "{{actions}} {{desc}}"
|
|
||||||
{{/weapons}}
|
|
||||||
|
|
||||||
\`\`\`
|
|
||||||
### Spells
|
|
||||||
{{#spells}}
|
|
||||||
- [[{{link}}|{{name}}]]
|
|
||||||
{{/spells}}
|
|
||||||
|
|
||||||
### Inventory
|
|
||||||
*Items* in containers takes three actions to retrieve.
|
|
||||||
|
|
||||||
{{#items}}
|
|
||||||
- {{name}}
|
|
||||||
{{/items}}
|
|
||||||
`
|
|
||||||
|
|
||||||
const template = Handlebars.compile(src)
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
|
|
||||||
character_name: name,
|
|
||||||
size: characterData["actSpace"],
|
|
||||||
classes: characterData["actClassText"],
|
|
||||||
ancestry: ancestry?.name ?? "",
|
|
||||||
languages: languages.map(predicate => predicate.name),
|
|
||||||
hp: this.byPrefixCode(gameStatistics, "rvHitPoints").rvCurrent,
|
|
||||||
perception: this.prefixNumber(perception.stNet),
|
|
||||||
ac: armorClass.stNet,
|
|
||||||
str: this.prefixNumber(this.byPrefixCode(gameStatistics, "asStr").stAbScModifier),
|
|
||||||
con: this.prefixNumber(this.byPrefixCode(gameStatistics, "asCon").stAbScModifier),
|
|
||||||
dex: this.prefixNumber(this.byPrefixCode(gameStatistics, "asDex").stAbScModifier),
|
|
||||||
int: this.prefixNumber(this.byPrefixCode(gameStatistics, "asInt").stAbScModifier),
|
|
||||||
cha: this.prefixNumber(this.byPrefixCode(gameStatistics, "asCha").stAbScModifier),
|
|
||||||
wis: this.prefixNumber(this.byPrefixCode(gameStatistics, "asWis").stAbScModifier),
|
|
||||||
fortitude: this.prefixNumber(this.byPrefixCode(gameStatistics, "svFortitude").stNet),
|
|
||||||
reflex: this.prefixNumber(this.byPrefixCode(gameStatistics, "svReflex").stNet),
|
|
||||||
will: this.prefixNumber(this.byPrefixCode(gameStatistics, "svWill").stNet),
|
|
||||||
skills: skills.map( predicate => {
|
|
||||||
return {
|
|
||||||
name: predicate.name,
|
|
||||||
mod: this.prefixNumber(predicate.stNet),
|
|
||||||
level: predicate.ProfLevel,
|
|
||||||
}
|
|
||||||
}).filter(predicate => predicate.mod != "+0"),
|
|
||||||
feats: feats.map( predicate => {
|
|
||||||
let desc = ""
|
|
||||||
let name = ""
|
|
||||||
let link = ""
|
|
||||||
let actionpoints = ""
|
|
||||||
if (predicate.name.split("(").length > 1) {
|
|
||||||
name = predicate.name.split("(")[0].trim()
|
|
||||||
} else {
|
|
||||||
name = predicate.name
|
|
||||||
}
|
|
||||||
if (predicate.useInPlay) {
|
|
||||||
desc = predicate.useInPlay
|
|
||||||
} else if (predicate.name.split("(").length > 1) {
|
|
||||||
desc = predicate.name.split("(")[1].split(")")[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
link = `compendium/feats/${name.trim().replace(/\ /g, "-")}`
|
|
||||||
|
|
||||||
if (predicate.actions) {
|
|
||||||
actionpoints = predicate.actions
|
|
||||||
actionpoints = actionpoints.replace("Action1", SYMBOLS.ACTION1)
|
|
||||||
actionpoints = actionpoints.replace("Action2", SYMBOLS.ACTION2)
|
|
||||||
actionpoints = actionpoints.replace("Action3", SYMBOLS.ACTION3)
|
|
||||||
actionpoints = actionpoints.replace("Reaction", SYMBOLS.REACTION)
|
|
||||||
actionpoints = actionpoints.replace("Free", SYMBOLS.FREE)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: name,
|
|
||||||
desc: this.hloTextToMarkdown(desc),
|
|
||||||
actions: actionpoints,
|
|
||||||
link: link,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
actions: actions.map(predicate => {
|
|
||||||
let desc = ""
|
|
||||||
let name = ""
|
|
||||||
let actionpoints = ""
|
|
||||||
let link = ""
|
|
||||||
if (predicate.name.split("(").length > 1) {
|
|
||||||
name = predicate.name.split("(")[0].trim()
|
|
||||||
} else {
|
|
||||||
name = predicate.name
|
|
||||||
}
|
|
||||||
if (predicate.useInPlay) {
|
|
||||||
desc = predicate.useInPlay
|
|
||||||
} else if (predicate.name.split("(").length > 1) {
|
|
||||||
desc = predicate.name.split("(")[1].split(")")[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
link = `rules/actions/${name.trim().replace(/\ /g, "-")}`
|
|
||||||
|
|
||||||
if (predicate.actions) {
|
|
||||||
actionpoints = predicate.actions
|
|
||||||
actionpoints = actionpoints.replace("Action1", SYMBOLS.ACTION1)
|
|
||||||
actionpoints = actionpoints.replace("Action2", SYMBOLS.ACTION2)
|
|
||||||
actionpoints = actionpoints.replace("Action3", SYMBOLS.ACTION3)
|
|
||||||
actionpoints = actionpoints.replace("Reaction", SYMBOLS.REACTION)
|
|
||||||
actionpoints = actionpoints.replace("Free", SYMBOLS.FREE)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: name,
|
|
||||||
desc: this.hloTextToMarkdown(desc),
|
|
||||||
actions: actionpoints,
|
|
||||||
link: link
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
weapons: [...weapons, ...naturalWeapons].map(predciate => {
|
|
||||||
return {
|
|
||||||
name: predciate.name,
|
|
||||||
desc: this.hloTextToMarkdown(predciate.useInPlay),
|
|
||||||
actions: SYMBOLS.ACTION1,
|
|
||||||
link: `compendium/equipment/items/${predciate.name}`
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
items: [...alchemicalItems, ...gear].map(predicate => {
|
|
||||||
let itemName = predicate.name
|
|
||||||
console.log(predicate.items)
|
|
||||||
if (predicate.items) {
|
|
||||||
console.log(predicate.items, Object.values(predicate.items))
|
|
||||||
itemName += "("
|
|
||||||
itemName += Object.values(predicate.items).map(predicate2 => "*" + predicate2.name + "*").join(", ")
|
|
||||||
itemName += ")"
|
|
||||||
}
|
|
||||||
itemName = itemName.replace(/'/g, "'")
|
|
||||||
return {
|
|
||||||
name: itemName
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
spells: [...focusSpells, ...spells].map(predicate => {
|
|
||||||
let actionpoints = ""
|
|
||||||
|
|
||||||
if (predicate.actions) {
|
|
||||||
actionpoints = predicate.actions
|
|
||||||
actionpoints = actionpoints.replace("Action1", SYMBOLS.ACTION1)
|
|
||||||
actionpoints = actionpoints.replace("Action2", SYMBOLS.ACTION2)
|
|
||||||
actionpoints = actionpoints.replace("Action3", SYMBOLS.ACTION3)
|
|
||||||
actionpoints = actionpoints.replace("Reaction", SYMBOLS.REACTION)
|
|
||||||
actionpoints = actionpoints.replace("Free", SYMBOLS.FREE)
|
|
||||||
}
|
|
||||||
|
|
||||||
let desc = ""
|
|
||||||
if (predicate.useInPlay) {
|
|
||||||
desc = this.hloTextToMarkdown(predicate.useInPlay)
|
|
||||||
}
|
|
||||||
|
|
||||||
let link = `compendium/spells/${predicate.name.trim()}`
|
|
||||||
|
|
||||||
link = link.split(" (")[0]
|
|
||||||
link = link.replace(/\ /g, "-")
|
|
||||||
|
|
||||||
let level = predicate.spLevelBase
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
name: predicate.name,
|
|
||||||
actions: actionpoints,
|
|
||||||
link,
|
|
||||||
level,
|
|
||||||
desc,
|
|
||||||
}
|
|
||||||
}) ,
|
|
||||||
speeds : this.byPrefixCode(gameStatistics, "mvSpeed")?.stNet ?? 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = template(data)
|
|
||||||
|
|
||||||
return {
|
|
||||||
output,
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchAccessToken() {
|
|
||||||
const { accessToken } = await fetch(Endpoints['acquire-access-token'], {
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
refreshToken: this.settings.userToken,
|
|
||||||
toolName,
|
|
||||||
lifespan: 0
|
|
||||||
})
|
|
||||||
}).then( response => response.json() )
|
|
||||||
return accessToken
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchStage(accessToken: String): Promise<String[]> {
|
|
||||||
return new Promise( async (resolve, reject) => {
|
|
||||||
if (accessToken) {
|
|
||||||
const {wait, castList} = await fetch(Endpoints['get-stage'], {
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
accessToken: accessToken,
|
|
||||||
campaignToken: this.settings.campaignToken
|
|
||||||
})
|
|
||||||
}).then( response => response.json() )
|
|
||||||
|
|
||||||
if (wait != 0) {
|
|
||||||
await new Promise( resolve1 => setTimeout( resolve1, wait))
|
|
||||||
}
|
|
||||||
resolve(castList as String[])
|
|
||||||
}
|
|
||||||
reject()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchCharacters(accessToken: String, castList: String[]): Promise<Object[]> {
|
|
||||||
return new Promise( async (resolve, reject) => {
|
|
||||||
if (accessToken && castList.length > 0) {
|
|
||||||
|
|
||||||
const individualCharacters: any = castList.map( castId => {
|
|
||||||
return {
|
|
||||||
elementToken: this.settings.campaignToken,
|
|
||||||
castId: castId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const {wait, characters} = await fetch(Endpoints['get-bulk'], {
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
elementToken: this.settings.campaignToken,
|
|
||||||
accessToken: accessToken,
|
|
||||||
characters: individualCharacters
|
|
||||||
})
|
|
||||||
}).then( response => response.json() )
|
|
||||||
|
|
||||||
if (wait != 0) {
|
|
||||||
await new Promise( resolve1 => setTimeout( resolve1, wait))
|
|
||||||
}
|
|
||||||
resolve(characters)
|
|
||||||
}
|
|
||||||
reject()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async preflightTest() {
|
|
||||||
if (this.settings.userToken == '' || this.settings.campaignToken == '') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
async performTask() {
|
|
||||||
if (await this.preflightTest()) {
|
|
||||||
const accessToken = await this.fetchAccessToken()
|
|
||||||
const castMember = await this.fetchStage(accessToken)
|
|
||||||
console.log(castMember)
|
|
||||||
if (castMember.length == 0) {
|
|
||||||
new Notice("No Castmember found on Stage. Did you forgot to start a session?")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const characters = await this.fetchCharacters(accessToken, castMember)
|
|
||||||
console.log(characters)
|
|
||||||
try {
|
|
||||||
const folder = this.app.vault.getFolderByPath(this.settings.notesFolder)
|
|
||||||
await this.app.vault.trash(folder as TAbstractFile, true)
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await this.app.vault.createFolder(this.settings.notesFolder)
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
for (let character of characters) {
|
|
||||||
const result = this.convert(character.export.actors)
|
|
||||||
if (result != null) {
|
|
||||||
try {
|
|
||||||
await this.app.vault.create(`${this.settings.notesFolder}/${result.name}.md`, result.output)
|
|
||||||
} catch (e) {
|
|
||||||
const file = this.app.vault.getFileByPath(`/${this.settings.notesFolder}/${result.name}.md`)
|
|
||||||
if (file) {
|
|
||||||
await this.app.vault.modify(file, result.output)
|
|
||||||
} else {
|
|
||||||
console.error(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new Notice("Import completed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async onload() {
|
async onload() {
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
|
||||||
// This creates an icon in the left ribbon.
|
// This creates an icon in the left ribbon.
|
||||||
const ribbonIconEl = this.addRibbonIcon('dice', 'Sync Stage', async (evt: MouseEvent) => {
|
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
|
||||||
// Called when the user clicks the icon.
|
// Called when the user clicks the icon.
|
||||||
await this.performTask()
|
new Notice('This is a notice!');
|
||||||
|
|
||||||
});
|
});
|
||||||
// Perform additional things with the ribbon
|
// Perform additional things with the ribbon
|
||||||
ribbonIconEl.addClass('my-plugin-ribbon-class');
|
ribbonIconEl.addClass('my-plugin-ribbon-class');
|
||||||
|
|
@ -479,10 +30,38 @@ attacks:
|
||||||
|
|
||||||
// This adds a simple command that can be triggered anywhere
|
// This adds a simple command that can be triggered anywhere
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'sync-herolabonline-stage',
|
id: 'open-sample-modal-simple',
|
||||||
name: 'Sync Stage',
|
name: 'Open sample modal (simple)',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.preflightTest()
|
new SampleModal(this.app).open();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// This adds an editor command that can perform some operation on the current editor instance
|
||||||
|
this.addCommand({
|
||||||
|
id: 'sample-editor-command',
|
||||||
|
name: 'Sample editor command',
|
||||||
|
editorCallback: (editor: Editor, view: MarkdownView) => {
|
||||||
|
console.log(editor.getSelection());
|
||||||
|
editor.replaceSelection('Sample Editor Command');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// This adds a complex command that can check whether the current state of the app allows execution of the command
|
||||||
|
this.addCommand({
|
||||||
|
id: 'open-sample-modal-complex',
|
||||||
|
name: 'Open sample modal (complex)',
|
||||||
|
checkCallback: (checking: boolean) => {
|
||||||
|
// Conditions to check
|
||||||
|
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||||
|
if (markdownView) {
|
||||||
|
// If checking is true, we're simply "checking" if the command can be run.
|
||||||
|
// If checking is false, then we want to actually perform the operation.
|
||||||
|
if (!checking) {
|
||||||
|
new SampleModal(this.app).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This command will only show up in Command Palette when the check function returns true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -512,10 +91,26 @@ attacks:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SampleSettingTab extends PluginSettingTab {
|
class SampleModal extends Modal {
|
||||||
plugin: P2HLO;
|
constructor(app: App) {
|
||||||
|
super(app);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(app: App, plugin: P2HLO) {
|
onOpen() {
|
||||||
|
const {contentEl} = this;
|
||||||
|
contentEl.setText('Woah!');
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
const {contentEl} = this;
|
||||||
|
contentEl.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SampleSettingTab extends PluginSettingTab {
|
||||||
|
plugin: MyPlugin;
|
||||||
|
|
||||||
|
constructor(app: App, plugin: MyPlugin) {
|
||||||
super(app, plugin);
|
super(app, plugin);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
|
|
@ -526,33 +121,13 @@ class SampleSettingTab extends PluginSettingTab {
|
||||||
containerEl.empty();
|
containerEl.empty();
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName('User Token')
|
.setName('Setting #1')
|
||||||
.setDesc('enter your user token from your herolab.online apprentice or higher account')
|
.setDesc('It\'s a secret')
|
||||||
.addText(text => text
|
.addText(text => text
|
||||||
.setPlaceholder('User Token')
|
.setPlaceholder('Enter your secret')
|
||||||
.setValue(this.plugin.settings.userToken)
|
.setValue(this.plugin.settings.mySetting)
|
||||||
.onChange(async (value) => {
|
.onChange(async (value) => {
|
||||||
this.plugin.settings.userToken = value;
|
this.plugin.settings.mySetting = value;
|
||||||
await this.plugin.saveSettings();
|
|
||||||
}));
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Campaign Token')
|
|
||||||
.setDesc('enter the Element Token of your Campaign you want to get the characters from')
|
|
||||||
.addText(text => text
|
|
||||||
.setPlaceholder('element token')
|
|
||||||
.setValue(this.plugin.settings.campaignToken)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
this.plugin.settings.campaignToken = value;
|
|
||||||
await this.plugin.saveSettings();
|
|
||||||
}));
|
|
||||||
new Setting(containerEl)
|
|
||||||
.setName('Character note folder')
|
|
||||||
.setDesc('enter the path where the character notes should be placed in')
|
|
||||||
.addText(text => text
|
|
||||||
.setPlaceholder('/')
|
|
||||||
.setValue(this.plugin.settings.notesFolder)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
this.plugin.settings.notesFolder = value;
|
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"id": "pf2e-herolab-bridge",
|
"id": "sample-plugin",
|
||||||
"name": "Pf2e Herolab Bridge",
|
"name": "Sample Plugin",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"minAppVersion": "0.15.0",
|
"minAppVersion": "0.15.0",
|
||||||
"description": "Allows to keep pathfinder 2e characters in a campaign synchronised",
|
"description": "Demonstrates some of the capabilities of the Obsidian API.",
|
||||||
"author": "org.macniel",
|
"author": "Obsidian",
|
||||||
"authorUrl": "",
|
"authorUrl": "https://obsidian.md",
|
||||||
"fundingUrl": "",
|
"fundingUrl": "https://obsidian.md/pricing",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-pf2e-herolab-bridge",
|
"name": "obsidian-sample-plugin",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "a pathfinder 2e herolab bridge to sync player characters",
|
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
|
|
@ -20,8 +20,5 @@
|
||||||
"obsidian": "latest",
|
"obsidian": "latest",
|
||||||
"tslib": "2.4.0",
|
"tslib": "2.4.0",
|
||||||
"typescript": "4.7.4"
|
"typescript": "4.7.4"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"handlebars": "^4.7.8"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue