Wiki

Pull pages from the community wikis as Markdown, so a mechanic's description sits in your app next to the numbers the catalogs give you. Two wikis are configured, tables and infoboxes are converted for you, and every page comes back with the URL it came from.

Read A Page

One call takes a page title and returns Markdown. The result carries the source it used, the title it actually resolved to, and the page URL.

import { fetchWikiPageAsMarkdown } from 'thetowersdk/wiki'

const page = await fetchWikiPageAsMarkdown('Golden Tower')

console.log(page.sourceId)        // 'fandom'
console.log(page.resolvedTitle)   // 'Golden Tower'
console.log(page.url)             // 'https://the-tower-idle-tower-defense.fandom.com/wiki/Golden_Tower'

console.log(page.markdown)
// ## Description
//
// Turns the tower golden for a period of time after a period of time. While
// active you receive a multiplier cash and coins from enemy kills.
//
// ## Workshop
// …

Search For One

searchWiki returns ranked titles with their source and URL, which is what you want when a player types a phrase rather than an exact page name.

import { searchWiki } from 'thetowersdk/wiki'

const hits = await searchWiki('golden tower')

console.log(hits.length)   // 10
console.log(hits[0])
// {
//   title: 'Golden Tower',
//   sourceId: 'fandom',
//   url: 'https://the-tower-idle-tower-defense.fandom.com/wiki/Golden_Tower'
// }

// Search, then read the best match.
const page = await fetchWikiPageAsMarkdown(hits[0].title)

The Two Wikis

WIKI_SOURCES lists what is configured. Fandom is the default and the broader of the two; Game Vault is the other. Each entry carries its API URL, the base for page links, and how its search behaves.

import { WIKI_SOURCES, DEFAULT_WIKI_SOURCE_ID, getWikiSource } from 'thetowersdk/wiki'

for (const source of WIKI_SOURCES) {
  console.log(source.id, '—', source.label)
}
// fandom    — Fandom — The Tower: Idle Tower Defense Wiki
// gamevault — Game Vault — The Tower Wiki and Guides

console.log(DEFAULT_WIKI_SOURCE_ID)   // 'fandom'

const fandom = getWikiSource('fandom')
console.log(fandom.apiUrl)        // 'https://…fandom.com/api.php'
console.log(fandom.pageUrlBase)   // 'https://…fandom.com/wiki/'
console.log(fandom.searchMode)    // 'search'  — Game Vault uses 'prefix'

Try Both

fetchWikiPageFromAnySource looks in each configured wiki and returns the first page it finds, which covers titles that exist on one and not the other.

import { fetchWikiPageFromAnySource } from 'thetowersdk/wiki'

const page = await fetchWikiPageFromAnySource('Guardian')
console.log(page.sourceId)   // whichever one had it

Show When It Was Last Edited

Wiki prose ages. fetchWikiPageLastEdited tells you when the page last changed, so a cached copy can show its age or be refreshed on a schedule.

import { fetchWikiPageLastEdited } from 'thetowersdk/wiki'

const revision = await fetchWikiPageLastEdited('Golden Tower')

console.log(revision.lastEdited)   // '2024-05-01T03:08:24Z'
console.log(revision.sourceId)     // 'fandom'
console.log(revision.title)        // 'Golden Tower'

Work With The Raw Wikitext

fetchWikiWikitext gives you the page source, for when you want to search it, diff it, or transform something before rendering.

import { fetchWikiWikitext } from 'thetowersdk/wiki'

const { wikitext, source, resolvedTitle } = await fetchWikiWikitext('Golden Tower')

console.log(resolvedTitle)   // 'Golden Tower'
console.log(source.id)       // 'fandom'
console.log(wikitext.slice(0, 80))

For Markdown, reach for fetchWikiPageAsMarkdown rather than converting by hand — the converter needs the page's template and transclusion context to expand tabs and infoboxes, and the fetcher assembles that for you.

Judge What A Page Carries

scoreFandomMarkdownQuality returns a number for how substantial a converted page is, and countMarkdownTableRows counts its table rows. Both help when you are choosing between two candidate pages, or deciding whether one is worth caching.

import { countMarkdownTableRows, scoreFandomMarkdownQuality } from 'thetowersdk/wiki'

console.log(scoreFandomMarkdownQuality(page.markdown))   // 953
console.log(countMarkdownTableRows(page.markdown))       // 0 — this page is prose

// Pick the richer of two candidates.
const best = [pageA, pageB].sort(
  (left, right) =>
    scoreFandomMarkdownQuality(right.markdown) - scoreFandomMarkdownQuality(left.markdown)
)[0]

Images

Wiki markup references images by file name. These resolve those names to the URLs the wiki serves, so an embedded page renders with its pictures.

import {
  readFandomFileNamesFromMarkdown,
  fetchFandomImageUrlMap,
  applyFandomImageUrlMap
} from 'thetowersdk/wiki'

const names = readFandomFileNamesFromMarkdown(page.markdown)

// A prose page returns none, so there is nothing to fetch.
if (names.length > 0) {
  const urls = await fetchFandomImageUrlMap(names)
  const withImages = applyFandomImageUrlMap(page.markdown, urls)
}

Attribution

The pages are written by the wiki communities. WIKI_ATTRIBUTION is the credit line to show wherever you display their content, and WIKI_CREDIT_SOURCES lists the wikis it names.

import { WIKI_ATTRIBUTION, WIKI_CREDIT_SOURCES } from 'thetowersdk/wiki'

console.log(WIKI_ATTRIBUTION)
// 'Wiki content is written and maintained by the contributors to Fandom — The
//  Tower: Idle Tower Defense Wiki and Game Vault — The Tower Wiki and Guides.
//  Each page links back to its source, where its authors are listed.'

Prose Beside The Numbers

The combination worth building: the wiki says what a mechanic does, the catalogs say what it costs, and the knowledge graph says what people get wrong about it.

import { fetchWikiPageAsMarkdown } from 'thetowersdk/wiki'
import { uwStoneChartData } from 'thetowersdk/data'
import { trapsFor, resolve } from 'thetowersdk/knowledge'

async function mechanic(name) {
  const page = await fetchWikiPageAsMarkdown(name)
  const weapon = Object.values(uwStoneChartData).find((entry) => entry.name === name)
  const id = resolve(name)

  return {
    description: page.markdown,
    source: page.url,
    stats: weapon?.stats.map((stat) => ({ name: stat.name, levels: stat.levels.length })),
    watchOutFor: id ? trapsFor(id) : []
  }
}

console.log(await mechanic('Golden Tower'))

Knowledge Graph → · Catalogs → · Reach It From An Assistant →