This guide explains which elements need to be updated whenever a new novel, translator, or hosting site is added. Please update the following files accordingly.
novel_mappings.pyThis file contains mapping data for each hosting site. When adding a new novel or new hosting site, you will update:
HOSTING_SITE_DATA
feed_url: The URL for the feed (e.g., free chapters)paid_feed_url: If site has URL for paid feedcomments_feed_url: If site has URL for comments.translator: Your username on that sitehost_logo: The URL for the hosting siteโs logocoin_emoji: currency used for paid chapters like ๐ฅ or ๐ชnovels: A dictionary that maps each novel title to:
discord_role_id: The Discord role IDnovel_url: The manual URL for the novelโs main pagefeatured_image: The URL for the novelโs featured imagepub_date_override: The override for the systemโs default time of scrapingwebhook-only fields: Contains information needed for webhook-discord scripts.๐ฆ
pyproject.tomllets other projects (like the Discord webhook script) install this repo as a package using pip. It tells Python where to findnovel_mappings.pyso the webhook scripts can always pull the latest novel data straight from here ๐โจ.
HOSTING_SITE_DATA = {
"Dragonholic": {
"feed_url": "https://dragonholic.com/feed/manga-chapters/",
"comments_feed_url": "https://dragonholic.com/comments/feed/",
"translator": "Cannibal Turtle",
"host_logo": "https://dragonholic.com/wp-content/uploads/2025/01/Web-Logo-White.png",
"coin_emoji": "๐ฅ",
"novels": {
"Quick Transmigration: The Villain Is Too Pampered and Alluring": {
"discord_role_id": "<@&1329391480435114005>",
"novel_url": "https://dragonholic.com/novel/quick-transmigration-the-villain-is-too-pampered-and-alluring/",
"featured_image": "https://dragonholic.com/wp-content/uploads/2024/08/177838.jpg",
"pub_date_override": {"hour": 12, "minute": 0, "second": 0}
# โโโ webhook-only fields โโโ
"chapter_count": "1184 chapters + 8 extras",
"last_chapter": "Extra 8",
"start_date": "31/8/2024",
"free_feed": "https://cannibal-turtle.github.io/rss-feed/free_chapters_feed.xml",
"paid_feed": "https://cannibal-turtle.github.io/rss-feed/paid_chapters_feed.xml",
"custom_emoji": ":man_supervillain:",
"discord_role_url":"https://discord.com/channels/1329384099609051136/1329419555600203776/1330466188349800458",
"history_file": "tvitpa_history.json"
},
# Second novel here
},
# Add more novels as needed.
}
},
# Add additional hosting sites here.
}
get_nsfw_novels()def get_nsfw_novels():
return [
# List NSFW novel titles here, e.g.:
"Some NSFW Novel Title"
]
host_utils.pysplit_title_dragonholic(full_title) โ Splits a chapter title into main_title, chaptername, and nameextend.chapter_num_dragonholic(chaptername) โ Extracts numeric values from chapter names.clean_description(raw_desc) โ Cleans raw HTML descriptions by removing unnecessary elements.extract_pubdate_from_soup(chap) โ Parses chapter <li> elements to extract absolute or relative publication dates.novel_has_paid_update_async(session, novel_url) โ Checks if a novel has a premium (paid) update within the last 7 days.scrape_paid_chapters_async(session, novel_url, host) โ Scrapes the paid chapter list from Dragonholic.format_volume_from_url(url, main_title) โ Utility to infer volume names from URLs.split_comment_title_dragonholic(comment_title) โ Extracts the novel title from the comment title string.extract_chapter_dragonholic(link) โ Extracts a readable chapter label from a URL.
๐ก Note: For Dragonholic paid chapters, volume names are scraped directly from the DOM (e.g., li.parent.has-child > a.has-child). No need to reconstruct them from URLs.
To get the appropriate utility functions for a specific host, use:
get_host_utils("Dragonholic")
---
novel_mappings.py, add or update the novels dictionary under the appropriate host in HOSTING_SITE_DATA.get_nsfw_novels().novel_mappings.py, create a new entry in HOSTING_SITE_DATA with:
feed_url, translator, host_logo, and a novels dictionary.host_utils.py, create new siteโspecific functions and group them in a new dictionary. Update get_host_utils(host) to return that dictionary.Following these steps keeps your feed generator modular and easy to update.
Each generated .xml feed (free or paid) will contain structured
<item>
<title>Quick Transmigration: The Villain Is Too Pampered and Alluring</title>
<volume>ใArc 5ใThe Fake Daughter Will Not Be a Cannon Fodder</volume>
<chaptername>Chapter 250</chaptername>
<nameextend>***Uglier Than a Monkey***</nameextend>
<link>https://dragonholic.com/novel/.../chapter-250/</link>
<description><![CDATA[A deadly twist awaits in the mirror world...]]></description>
<category>SFW</category>
<translator>Cannibal Turtle</translator>
<discord_role_id><![CDATA[<@&1329XXXXXX>]]></discord_role_id>
<featuredImage url="https://dragonholic.com/.../cover.jpg"/>
<coin>๐ฅย 10</coin>
<pubDate>Fri, 18 Apr 2025 12:00:00 +0000</pubDate>
<host>Dragonholic</host>
<hostLogo url="https://dragonholic.com/.../logo.png"/>
<guid isPermaLink="false">chapter-250-guid</guid>
</item>
MISTMINT_FORCE_STATE="0". Runs on scheduleMISTMINT_FORCE_STATE="1" or no cookie. Accepts manual paid chapter entry via manual_scripts/mistmint_state.json and updates manual_scripts/paid_history.json. If mode is switched, clear paid_history.json and update mistmint_state.json.novel_mappings.py)Add:
HOSTING_SITE_DATA["Mistmint Haven"] = {
"token_secret": "MISTMINT_COOKIE", # name of the repo secret to read at runtime
}
MISTMINT_COOKIE โ logged-in cookie stringPAT_GITHUB โ for repo dispatch to other botsDISCORD_BOT_TOKEN, DISCORD_MOD_CHANNEL_ID โ for alert postscomments.py โ calls maybe_dispatch_token_alerts if token is expiring โ send_token_alert.yml โ send_token_alert.py.token_secret, it reads that env var, decodes JWT exp, and if โค 1 day, fires:
repository_dispatch โ event_type: token-expiring..token_alert_state.json stores the last exp per (host, token_secret) so you arenโt spammed hourly.Now also updates <category> if <chaptername> and <nameextend> has these keywords:
This system keeps existing Discord novel cards up-to-date whenever new free chapters are announced, without reposting or changing formatting.
It is designed to work across repositories and servers.
When a new free chapter is detected and posted:
repository_dispatch event to the rss-feed repotitle + host โ short_code (via HOSTING_SITE_DATA)
๐ Short codes are NOT passed between repos
Resolution happens only inside the updater Python script, using mappings.
Title + Host
โ
HOSTING_SITE_DATA
โ
short_code
The updater uses a static map of existing Discord messages that should be updated.
Example (novel_status_targets.json):
{
"TVITPA": [
{
"channel_id": "123456789",
"message_id": "123456789"
}
],
"TDLBKGC": [
{
"channel_id": "123456789",
"message_id": "123456789"
}
],
"ATVHE": [
{
"channel_id": "123456789",
"message_id": "123456789"
}
]
}
*Ongoing* / *Completed*Next free chapter live <t:UNIX:R>All chapters are now freeIf a serverโs embed does not have a Role field, the updater skips it safely.
The updater:
*Completed**Ongoing*Next free chapter live <t:โฆ:R>All chapters are now free_Free release schedule not available_Because this system triggers across repositories, a PAT is required.
| Secret | Repo | Purpose |
|โ|โ|โ|
| PAT_GITHUB | Source repo (in this case discord-webhook) | Dispatch events to rss-feed |
| DISCORD_BOT_TOKEN | rss-feed | Edit existing Discord messages |
| MISTMINT_COOKIE | rss-feed | Paid/free API access (if applicable) |
โ ๏ธ
GITHUB_TOKENis not sufficient for cross-repo dispatch.
To enable automatic status updates for a new novel:
HOSTING_SITE_DATAshort_codenovel_status_targets.jsontools/publish_single_novel.py can serve as template for first run.Update NOVEL_META in tools/publish_single_novel.py` for every new novel; Color = embed color; Omit forum_post_id if it doesnโt belong to any forum. Example:
NOVEL_META = {
"TVITPA": {"color": "#f8d8c9", "forum_post_id": "1444214902322368675"},
update_novel_status.py looks for the status field for updates.Role field only shows for ARCHIVE_CHANNEL_ID listed and is omitted unless message is sent to that channel.โ ๏ธ Cookie must be available as the environment variable named MISTMINT_COOKIE (or whatever name you put in
token_secretunderHOSTING_SITE_DATA)
publish_single_novel.yml script with a shortcode and channel ID for the first run, and it will update novel_status_targets.json automatically.update_novel_status.py trigerred automatically by update_novel_status.yml everytime new free chapter is announced, will use the shortcode, channel, and message ID stored in novel_status_targets.json.Discord free chapter announcement
โ
Trigger GitHub event
โ
Recompute novel status
โ
Edit existing embeds
Result: