Commit cc082e51ff
Verified · cmc
Layout: unified · split
README.nfo +5 −1
| @@ -41,7 +41,11 @@ | ||
| 41 | 41 | │ LOGO: --logo art.asc places art above the box │ |
| 42 | 42 | │ BANNERS: --presents --footer scene boilerplate │ |
| 43 | 43 | │ │ |
| 44 | │ OPTIONS: -o -c -g -s -w --style --layout --logo │ | |
| 44 | │ PROFILE: -p group.toml supplies logo, footer, │ | |
| 45 | │ members, couriers, boards, affiliates, │ | |
| 46 | │ news, outposts. release cfg overrides it. │ | |
| 47 | │ │ | |
| 48 | │ OPTIONS: -o -c -p -g -s -w --style --layout --logo │ | |
| 45 | 49 | │ --encoding --presents --footer -i --no-input │ |
| 46 | 50 | │ │ |
| 47 | 51 | │ Generated by nfogen itself. │ |
examples/krz.profile.toml added +82
| @@ -0,0 +1,82 @@ | ||
| 1 | # A group profile: the reusable KRZ boilerplate. Point any release config at | |
| 2 | # it with `profile = "examples/krz.profile.toml"` (or nfogen -p PATH) and it | |
| 3 | # supplies the logo, roster, boards, footer and house style. The release | |
| 4 | # config only carries per-release fields (title, cracker, notes, ...). | |
| 5 | ||
| 6 | group = "KRZ" | |
| 7 | site = "krz.sh" | |
| 8 | style = "double" | |
| 9 | layout = "panel" | |
| 10 | presents = true | |
| 11 | footer = true | |
| 12 | logo = "examples/krz-logo.asc" | |
| 13 | logo_encoding = "utf8" | |
| 14 | ||
| 15 | footer_text = """ | |
| 16 | SUPPORT THE COMPANIES THAT PRODUCE QUALITY SOFTWARE | |
| 17 | if you enjoyed this release, buy it! | |
| 18 | KRZ does not accept cash or hardware of any kind. | |
| 19 | """ | |
| 20 | ||
| 21 | news = """ | |
| 22 | Welcome Celestial Wizard back to the group! | |
| 23 | We are in need of couriers for the release rush -- especially | |
| 24 | those available in the daytime. Contact a senior member. | |
| 25 | """ | |
| 26 | ||
| 27 | members = [ | |
| 28 | "Avalon", "Black Spyrit", "Butcher", "The Chairman", "Darwin", | |
| 29 | "Doctor No", "Eldar", "Grimstalk", "Hologram", "Intrepid", "Jack", | |
| 30 | "Knightmare", "Marko Ramius", "ONYX", "Razor Blade", "Red Baron", | |
| 31 | "Savage Steel", "StingRay", "The Unknown", "The Witch King", | |
| 32 | ] | |
| 33 | ||
| 34 | [couriers] | |
| 35 | Director = ["The Unforgiven"] | |
| 36 | Senior = ["Ur Lord", "Lord Tiamat", "Wind Walker", "Bubba"] | |
| 37 | Elite = ["Shadow Phantom", "Baddog", "Load Master", "Joebee", "Azmodeus"] | |
| 38 | Trial = ["Dead Pool", "Beach Bum", "Black Mage", "Ben Jammin"] | |
| 39 | ||
| 40 | [[boards]] | |
| 41 | name = "Suburbia" | |
| 42 | role = "World HQ" | |
| 43 | sysop = "The Chairman" | |
| 44 | phone = "ITS/PRIVATE" | |
| 45 | ||
| 46 | [[boards]] | |
| 47 | name = "TerrorDome" | |
| 48 | role = "Canadian HQ" | |
| 49 | sysop = "StingRay" | |
| 50 | phone = "ITS/PRIVATE" | |
| 51 | ||
| 52 | [[boards]] | |
| 53 | name = "Kindergarden" | |
| 54 | role = "European HQ" | |
| 55 | sysop = "Doctor No" | |
| 56 | phone = "+47-7-525156" | |
| 57 | ||
| 58 | [[boards]] | |
| 59 | name = "Apocalypse" | |
| 60 | role = "Courier HQ" | |
| 61 | sysop = "P.O.W." | |
| 62 | phone = "ITS/PRIVATE" | |
| 63 | ||
| 64 | [[affiliates]] | |
| 65 | name = "Reign In Blood" | |
| 66 | role = "Affiliate" | |
| 67 | sysop = "Uriel" | |
| 68 | phone = "718/698-1969" | |
| 69 | ||
| 70 | [[affiliates]] | |
| 71 | name = "So-Krate's" | |
| 72 | role = "Affiliate" | |
| 73 | sysop = "Philosopher" | |
| 74 | phone = "310/578-7226" | |
| 75 | ||
| 76 | [[affiliates]] | |
| 77 | name = "The Space Zone" | |
| 78 | role = "Affiliate" | |
| 79 | sysop = "Death Ray" | |
| 80 | phone = "301/416-0118" | |
| 81 | ||
| 82 | outposts = ["Infinite Darkness", "The Atomic Cafe", "Sanctuary"] | |
examples/nightfall-demo.cp437.nfo added +83
| @@ -0,0 +1,83 @@ | ||
| 1 | _ _______ ___ | |
| 2 | | |/ / _ \ \ / / | | |
| 3 | | ' /| |_) \ V /| | k r a z y w a r e z | |
| 4 | | . \| _ < | | |_| | |
| 5 | |_|\_\_| \_\|_| (_) ------- since 1985 ------- | |
| 6 | ||
| 7 | ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» | |
| 8 | º N I G H T F A L L . P R O T O C O L . V 1 . 4 - K R Z º | |
| 9 | º [ KRZ ] krz.sh º | |
| 10 | º -={ KRZ proudly presents }=- º | |
| 11 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 12 | º Release Information º Game Information º | |
| 13 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÎÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 14 | º DATE : 2026-08-08 º TYPE : GAME º | |
| 15 | º CRACKER : Lime º PUBLISHER : Nocturne Interactive º | |
| 16 | º SUPPLIER : The Witch King º FORMAT : ISO + patch º | |
| 17 | º PACKAGER : Renegade Chemist º DISKS : 3 x CD º | |
| 18 | º PROTECTION : SafeDisc + Serial º SIZE : 1.87 GB º | |
| 19 | º INSTALL : unzip, run SETUP, ap.. º FILES : 96 x 20 MB º | |
| 20 | º RATING : 9/10 º URL : krz.sh/nightfall º | |
| 21 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 22 | º NOTES º | |
| 23 | º º | |
| 24 | º A stealth-action thriller set in a city that never sleeps. º | |
| 25 | º Cracked clean -- no disc check, no online activation. º | |
| 26 | º º | |
| 27 | º Mount or burn the ISO, install, then run the KRZ patch over º | |
| 28 | º the game directory. Serial is in the keygen. Tested working. º | |
| 29 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 30 | º GREETS º | |
| 31 | º º | |
| 32 | º razor-blade jack the-couriers everyone-in-the-scene º | |
| 33 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 34 | º GROUP NEWS º | |
| 35 | º º | |
| 36 | º Welcome Celestial Wizard back to the group! º | |
| 37 | º We are in need of couriers for the release rush -- especially º | |
| 38 | º those available in the daytime. Contact a senior member. º | |
| 39 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 40 | º MEMBERS º | |
| 41 | º º | |
| 42 | º Avalon Black Spyrit Butcher The Chairman Darwin Doctor No º | |
| 43 | º Eldar Grimstalk Hologram Intrepid Jack Knightmare Marko º | |
| 44 | º Ramius ONYX Razor Blade Red Baron Savage Steel StingRay The º | |
| 45 | º Unknown The Witch King º | |
| 46 | º º | |
| 47 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 48 | º COURIERS º | |
| 49 | º º | |
| 50 | º - Director - º | |
| 51 | º The Unforgiven º | |
| 52 | º º | |
| 53 | º - Senior - º | |
| 54 | º Ur Lord Lord Tiamat Wind Walker Bubba º | |
| 55 | º º | |
| 56 | º - Elite - º | |
| 57 | º Shadow Phantom Baddog Load Master Joebee Azmodeus º | |
| 58 | º º | |
| 59 | º - Trial - º | |
| 60 | º Dead Pool Beach Bum Black Mage Ben Jammin º | |
| 61 | º º | |
| 62 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 63 | º BOARDS º | |
| 64 | º º | |
| 65 | º BOARD ROLE SYSOP CONTACT º | |
| 66 | º Suburbia World HQ The Chairman ITS/PRIVATE º | |
| 67 | º TerrorDome Canadian HQ StingRay ITS/PRIVATE º | |
| 68 | º Kindergarden European HQ Doctor No +47-7-525156 º | |
| 69 | º Apocalypse Courier HQ P.O.W. ITS/PRIVATE º | |
| 70 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 71 | º AFFILIATES º | |
| 72 | º º | |
| 73 | º BOARD ROLE SYSOP CONTACT º | |
| 74 | º Reign In Blood Affiliate Uriel 718/698-1969 º | |
| 75 | º So-Krate's Affiliate Philosopher 310/578-7226 º | |
| 76 | º The Space Zone Affiliate Death Ray 301/416-0118 º | |
| 77 | ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ | |
| 78 | º º | |
| 79 | º SUPPORT THE COMPANIES THAT PRODUCE QUALITY SOFTWARE º | |
| 80 | º if you enjoyed this release, buy it! º | |
| 81 | º KRZ does not accept cash or hardware of any kind. º | |
| 82 | º º | |
| 83 | ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ | |
examples/nightfall-demo.nfo added +83
| @@ -0,0 +1,83 @@ | ||
| 1 | _ _______ ___ | |
| 2 | | |/ / _ \ \ / / | | |
| 3 | | ' /| |_) \ V /| | k r a z y w a r e z | |
| 4 | | . \| _ < | | |_| | |
| 5 | |_|\_\_| \_\|_| (_) ------- since 1985 ------- | |
| 6 | ||
| 7 | ╔�����������������������������������������������������������������������������╗ | |
| 8 | â•‘ N I G H T F A L L . P R O T O C O L . V 1 . 4 - K R Z â•‘ | |
| 9 | â•‘ [ KRZ ] krz.sh â•‘ | |
| 10 | â•‘ -={ KRZ proudly presents }=- â•‘ | |
| 11 | ╠��������������������������������������╦��������������������������������������╣ | |
| 12 | â•‘ Release Information â•‘ Game Information â•‘ | |
| 13 | ╠��������������������������������������╬��������������������������������������╣ | |
| 14 | â•‘ DATE : 2026-08-08 â•‘ TYPE : GAME â•‘ | |
| 15 | â•‘ CRACKER : Lime â•‘ PUBLISHER : Nocturne Interactive â•‘ | |
| 16 | â•‘ SUPPLIER : The Witch King â•‘ FORMAT : ISO + patch â•‘ | |
| 17 | â•‘ PACKAGER : Renegade Chemist â•‘ DISKS : 3 x CD â•‘ | |
| 18 | â•‘ PROTECTION : SafeDisc + Serial â•‘ SIZE : 1.87 GB â•‘ | |
| 19 | â•‘ INSTALL : unzip, run SETUP, ap.. â•‘ FILES : 96 x 20 MB â•‘ | |
| 20 | â•‘ RATING : 9/10 â•‘ URL : krz.sh/nightfall â•‘ | |
| 21 | ╠��������������������������������������╩��������������������������������������╣ | |
| 22 | â•‘ NOTES â•‘ | |
| 23 | â•‘ â•‘ | |
| 24 | â•‘ A stealth-action thriller set in a city that never sleeps. â•‘ | |
| 25 | â•‘ Cracked clean -- no disc check, no online activation. â•‘ | |
| 26 | â•‘ â•‘ | |
| 27 | â•‘ Mount or burn the ISO, install, then run the KRZ patch over â•‘ | |
| 28 | â•‘ the game directory. Serial is in the keygen. Tested working. â•‘ | |
| 29 | ╠�����������������������������������������������������������������������������╣ | |
| 30 | â•‘ GREETS â•‘ | |
| 31 | â•‘ â•‘ | |
| 32 | â•‘ razor-blade jack the-couriers everyone-in-the-scene â•‘ | |
| 33 | ╠�����������������������������������������������������������������������������╣ | |
| 34 | â•‘ GROUP NEWS â•‘ | |
| 35 | â•‘ â•‘ | |
| 36 | â•‘ Welcome Celestial Wizard back to the group! â•‘ | |
| 37 | â•‘ We are in need of couriers for the release rush -- especially â•‘ | |
| 38 | â•‘ those available in the daytime. Contact a senior member. â•‘ | |
| 39 | ╠�����������������������������������������������������������������������������╣ | |
| 40 | â•‘ MEMBERS â•‘ | |
| 41 | â•‘ â•‘ | |
| 42 | â•‘ Avalon Black Spyrit Butcher The Chairman Darwin Doctor No â•‘ | |
| 43 | â•‘ Eldar Grimstalk Hologram Intrepid Jack Knightmare Marko â•‘ | |
| 44 | â•‘ Ramius ONYX Razor Blade Red Baron Savage Steel StingRay The â•‘ | |
| 45 | â•‘ Unknown The Witch King â•‘ | |
| 46 | â•‘ â•‘ | |
| 47 | ╠�����������������������������������������������������������������������������╣ | |
| 48 | â•‘ COURIERS â•‘ | |
| 49 | â•‘ â•‘ | |
| 50 | â•‘ - Director - â•‘ | |
| 51 | â•‘ The Unforgiven â•‘ | |
| 52 | â•‘ â•‘ | |
| 53 | â•‘ - Senior - â•‘ | |
| 54 | â•‘ Ur Lord Lord Tiamat Wind Walker Bubba â•‘ | |
| 55 | â•‘ â•‘ | |
| 56 | â•‘ - Elite - â•‘ | |
| 57 | â•‘ Shadow Phantom Baddog Load Master Joebee Azmodeus â•‘ | |
| 58 | â•‘ â•‘ | |
| 59 | â•‘ - Trial - â•‘ | |
| 60 | â•‘ Dead Pool Beach Bum Black Mage Ben Jammin â•‘ | |
| 61 | â•‘ â•‘ | |
| 62 | ╠�����������������������������������������������������������������������������╣ | |
| 63 | â•‘ BOARDS â•‘ | |
| 64 | â•‘ â•‘ | |
| 65 | â•‘ BOARD ROLE SYSOP CONTACT â•‘ | |
| 66 | â•‘ Suburbia World HQ The Chairman ITS/PRIVATE â•‘ | |
| 67 | â•‘ TerrorDome Canadian HQ StingRay ITS/PRIVATE â•‘ | |
| 68 | â•‘ Kindergarden European HQ Doctor No +47-7-525156 â•‘ | |
| 69 | â•‘ Apocalypse Courier HQ P.O.W. ITS/PRIVATE â•‘ | |
| 70 | ╠�����������������������������������������������������������������������������╣ | |
| 71 | â•‘ AFFILIATES â•‘ | |
| 72 | â•‘ â•‘ | |
| 73 | â•‘ BOARD ROLE SYSOP CONTACT â•‘ | |
| 74 | â•‘ Reign In Blood Affiliate Uriel 718/698-1969 â•‘ | |
| 75 | â•‘ So-Krate's Affiliate Philosopher 310/578-7226 â•‘ | |
| 76 | â•‘ The Space Zone Affiliate Death Ray 301/416-0118 â•‘ | |
| 77 | ╠�����������������������������������������������������������������������������╣ | |
| 78 | â•‘ â•‘ | |
| 79 | â•‘ SUPPORT THE COMPANIES THAT PRODUCE QUALITY SOFTWARE â•‘ | |
| 80 | â•‘ if you enjoyed this release, buy it! â•‘ | |
| 81 | â•‘ KRZ does not accept cash or hardware of any kind. â•‘ | |
| 82 | â•‘ â•‘ | |
| 83 | ╚������������������������������������������������������������������������������ | |
examples/nightfall-demo.toml added +31
| @@ -0,0 +1,31 @@ | ||
| 1 | # A release config. It names a group profile for all the shared boilerplate | |
| 2 | # and only sets the fields that change from one release to the next. | |
| 3 | profile = "examples/krz.profile.toml" | |
| 4 | ||
| 5 | title = "Nightfall.Protocol.v1.4-KRZ" | |
| 6 | date = "2026-08-08" | |
| 7 | ||
| 8 | cracker = "Lime" | |
| 9 | supplier = "The Witch King" | |
| 10 | packager = "Renegade Chemist" | |
| 11 | protection = "SafeDisc + Serial" | |
| 12 | install = "unzip, run SETUP, apply keygen" | |
| 13 | rating = "9/10" | |
| 14 | ||
| 15 | type = "GAME" | |
| 16 | publisher = "Nocturne Interactive" | |
| 17 | format = "ISO + patch" | |
| 18 | disks = "3 x CD" | |
| 19 | size = "1.87 GB" | |
| 20 | files = "96 x 20 MB" | |
| 21 | url = "krz.sh/nightfall" | |
| 22 | ||
| 23 | notes = """ | |
| 24 | A stealth-action thriller set in a city that never sleeps. | |
| 25 | Cracked clean -- no disc check, no online activation. | |
| 26 | ||
| 27 | Mount or burn the ISO, install, then run the KRZ patch over | |
| 28 | the game directory. Serial is in the keygen. Tested working. | |
| 29 | """ | |
| 30 | ||
| 31 | greets = ["razor-blade", "jack", "the-couriers", "everyone-in-the-scene"] | |
nfogen.py +83 −3
| @@ -140,10 +140,18 @@ def _split_list(value) -> list[str]: | ||
| 140 | 140 | return [p for p in str(value).replace(",", " ").split() if p] |
| 141 | 141 | |
| 142 | 142 | |
| 143 | def _col_widths(available: int, fracs: list[float]) -> list[int]: | |
| 144 | """Split `available` chars into columns by fraction; last column absorbs | |
| 145 | the remainder so the widths always sum exactly.""" | |
| 146 | widths = [max(4, int(available * f)) for f in fracs[:-1]] | |
| 147 | widths.append(available - sum(widths)) | |
| 148 | return widths | |
| 149 | ||
| 150 | ||
| 143 | 151 | def render(data: dict, group: str, site: str, width: int, |
| 144 | 152 | style: str = DEFAULT_STYLE, presents: bool = False, |
| 145 | 153 | footer: str | None = None, layout: str = DEFAULT_LAYOUT, |
| 146 | logo: str | None = None, | |
| 154 | logo: str | None = None, roster: dict | None = None, | |
| 147 | 155 | panel_titles: tuple[str, str] = PANEL_TITLES) -> str: |
| 148 | 156 | s = STYLES[style] |
| 149 | 157 | h, v = s["h"], s["v"] |
| @@ -243,6 +251,66 @@ def render(data: dict, group: str, site: str, width: int, | ||
| 243 | 251 | for wrapped in textwrap.wrap(" ".join(greets), inner - 4): |
| 244 | 252 | out.append(line(" " + wrapped)) |
| 245 | 253 | |
| 254 | # group roster sections (from a profile): news, members, couriers, | |
| 255 | # boards/affiliates tables, outposts | |
| 256 | r = roster or {} | |
| 257 | ||
| 258 | def section(header: str) -> None: | |
| 259 | out.append(sep_before()) | |
| 260 | out.append(line(f" {header}")) | |
| 261 | out.append(line()) | |
| 262 | ||
| 263 | def names_block(names: list[str]) -> None: | |
| 264 | for wrapped in textwrap.wrap(" ".join(names), inner - 4): | |
| 265 | out.append(center(wrapped)) | |
| 266 | out.append(line()) | |
| 267 | ||
| 268 | def table(header: str, entries: list) -> None: | |
| 269 | section(header) | |
| 270 | if entries and isinstance(entries[0], dict): | |
| 271 | ws = _col_widths(inner - 2, [0.34, 0.24, 0.22, 0.20]) | |
| 272 | keys = ["name", "role", "sysop", "phone"] | |
| 273 | ||
| 274 | def row(vals: list[str]) -> str: | |
| 275 | cells = "".join(_clip(str(x), w - 1).ljust(w) | |
| 276 | for x, w in zip(vals, ws)) | |
| 277 | return line(" " + cells) | |
| 278 | ||
| 279 | out.append(row(["BOARD", "ROLE", "SYSOP", "CONTACT"])) | |
| 280 | for e in entries: | |
| 281 | out.append(row([e.get(k, "") for k in keys])) | |
| 282 | else: | |
| 283 | names_block(_split_list(entries)) | |
| 284 | ||
| 285 | news = str(r.get("news") or "").strip() | |
| 286 | if news: | |
| 287 | section("GROUP NEWS") | |
| 288 | for para in news.splitlines(): | |
| 289 | for wrapped in (textwrap.wrap(para, inner - 4) or [""]): | |
| 290 | out.append(line(" " + wrapped)) | |
| 291 | ||
| 292 | members = _split_list(r.get("members") or []) | |
| 293 | if members: | |
| 294 | section("MEMBERS") | |
| 295 | names_block(members) | |
| 296 | ||
| 297 | couriers = r.get("couriers") or {} | |
| 298 | if couriers: | |
| 299 | section("COURIERS") | |
| 300 | for tier, who in couriers.items(): | |
| 301 | out.append(center(f"- {tier} -")) | |
| 302 | names_block(_split_list(who)) | |
| 303 | ||
| 304 | if r.get("boards"): | |
| 305 | table("BOARDS", r["boards"]) | |
| 306 | if r.get("affiliates"): | |
| 307 | table("AFFILIATES", r["affiliates"]) | |
| 308 | ||
| 309 | outposts = _split_list(r.get("outposts") or []) | |
| 310 | if outposts: | |
| 311 | section("OUTPOSTS") | |
| 312 | names_block(outposts) | |
| 313 | ||
| 246 | 314 | # footer disclaimer, centered |
| 247 | 315 | if footer: |
| 248 | 316 | out.append(sep_before()) |
| @@ -268,6 +336,8 @@ def build_parser() -> argparse.ArgumentParser: | ||
| 268 | 336 | ) |
| 269 | 337 | p.add_argument("-c", "--config", metavar="PATH", |
| 270 | 338 | help="config file (.toml or .json) with field defaults") |
| 339 | p.add_argument("-p", "--profile", metavar="PATH", | |
| 340 | help="group profile (logo, roster, boards) merged under the config") | |
| 271 | 341 | p.add_argument("-o", "--output", metavar="PATH", |
| 272 | 342 | help="write to PATH instead of stdout") |
| 273 | 343 | p.add_argument("-g", "--group", help=f"release group (default {DEFAULT_GROUP})") |
| @@ -300,7 +370,13 @@ def build_parser() -> argparse.ArgumentParser: | ||
| 300 | 370 | |
| 301 | 371 | def main(argv: list[str] | None = None) -> int: |
| 302 | 372 | args = build_parser().parse_args(argv) |
| 303 | config = load_config(args.config) | |
| 373 | release = load_config(args.config) | |
| 374 | ||
| 375 | # a group profile supplies base values (logo, roster, style, footer, ...); | |
| 376 | # the release config overrides it, and CLI flags override both. | |
| 377 | profile_path = args.profile or release.get("profile") | |
| 378 | profile = load_config(profile_path) if profile_path else {} | |
| 379 | config = {**profile, **release} | |
| 304 | 380 | |
| 305 | 381 | # merge: config defaults, then any CLI flag that was supplied |
| 306 | 382 | data = {k: config[k] for k, *_ in FIELDS if k in config and config[k] != ""} |
| @@ -338,6 +414,10 @@ def main(argv: list[str] | None = None) -> int: | ||
| 338 | 414 | except FileNotFoundError: |
| 339 | 415 | sys.exit(f"nfogen: logo not found: {logo_path}") |
| 340 | 416 | |
| 417 | roster = {k: config[k] for k in | |
| 418 | ("news", "members", "couriers", "boards", "affiliates", "outposts") | |
| 419 | if config.get(k)} | |
| 420 | ||
| 341 | 421 | interactive = sys.stdin.isatty() and not args.no_input |
| 342 | 422 | if args.interactive: |
| 343 | 423 | prompt_missing(data, force_all=True) |
| @@ -349,7 +429,7 @@ def main(argv: list[str] | None = None) -> int: | ||
| 349 | 429 | |
| 350 | 430 | text = render(data, group=group, site=site, width=width, style=style, |
| 351 | 431 | presents=presents, footer=footer, layout=layout, |
| 352 | logo=logo, panel_titles=panel_titles) | |
| 432 | logo=logo, roster=roster, panel_titles=panel_titles) | |
| 353 | 433 | |
| 354 | 434 | if args.encoding == "cp437": |
| 355 | 435 | raw = text.encode("cp437", errors="replace") |
nfogen.toml +5 −1
| @@ -40,7 +40,11 @@ ENCODING: utf8 or cp437 (--encoding cp437 = DOS) | ||
| 40 | 40 | LOGO: --logo art.asc places art above the box |
| 41 | 41 | BANNERS: --presents --footer scene boilerplate |
| 42 | 42 | |
| 43 | OPTIONS: -o -c -g -s -w --style --layout --logo | |
| 43 | PROFILE: -p group.toml supplies logo, footer, | |
| 44 | members, couriers, boards, affiliates, | |
| 45 | news, outposts. release cfg overrides it. | |
| 46 | ||
| 47 | OPTIONS: -o -c -p -g -s -w --style --layout --logo | |
| 44 | 48 | --encoding --presents --footer -i --no-input |
| 45 | 49 | |
| 46 | 50 | Generated by nfogen itself. |