Skip to content

Releases: SimpleHonors/SparkyBot

v1.6.7

19 Apr 04:48

Choose a tag to compare

Release Notes

AI Commentary: Session-Aware Voice + Smarter Fight Dynamics

What Changed

Several rounds of improvements to how SparkyBot's AI commentary handles
session-level context, fight dynamics, and voice freshness. All shipped in
the same wave; group them however helps you remember.

1. Tiered hype on advantage wins

SparkyBot now understands the difference between a legendary outnumbered win
and a casual 2-to-1 curbstomp, and writes accordingly. Previously, the AI
treated any Decisive Win the same way regardless of the numbers matchup, so
a 35v50 miracle and a 50v18 havoc-party stomp could both produce "LEGENDARY",
"SURGICAL EXECUTION", "speedrun deletion" style commentary.

Now lopsided stomps get actively dismissive commentary. Comfortable wins
(1.3x to 2x squad advantage) get a lighter version of the same treatment.
Outnumbered wins still get full euphoric hype.

2. Player name rotation

SparkyBot tracks which squad players get named as the lead story in recent
responses and discourages the AI from naming the same player repeatedly
across a 2-hour window. Solves the "Formuele in 5 fights in a row" session
fatigue. Live data confirms Formuele's spotlight rate dropped from a 47%
baseline to roughly 17 to 24% across recent sessions, with the spotlight
spread across 10 to 12 different non-commander players per session.

The commander is always exempt. The outlier exception clause allows brief
credit to a flagged player if they are also the current fight's data outlier.

3. Topic / category rotation

Same idea as player rotation, applied to recurring criticism topics. The
prompt previously hit the same complaints (PUG behavior, stomp discipline,
boon denial, etc.) every fight that triggered them. Now SparkyBot tracks
which categories have been pushed into recent prompts and rotates over-used
ones out automatically.

Soft suppression: when a category gets rotated out, the underlying data
stays visible in the FIGHT ANALYSIS block, so the AI can still notice
organically if that category is the defining fact of the fight. The "must
mention this" directive is what rotates, not the data itself.

Threshold: a category fires in 3 of the last 5 fights and gets suppressed
on the next fight. Recovery happens naturally as new fights roll in.

4. Session streak awareness with tone escalation

SparkyBot now tracks the last several fight outcomes and adjusts its mood
based on the current win or loss streak. The voice progressively neutralizes
on long streaks:

  • 2 to 3 win streak: ease off the euphoria slightly, this is not the first win of the night
  • 4 to 5 win streak: rolling hard, do not celebrate the result, mock the enemy for walking into yet another loss
  • 6+ win streak: getting boring, do not celebrate at all, mock the enemy's persistence in showing up to feed
  • 2 to 3 loss streak: this is becoming a pattern, the anger should feel like a repeated warning
  • 4+ loss streak: full tilt, something is structurally wrong tonight

When a streak breaks (loss after a win streak, or win after a loss streak),
SparkyBot now has a special tone for the transition. Win-streak-broken-by-loss
emits a "disappointed not full-tilt rageful, the squad was rolling and this
loss is a shift in energy" mood. Loss-streak-broken-by-win emits a "relief
more than euphoria, do not oversell the recovery" mood.

There is also a shape flavor in the session context line. If every fight in
a streak shares a quality, the streak gets a parenthetical tag. All-blowout
streaks read "(all blowouts, unimpressive)". All-comparable-or-outnumbered
streaks read "(all comparable or outnumbered, real quality)". Mixed streaks
get no flavor.

5. Fight shape labels

A new fight_shape field is now derived from the enemy/friendly ratio and
exposed alongside the existing outcome field. Categories: legendary_outnumbered,
comparable, comfortable, blowout. The outcome field is unchanged so
nothing downstream breaks. fight_shape is the new numbers-aware dimension.

6. Fight dynamics observations

Four new observation lines now appear in the FIGHT ANALYSIS block when the
data shape suggests something specific happened beyond the basic outcome:

  • Enemy respawn traffic: when squad kill events exceed unique enemy count, meaning enemies died, respawned, and returned to die again. Implies a short waypoint or a long enough fight to matter.
  • Enemy rez/rally chain: when squad downs exceed unique enemy count but kills don't, meaning enemies kept going down and getting picked up rather than dying.
  • Squad runbacks: mirror of respawn traffic, applied to our side. When our death events exceed our unique squad count, meaning our players died and ran back.
  • Squad resilience: when our downs received exceeded our deaths, meaning the support line was rezzing well or the enemy could not convert downs to kills. Threshold tuned to require at least 10 downs received and 5 saved to fire, so trivial fights don't trigger noise.

These are informational observations, not mandatory callouts. They live in
the neutral analysis block and the AI uses them when they are the most
dramatic story available.

7. Enemy comp commentary gated to losses

The single most impactful change in this version. Previously, the AI received
full enemy composition data (breakdown by profession, top enemy skills, and a
narrative strategy fingerprint) on every fight. The result: the model led with
enemy comp descriptions on 91% of wins, producing formulaic "that static
meteor-and-lava comp got dismantled" commentary fight after fight.

Now all enemy comp data is stripped from the prompt on wins. The model cannot
see enemy_breakdown or top_enemy_skills in the FIGHT DATA JSON, the strategy
fingerprint line does not appear in FIGHT ANALYSIS, and the enemy_comp_failure
callout does not fire. On losses, all data is preserved so the model can
explain what the enemy did right.

Siege detection is the exception: siege weapons are mockery-worthy regardless
of outcome, so the SIEGE DETECTED observation fires on wins via a separate
code path.

Live testing across Grok 4.20, GPT-5.4, and Gemini 2.5 Flash: enemy comp
references on wins dropped from 91% to 0%.

8. Siege detection overhaul

The previous siege detection used a hardcoded set of 5 skill names including
"Mortar Shot", which is the Engineer Mortar Kit auto-attack, not a siege
weapon. This caused false-positive "SIEGE DETECTED" callouts on fights with
no actual siege. The detection set was also missing Catapults and Flame Rams
entirely.

Replaced with a proper categorized frozenset of ~25 skills covering all 6
siege weapon types, plus substring fallbacks for naming variants. A dedicated
_is_siege_skill() helper replaces all inline set checks.

9. Vocabulary freshness: three detection layers

The n-gram phrase tracker from the previous version now has two additional
detection layers to catch repetition patterns it missed:

Fixation verbs: words like "shredded", "vaporized", "dismantled" that
models latch onto regardless of what comes after them. The n-gram tracker
missed these because "shredded every boon" and "shredded their stability"
are different 3-grams. Now the verb itself is tracked and banned after 3+
uses.

Word frequency tracker: counts every non-stopword, non-domain word across
recent responses. Any word appearing in 6 or more of the last 8 responses
gets banned dynamically. This catches model-specific fixations without manual
curation. Player name components are excluded via accent-normalized matching
so "Bálls" doesn't get "balls" banned.

Live result: "shredded" usage dropped from 89% to 25% across a 12-fight
session.

10. Commander mention variety

The commander was previously blanket-exempt from player name suppression,
resulting in commander mentions in 89% of responses. Now after 3+ mentions
in the 2-hour window, the commander faces a 50% dice roll per fight. Live
result: commander mentions dropped to approximately 50%.

11. PUG commentary controls

Two changes to prevent PUG-blame from becoming default filler:

PUG saturation tracking: when PUGs have been mentioned in 3 or more
recent responses, the model is directed to skip PUG commentary entirely
regardless of the data threshold.

Outnumbered loss mood rewrite: loss mood directives no longer blame PUGs
when the squad was outnumbered. The model is told to acknowledge the matchup
was unfavorable and credit anything the squad did well despite it.

12. max_tokens raised to 8000

Default max_tokens raised from 1000 to 8000 to support reasoning models
(GLM 5.1, GPT-5.3 Codex) where internal thinking consumes most of the token
budget before emitting visible output. Zero cost impact on non-reasoning
models (they stop at finish_reason=stop well before any limit).

What You'll Notice (1.6.7 additions)

Enemy comp is gone on wins. The model now talks about what the squad
did, not what the enemy ran. On losses, the enemy comp analysis is still
there to explain what went wrong.

"Shredded" fatigue is gone. The verb frequency tracker catches model
fixations dynamically. Different models fixate on different words; the
system adapts without manual word-ban curation.

Commander isn't in every post. The dice roll gives the commander name
a natural frequency of roughly every other fight instead of every fight.

Siege callouts are accurate. Engineer Mortar Kit no longer triggers
false siege detection. Real siege weapons (including catapults, which were
previously missing) are properly detected.

Reasoning models work. The max_tokens increase lets GLM 5.1 and GPT-5.3
Codex produce full responses instead of truncating mid-sentence.


What You'll Notice (1.6...

Read more

v1.6.6

09 Apr 21:40

Choose a tag to compare

SparkyBot v1.6.6

Changes

Vocabulary Editor Redesign

The vocabulary editor has been completely rebuilt as a tabbed form-based dialog instead of a raw JSON text box. Each slang category (Shock, Positive, Negative, Situational Slang) gets its own tab with a list view showing terms in a two-line format — the term itself on the first line and its description on the second. Gates entries show their condition on the second line.

  • Per-category Edit buttons: Each category row in the AI settings tab now has its own "Edit..." button that opens the vocabulary editor pre-selected to that category's tab.
  • Default/Custom mode toggle: Each category has a dropdown to switch between Default (uses default terms and weight) and Custom (allows per-category term editing and weight adjustment). Switching to Default resets terms and weight to the built-in defaults.
  • Simplified term dialog: The add/edit term dialog uses friendly labels ("Also matches:", "When to use it:", "Style:") and auto-generates regex patterns from the term name.
  • "Slang Gates" renamed to "Situational Slang": All user-facing labels and tab names now use "Situational Slang".

Vocabulary Update System

SparkyBot tracks vocabulary versions and notifies users when new default terms are available.

  • sparkybot_vocabulary.json now includes a version field and a custom_categories list tracking which categories the user has customized.
  • Update notification: On startup, if a newer default vocabulary is available, SparkyBot prompts with "SparkyBot learned some new words!" and offers to merge new terms (additions only, no destructive replacement) or skip.
  • is_user_modified() / mark_modified(): SparkyBot only prompts when the user has actually customized their vocabulary — factory-reset installations are never prompted.

Vocabulary Version Bump

The vocabulary version has been bumped to 2 to trigger the update prompt for existing users. The update adds "absolute/absolutely" as a tracked intensifier (not a specific catchphrase — tracked to prevent overuse, not to encourage it).

Per-Category Vocabulary Weights

Each slang category now has an independent weight slider in the AI settings tab.

  • Config: aiVocabWeightShock, aiVocabWeightPositive, aiVocabWeightNegative, aiVocabWeightGates in config.properties (0–100, stored as floats in _load_values()).
  • GUI: Per-category QSpinBox (0–100%) in the AI tab, enabled only when that category is in Custom mode.
  • Effect: Weights control how often terms from each category are included in the vocabulary guidance injected into each AI prompt.

System Prompt Versioning and Changelog

SparkyBot tracks system prompt versions and notifies users of improvements.

  • DEFAULT_PROMPT_VERSION = 3 and DEFAULT_PROMPT_CHANGELOG dict in ai_analyst.py document what changed and why.
  • aiPromptVersion config key tracks which version the user has seen.
  • Update notification: On startup, if SparkyBot shipped with a new system prompt since the user's last run, a dialog shows the changelog with "Switch to New Default", "View New Default", and "Keep Mine" options. Every path marks the new version as seen.

AI System Prompt: Version 1 — Architecture and Vocabulary

The system prompt was reorganized into focused methods:

  • _core_system_prompt(): Main body — TRANSLATION LAYER, NARRATIVE ANGLES, VOCABULARY GUIDANCE, RECENT VOCABULARY USAGE, and FIGHT ANALYSIS instructions.
  • _build_vocabulary_sections(): Constructs the vocabulary guidance block injected at the end of every prompt.
  • _rules_section(): Hard rules — sentence count, stat limits, no formatting, output-only, enemy anonymity, PUG threshold, opener variety.

Vocabulary terms are now selected by Python on each API call via probabilistic sampling:

  • Each category has an independent probability (weight) controlling inclusion.
  • Overused terms (2+ uses in the rolling window) are given 0% probability so the model naturally rotates away.
  • The AI is told the palette is a suggestion, not a constraint — it can invent phrases freely.
  • Overuse threshold changed from 2 to 3: Terms must be used 3+ times before being marked overused, giving the model more latitude before terms are banned.

AI System Prompt: Version 2 — Anti-Patterns and Opener Variety

Analysis of 15 AI responses identified recurring structural violations that this update addresses:

  • WRONG/RIGHT example pairs: Three explicit before/after examples show the model how to rewrite "[Player] was a [noun]", "turning X into Y", and double-player "while" constructions. Framed as "Do not fall into these structural ruts" — not as vague "vary across responses" instructions that the model cannot follow in stateless calls.
  • Opener strategy: The model is told to choose an opening that fits the fight data and not default to a shock exclamation when another opener fits better. The word "rotate" (which implies memory across calls) was removed.
  • Sub-300s opener enforcement: When fights are under 300 seconds, the pre-analysis includes "OPENER REQUIREMENT: You MUST reference the fight duration or speed in your first sentence."
  • Stat guidance tightened: "One or two stats" changed to "exactly one number" and "One stat only." "Pick the one that hits hardest" replaces "rotate through different categories."
  • Vocabulary saturation collapse: When 15+ terms are overused, the entire vocabulary ban list is replaced with a single directive ("Invent all vocabulary from scratch") to avoid priming the model with banned terms.
  • Gate conditions enforced in Python: Gates ("Mudda Fucka", "Rallybot", "Siege Humping", etc.) are now filtered by _gate_condition_met() before the dice roll, not left to the model. "Mudda Fucka" won't roll on a Decisive Win, "a free win" requires enemy/friendly ratio > 0.85, etc.
  • Post-processing monitoring: After each response, _handle_success() logs a warning if stat references exceed 3 (to catch models that overshoot the 2-stat limit) and if overused vocabulary terms appear despite the exclusion.

AI System Prompt: Version 3 — Word Count Discipline and Narrative Focus

Shootout testing of 30+ models across multiple fights showed consistent patterns: stat-heavy outputs scored poorly, verbose outputs gamed sentence count with run-on sentences, and loss commentary was underspecified. This update addresses all three.

  • 80-word hard maximum (Rule 2): Models that gamed the 2-4 sentence limit with run-on sentences are now constrained by word budget. The voice description and Rule 2 both state "80 words maximum."
  • Stat limit reduced from 2 to 1: Zero numbers preferred. One allowed only when dramatically impactful. Two or more is a violation.
  • FOCUS section added: "Pick the TWO most dramatic data points, ignore everything else." Stops models from trying to address all 15+ pre-analysis conclusions in 80 words.
  • BROADCAST MODE (Rule 1): New rule stating the entire response is posted directly to a Discord channel. "If a Discord user can tell you are an AI reading a prompt, you have failed."
  • Closing impact (Rule 8): "Your last sentence must land like a punch."
  • Markdown banned (Rule 4): Bold, italics, and asterisks explicitly prohibited.
  • Translation layer examples rewritten: Stat-anchored examples for KDR and healing removed. Remaining examples labeled "(preferred)" for pure narrative to reduce stat mining.
  • Loss/Draw mood computed from fight data: Draw mood now checks tag discipline (scattered vs held-position). Loss and Decisive Loss moods build a specific loss_reasons list from tag distance, stomp rate, and PUG percentage instead of generic directives.
  • Raw aggregate numbers stripped: squad_damage, squad_healing, squad_barrier, enemy_total_damage, and squad_dps removed from the fight data JSON sent to the model. Pre-analysis conclusions provide the same information narratively.

AI System Prompt: Translation Layer Rewrite

The TRANSLATION LAYER section was rewritten. The old prescriptive BAD/GOOD examples were replaced with a narrative-to-stat-anchored spectrum showing genuine examples across the range. The section explicitly instructs the model to:

  • Convert numbers into narrative conclusions before deciding whether to include them.
  • Use the single most impactful stat when a number strengthens a sentence.
  • Pick the one stat that hits hardest — no rotating across categories.

AI System Prompt: Style Event Tracking (Palette vs. Freestyle)

SparkyBot tracks whether each response used predefined vocabulary terms or went freestyle.

  • VocabularyTracker._style_events records {used_palette: bool, ts: float} for each response.
  • _build_style_guidance() injects a nudge based on recent history: if the model went freestyle several times in a row, it's pushed toward palette terms; if it overused the palette, it's encouraged to invent.
  • The AI is explicitly told it can go freestyle at any time.

AI System Prompt: Rule 2 Hardening

Rule 2 now states: "HARD LIMIT — no more than two number references in your entire response." The model must count numbers before outputting.

AI System Prompt: Stomp Discipline — Negative Case

Added: if squad_downs vastly exceeds squad_kills, the squad failed to finish downed enemies. The AI must call this out, not praise the kill count.

AI System Prompt: Pre-Analysis Self-Correction

Added: "If the raw FIGHT DATA contradicts a pre-computed conclusion, trust the data." The model is told to override a pre-analysis conclusion when the raw numbers tell a different story.

AI Pre-Analysis Overhaul

_pre_analyze() in ai_analyst.py now computes far more fight conclusions in Python before sending data to the AI (~35% prompt size reduction).

Tag Discipline Pre-Analysis

Squad positioning relative to commander tag ...

Read more

v1.6.5

03 Apr 00:03

Choose a tag to compare

SparkyBot v1.6.5

Changes

Vocabulary Editor Redesign

The vocabulary editor has been completely rebuilt as a tabbed form-based dialog instead of a raw JSON text box. Each slang category (Shock, Positive, Negative, Situational Slang) gets its own tab with a list view showing terms in a two-line format — the term itself on the first line and its description on the second. Gates entries show their condition on the second line.

  • Per-category Edit buttons: Each category row in the AI settings tab now has its own "Edit..." button that opens the vocabulary editor pre-selected to that category's tab.
  • Default/Custom mode toggle: Each category has a dropdown to switch between Default (uses default terms and weight) and Custom (allows per-category term editing and weight adjustment). Switching to Default resets terms and weight to the built-in defaults.
  • Simplified term dialog: The add/edit term dialog uses friendly labels ("Also matches:", "When to use it:", "Style:") and auto-generates regex patterns from the term name.
  • "Slang Gates" renamed to "Situational Slang": All user-facing labels and tab names now use "Situational Slang".

Vocabulary Update System

SparkyBot tracks vocabulary versions and notifies users when new default terms are available.

  • sparkybot_vocabulary.json now includes a version field and a custom_categories list tracking which categories the user has customized.
  • Update notification: On startup, if a newer default vocabulary is available, SparkyBot prompts with "SparkyBot learned some new words!" and offers to merge new terms (additions only, no destructive replacement) or skip.
  • is_user_modified() / mark_modified(): SparkyBot only prompts when the user has actually customized their vocabulary — factory-reset installations are never prompted.

Vocabulary Version Bump

The vocabulary version has been bumped to 2 to trigger the update prompt for existing users. The update adds "absolute/absolutely" as a tracked intensifier (not a specific catchphrase — tracked to prevent overuse, not to encourage it).

Per-Category Vocabulary Weights

Each slang category now has an independent weight slider in the AI settings tab.

  • Config: aiVocabWeightShock, aiVocabWeightPositive, aiVocabWeightNegative, aiVocabWeightGates in config.properties (0–100, stored as floats in _load_values()).
  • GUI: Per-category QSpinBox (0–100%) in the AI tab, enabled only when that category is in Custom mode.
  • Effect: Weights control how often terms from each category are included in the vocabulary guidance injected into each AI prompt.

System Prompt Versioning and Changelog

SparkyBot tracks system prompt versions and notifies users of improvements.

  • DEFAULT_PROMPT_VERSION = 2 and DEFAULT_PROMPT_CHANGELOG dict in ai_analyst.py document what changed and why.
  • aiPromptVersion config key tracks which version the user has seen.
  • Update notification: On startup, if SparkyBot shipped with a new system prompt since the user's last run, a dialog shows the changelog with "Switch to New Default", "View New Default", and "Keep Mine" options. Every path marks the new version as seen.

AI System Prompt: Version 1 — Architecture and Vocabulary

The system prompt was reorganized into focused methods:

  • _core_system_prompt(): Main body — TRANSLATION LAYER, NARRATIVE ANGLES, VOCABULARY GUIDANCE, RECENT VOCABULARY USAGE, and FIGHT ANALYSIS instructions.
  • _build_vocabulary_sections(): Constructs the vocabulary guidance block injected at the end of every prompt.
  • _rules_section(): Hard rules — sentence count, stat limits, no formatting, output-only, enemy anonymity, PUG threshold, opener variety.

Vocabulary terms are now selected by Python on each API call via probabilistic sampling:

  • Each category has an independent probability (weight) controlling inclusion.
  • Overused terms (2+ uses in the rolling window) are given 0% probability so the model naturally rotates away.
  • The AI is told the palette is a suggestion, not a constraint — it can invent phrases freely.
  • Overuse threshold changed from 2 to 3: Terms must be used 3+ times before being marked overused, giving the model more latitude before terms are banned.

AI System Prompt: Version 2 — Anti-Patterns and Opener Variety

Analysis of 15 AI responses identified recurring structural violations that this update addresses:

  • WRONG/RIGHT example pairs: Three explicit before/after examples show the model how to rewrite "[Player] was a [noun]", "turning X into Y", and double-player "while" constructions. Framed as "Do not fall into these structural ruts" — not as vague "vary across responses" instructions that the model cannot follow in stateless calls.
  • Opener strategy: The model is told to choose an opening that fits the fight data and not default to a shock exclamation when another opener fits better. The word "rotate" (which implies memory across calls) was removed.
  • Sub-300s opener enforcement: When fights are under 300 seconds, the pre-analysis includes "OPENER REQUIREMENT: You MUST reference the fight duration or speed in your first sentence."
  • Stat guidance tightened: "One or two stats" changed to "exactly one number" and "One stat only." "Pick the one that hits hardest" replaces "rotate through different categories."
  • Vocabulary saturation collapse: When 15+ terms are overused, the entire vocabulary ban list is replaced with a single directive ("Invent all vocabulary from scratch") to avoid priming the model with banned terms.
  • Gate conditions enforced in Python: Gates ("Mudda Fucka", "Rallybot", "Siege Humping", etc.) are now filtered by _gate_condition_met() before the dice roll, not left to the model. "Mudda Fucka" won't roll on a Decisive Win, "a free win" requires enemy/friendly ratio > 0.85, etc.
  • Post-processing monitoring: After each response, _handle_success() logs a warning if stat references exceed 3 (to catch models that overshoot the 2-stat limit) and if overused vocabulary terms appear despite the exclusion.

AI System Prompt: Translation Layer Rewrite

The TRANSLATION LAYER section was rewritten. The old prescriptive BAD/GOOD examples were replaced with a narrative-to-stat-anchored spectrum showing genuine examples across the range. The section explicitly instructs the model to:

  • Convert numbers into narrative conclusions before deciding whether to include them.
  • Use the single most impactful stat when a number strengthens a sentence.
  • Pick the one stat that hits hardest — no rotating across categories.

AI System Prompt: Style Event Tracking (Palette vs. Freestyle)

SparkyBot tracks whether each response used predefined vocabulary terms or went freestyle.

  • VocabularyTracker._style_events records {used_palette: bool, ts: float} for each response.
  • _build_style_guidance() injects a nudge based on recent history: if the model went freestyle several times in a row, it's pushed toward palette terms; if it overused the palette, it's encouraged to invent.
  • The AI is explicitly told it can go freestyle at any time.

AI System Prompt: Rule 2 Hardening

Rule 2 now states: "HARD LIMIT — no more than two number references in your entire response." The model must count numbers before outputting.

AI System Prompt: Stomp Discipline — Negative Case

Added: if squad_downs vastly exceeds squad_kills, the squad failed to finish downed enemies. The AI must call this out, not praise the kill count.

AI System Prompt: Pre-Analysis Self-Correction

Added: "If the raw FIGHT DATA contradicts a pre-computed conclusion, trust the data." The model is told to override a pre-analysis conclusion when the raw numbers tell a different story.

AI Pre-Analysis Overhaul

_pre_analyze() in ai_analyst.py now computes far more fight conclusions in Python before sending data to the AI (~35% prompt size reduction).

Tag Discipline Pre-Analysis

Squad positioning relative to commander tag is extracted from EI's distToCom field.

  • 10% trim: Removes the top 10% of distances before computing the core average.
  • Median for grading: Uses median of core distances for threshold comparisons — robust against outliers.
  • No per-player names sent to model: squad_tag_distance is stripped from the trimmed fight JSON before sending. The median grade in the pre-analysis is sufficient — naming the farthest player enables targeting, which encourages calling out individuals rather than the squad's positioning as a whole.
  • Thresholds recalibrated: EXCELLENT < 1200, Acceptable < 2000, Loose < 3500, Scattered >= 3500.
  • Loss/draw gating: Tag discipline commentary only goes negative on Loss, Decisive Loss, or Draw.
  • Debug logging: logger.debug() outputs the full sorted distance list and core distances after trim.

Enemy Strategy Fingerprinting

Deterministic Python-based detection of enemy composition strategy, moved from the AI model into the pre-analysis layer. The AI summary receives a one-line tactical read such as "Enemy strategy: Elementalist nuke comp; boon-heavy support core".

Detects: ranged poke (Soulbeast + Barrage), Elementalist nuke (3+ eles + channeled AoE), Guardian trap-burst (Dragonhunter spike), Scourge-heavy corruption, glass-cannon Berserker carries, and boon-heavy support cores (Herald/Firebrand/Chronomancer).

Co-Outlier Detection

The get_outlier() function in fight_report.py now catches cases where two players are both exceptional. If the top two are within 1.5x of each other but both are 1.5x+ above third place, the outlier dict returns "name1 and name2" so the AI can praise both.

Zone in Pre-Analysis

The map zone name is now extracted from EI JSON and included in the pre-analysis block as Zone: {name}.

Decisive...

Read more

V1.6.4

27 Mar 23:00

Choose a tag to compare

Changelog — SparkyBot v1.6.4

Changes

AI System Prompt: Complete Overhaul

The default AI system prompt has been fully rewritten with a much more detailed analytical framework:

  • Box Score Decoder: A structured methodology for reading fight statistics without having watched the fight, converting raw numbers into narrative conclusions
  • Fight Duration Interpretation: Four tiers (under 300s = execution, 300–900s = standard, 900–1500s = attrition, 1500s+ = epic brawl) with specific commentary guidance for each
  • 20% PUG Blame Threshold: PUGs only mentioned when ally_count exceeds 20% of friendly_count
  • Stomp Discipline Framework: squad_kills vs squad_downs analysis for evaluating rally denial
  • Boon Denial as First-Class Mechanic: Scourge triple-work, Spellbreaker Winds of Disenchantment, and the WvW strip → CC → bomb order of operations
  • Enemy Composition Fingerprinting: Cross-referencing enemy_breakdown with top_enemy_skills to identify ranged poke, nuke, trap-burst, and glass-cannon compositions
  • Siege Damage Detection: Detects Arrow Cart, Ballista, Mortar Shot, Trebuchet in top_enemy_skills — enemies hiding behind siege are "siege humping"
  • Translation Layer: Mandatory internal rule — every stat must be converted to a narrative conclusion; numbers are never reproduced in output
  • Four Narrative Angles: The Carry, The Enemy Failed, The War of Attrition, The Execution/The Collapse — commit to one per fight
  • Expanded Emotional Vocabulary: HOLY SHIT, WHAT THE HELL, GIGACHADS, ABSOLUTE MONSTERS, BIG DAMAGE, battering ram, relentless, fed to the wolves, and more
  • Eight-Gate Slang Decision Tree: Siege Humping → Skill Lag → On Tag → Bags → Police the Rosters → Rallybot → Mudda Fucka → Blob/Zerg
  • Capitalization Rules: Specific terms always ALL CAPS for emphasis
  • Commander Protection: In Loss/Draw/Decisive Loss moods, the commander is never blamed — failure is attributed to squad mechanics or PUGs

Report Sections: Reduced Leaderboard Limits

All player leaderboards in fight reports are now capped at Top 5 (down from Top 10) for tighter, more readable embeds. Affected sections:

  • Damage & Down Contribution, Strips, Cleanses, Outgoing CCs & Interrupts, Outgoing Downs & Kills, Defense, Burst Damage, Enemy Top Skills, Offensive Boon Uptime by Party, Defensive Boon Uptime by Party

The Heals section is also simplified — Barrier and D-Heals (downed healing) columns are removed, and the section is renamed from "Heals & Barrier" to "Heals".

Squad Summary and Enemy Summary are now always included in reports regardless of the display toggles.

Discord Audio: Separated from Stats Embed

The guild icon is no longer attached to the AI commentary embed — it only appears on the fight stats embeds. The audio file is posted as a separate webhook message 1.0 seconds after the AI embed to ensure Discord processes them in the correct order.

AI Debug Prompt: Response Appended

When --debug-ai-prompt is enabled, the debug JSON file now contains both the request and the response in one file per fight. The response section includes raw_content, stripped_content, token usage, and attempt number.

Full Changelog: v1.6.3...v1.6.4

V1.6.3

27 Mar 19:04

Choose a tag to compare

SparkyBot v1.6.3

New Features

Local TTS Playback

AI fight commentary can optionally be read aloud on the machine running SparkyBot after each fight. Disabled by default — enable it in Settings → TTS. Two providers supported:

  • Microsoft Edge TTS — neural voices, free, no API key required
  • ElevenLabs — high-quality voices with five tuning settings (API key required)

Audio is generated once and reused for both local playback and Discord attachment — no double-generation or extra API calls.

Discord Audio Attachment

The generated audio file (sparkybot-commentary.mp3) can optionally be uploaded as an attachment alongside the AI commentary embed. On Discord desktop it renders as an inline audio player beneath the embed. On Discord mobile it appears as a downloadable attachment (Discord limitation).

New Settings: TTS Tab

A dedicated "TTS" tab in Settings with three groups:

  • General — enable local playback, enable Discord attachment, volume slider
  • Provider — Edge TTS or ElevenLabs selection, voice picker, voice tuning sliders
  • Test — one-click test button to verify audio generation

ElevenLabs Voice Tuning

Five settings control output quality and style:

  • Stability (default 35%) — expressive variety vs. consistency
  • Similarity Boost (default 75%) — adherence to original voice
  • Style (default 15%) — voice characteristic amplification
  • Speed (default 1.0, range 0.7–1.2) — speech rate multiplier
  • Speaker Boost (default on) — boosts similarity at minor latency cost

Security Fixes

  • ai_analyst.py: Base URL provider detection now uses parsed hostnames (urlparse) instead of substring search. Prevents URLs like https://evil.com/googleapis.com/path from incorrectly triggering Gemini-specific options.
  • twitch_bot.py: IRC connections now enforce TLS 1.2 minimum version (ssl.TLSVersion.TLSv1_2).

Dependencies

  • Added edge-tts>=7.0.0 for Microsoft Edge TTS voices
  • ElevenLabs integration uses direct REST API calls via requests — no additional pip package required, works on all Python versions including 3.14

Full Changelog: v1.6.2...v1.6.3

v1.6.2

27 Mar 01:42

Choose a tag to compare

v1.6.2

Hotfix: Fixed import re missing in ai_analyst.py, causing AI commentary to fail with "name 're' is not defined" after the v1.6.1 retry logic refactor.

Full Changelog: v1.6.1...v1.6.2

v1.6.1

26 Mar 09:31

Choose a tag to compare

SparkyBot v1.6.1
Twitch IRC — TLS Encryption (Default)
Twitch IRC connections now use TLS on port 6697 by default, encrypting the OAuth token in transit. A new "Use secure connection (TLS)" checkbox is available in Settings → Messaging under the Twitch section. Users behind restrictive firewalls can disable TLS to fall back to plaintext on port 6667 — a warning is displayed when TLS is disabled.

AI Retry Logic
AI commentary requests now retry automatically on failure:

Timeouts — retries up to 2 times with a 3-second delay between attempts
Server errors (5xx) — retries up to 2 times with a 3-second delay
Connection failures — retries up to 2 times with a 3-second delay
Client errors (4xx) — fails immediately (no retry)
Worst case is 3 attempts over ~90 seconds before giving up. Retry attempts are logged so you can track how often they occur.

Updated Security Documentation
SECURITY.md updated to reflect TLS as the default for Twitch IRC
Network communication table now shows TLS (port 6697) as default with plaintext (port 6667) as optional fallback
Supported versions table updated for v1.6.1
Repository Improvements
Added CODE_OF_CONDUCT.md with AI-generated content disclaimer
Added CONTRIBUTING.md with development setup guide and contribution guidelines
Added GitHub issue templates: Bug Report, Feature Request, Unmapped Team ID
Added issue template config with links to GW2EI and ArcDPS for out-of-scope reports
Added LICENSE file (MIT)

Full Changelog: v1.6.0...v1.6.1

V1.6.0

26 Mar 02:53
426b42e

Choose a tag to compare

SparkyBot

A Guild Wars 2 WvW fight log reporter for Discord and Twitch.

SparkyBot monitors your ArcDPS combat log folder, automatically parses new WvW fight logs using GW2 Elite Insights, and posts rich, detailed fight reports to your Discord server or Twitch via webhooks — all without leaving the game.


What It Does

After a WvW fight ends and ArcDPS writes a log file, SparkyBot picks it up within seconds and delivers a full combat report to Discord and optionally Twitch including:

  • AI Fight Commentary (optional) — hype, unhinged WvW analyst narration generated by any OpenAI-compatible LLM
  • Quick Report — KDR, duration, squad/enemy downs, kills, and deaths at a glance
  • Squad & Enemy Summaries — player counts by team color, total damage, DPS, downs, deaths
  • Damage & Down Contribution — top 10 damage dealers with profession and downed damage
  • Burst Damage — best 4-second and 2-second burst windows per player
  • Strips & Cleanses — boon removal and condition cleanse leaderboards
  • Heals & Barrier — healing output, barrier, and downed healing (requires ArcDPS healing addon)
  • Defense — invulns, evades, and blocks
  • Outgoing CCs & Interrupts — hard CC, soft CC, immobilize, and interrupt counts
  • Outgoing Downs & Kills — who secured the most downs and kills
  • Boon Uptime by Party — defensive and offensive boon uptime percentages per subgroup
  • Enemy Top Damage Skills — what hit your squad the hardest
  • Enemy Breakdown — enemy composition by profession and team color

Reports are formatted with uniform-width code blocks, team color detection (Red/Blue/Green), and a configurable guild icon and embed accent color.


Setup

Prerequisites

  1. Python 3.9 or newer — download from python.org
    • Important: during installation, check the box that says "Add Python to PATH"
  2. .NET 8.0 Runtime — download from Microsoft (required by GW2 Elite Insights)
  3. ArcDPS — install in your Guild Wars 2 folder (deltaconnected.com/arcdps)

Installation

  1. Download SparkyBot from the Releases page
  2. Extract the zip to any folder (e.g., C:\SparkyBot)
  3. Double-click SparkyBot.bat

That's it. On first launch, SparkyBot will:

  • Install any missing Python packages automatically
  • Walk you through a setup wizard to configure everything
  • Start monitoring for fight logs

Creating a Discord Webhook

  1. Open Discord and go to the channel where you want fight reports posted
  2. Click the gear icon (Edit Channel) → IntegrationsWebhooks
  3. Click New Webhook, give it a name, and click Copy Webhook URL
  4. Paste the URL into SparkyBot's setup wizard or Settings → Messaging tab

Setting Up Twitch (Optional)

  1. Create a Twitch account for the bot (or use an existing account)
  2. Go to twitchtokengenerator.com
  3. Authorize with Twitch and copy the Access Token
  4. In SparkyBot Settings → Messaging tab, enter the Channel Name and Bot Token
  5. Check "Enable Twitch Bot"
  6. Click "Test Connection" to verify — you should see a test message appear in your Twitch chat

First-Run Wizard

The setup wizard walks you through four steps:

  1. Dependencies — verifies and installs required Python packages
  2. GW2 Elite Insights — downloads the parser automatically, or lets you point to an existing installation
  3. Log Folder — auto-detects your ArcDPS log folder, or lets you browse manually
  4. Discord Webhook — paste your webhook URL

After the wizard completes, click Start Watcher and you're done. SparkyBot minimizes to your system tray and posts fight reports automatically.


Configuration

All settings are accessible through the GUI (right-click the system tray icon → Settings).

Messaging (Discord + Twitch)

Setting Description
Primary / Secondary / Tertiary Webhook Up to three Discord webhook URLs
Active Webhook Which webhook receives reports
Webhook Label Display name for the bot in Discord
Guild Icon Local image shown as the embed thumbnail
Embed Color Accent color for the embed sidebar
Enable Twitch Bot Post fight reports to a Twitch channel
Twitch Channel Name The channel name to post to
Twitch Bot Token OAuth token from twitchtokengenerator.com

Paths

Setting Description
Log Folder Path to your ArcDPS WvW log folder
CLI Executable Path to GW2EI CLI (auto-detected if installed via wizard)
Network Poll Interval How often to check for new files on network shares (1-30 seconds, default 5). Does not affect local folder monitoring.

Thresholds

Setting Description
Min Fight Duration Ignore fights shorter than this (seconds)
Min Fight Downs Ignore fights with fewer total downs
Min Fight Total DMG Ignore fights below this damage threshold
Max Upload Size Maximum log file size to process (MB)

Display

Toggle individual report sections on or off: Quick Report, Damage, Heals, Defense, CCs, Cleanses, Strips, Downs/Kills, Burst Damage, Enemy Skills, Boon Uptimes, Enemy Breakdown.

Behavior

Setting Description
Start with Windows Launch SparkyBot automatically when Windows boots (adds registry entry)
Hide Console Window Use pythonw.exe instead of python.exe — no console window. Also respected by SparkyBot.bat
Close to System Tray Closing the window hides to tray instead of quitting
Minimize to System Tray Minimizing hides to tray
Start Minimized Launch directly to system tray
Start Watcher on Startup Begin monitoring as soon as SparkyBot opens

Tip: For fully hands-off operation, enable Start with Windows + Hide Console Window + Start Minimized + Start Watcher on Startup. SparkyBot will boot silently to the system tray and start watching for fight logs automatically.

Note: Changes to Start with Windows and Hide Console Window require a relaunch to take effect.

Updates

Check for and install updates to both SparkyBot and GW2 Elite Insights from the Settings → Updates tab. Elite Insights updates preserve your settings automatically.

AI

Optional AI-powered fight commentary powered by any OpenAI-compatible API.

Setting Description
Enable AI Fight Analysis Toggle AI commentary on or off
Provider Preset API configurations (see tested providers below)
API Base URL The API endpoint URL (auto-filled from provider preset)
API Key Your API key (blank for local models like Ollama/LM Studio)
Model Model name — select from dropdown or type manually
Refresh Models Fetch available models from the API (falls back to preset list)
Max Tokens Response length limit (default 350, increase to 500+ for thinking models)
System Prompt Custom instructions for the AI — click "Edit System Prompt..." for a full-size editor with a Reset to Default button

When enabled, a brief fight commentary is posted as a separate Discord message immediately after the fight report under the header "[YourBotName] Hot Take and Bad Advice!" — the header pulls from your Webhook Label in the Discord tab, so it matches whatever you've named your bot. The fight report is never delayed by the AI — stats go out first, commentary follows.

Tested Providers

AI Fight Analysis has been tested and confirmed working with:

  • Google Gemini — free tier available at Google AI Studio. Recommended model: gemini-2.5-flash
  • MiniMax — sign up at minimaxi.chat. Recommended model: MiniMax-M2.7
  • OpenRouter — sign up at openrouter.ai. Offers dozens of completely free models — no credit card required, just an API key. See recommended free models below.

OpenRouter Free Models (Recommended)

OpenRouter is the easiest way to try AI Fight Analysis at zero cost. They offer 29+ free models from major providers, all accessible through one API key. Free models have rate limits (typically 20 requests/minute, 200 requests/day) but that's more than enough for WvW fight reports.

To use a free model, select "OpenRouter" as your provider in Settings → AI, enter your OpenRouter API key, and type one of these model IDs in the Model field (free model IDs always end with :free):

Model Why It's Good for SparkyBot
nvidia/nemotron-3-super-120b-a12b:free Large 120B model, great output quality. Tested and confirmed working.
meta-llama/llama-3.3-70b-instruct:free Strong general-purpose model, good at following instructions and staying concise.
mistralai/mistral-small-3.1-24b-instruct:free Fast and instruction-focused. Good at respecting length limits.
stepfun/step-3.5-flash:free Activates only 11B of 196B parameters per token — fast with solid quality.
openrouter/free Auto-router that picks the best available free model for each request. Easiest option.

Free model availability can change over time. Browse the full list at openrouter.ai/collections/free-models.

Untested Providers

The following providers are included as presets but have not been tested by the developer. They should work since they all implement the standard OpenAI-compatible chat completions API, but your mileage m...

Read more

v1.5.2

25 Mar 22:47

Choose a tag to compare

SparkyBot v1.5.2 Release Notes
Bug Fixes
File Watcher Hang on Copied Files
Fixed a critical bug where the file watcher would hang indefinitely when processing copied .zevtc files on local folders. The stability check was using st_mtime which continues updating on Windows/NTFS after a file copy completes, causing an infinite loop. The fix uses size-only checking with 3 consecutive stable reads — faster and more reliable.

AI Commentary Meta-Text Leak
Fixed an issue where some AI providers would leak internal reasoning into the Discord output (e.g., "Character limit: 600 characters max, 2-4 sentences. Let me draft:"). The system prompt now explicitly instructs the AI to output only the commentary itself.

AI Squad/Enemy Confusion
Fixed an issue where the AI would misidentify top-performing squad members as enemies, especially in lost fights. The system prompt now explicitly states that all named players in the report are on the friendly squad, and enemies only appear as aggregated professions in the Enemy Breakdown section.

Self-Update False Positive
Fixed a bug where running an unreleased version (e.g., v1.5.2 dev) would incorrectly show "Update available" when GitHub has an older version. The version comparison was using string equality — now uses proper tuple-based comparison, and correctly shows "You are ahead of the latest release" when running a newer unreleased build.

Process Files Tab Path Matching
Fixed a bug where successful file checkmarks and auto-cleanup never worked because paths from the worker thread (Windows Path objects with backslashes) didn't match the stored strings (from QFileDialog or drag-drop URLs with forward slashes). Both sides are now normalized via resolve() before comparing.

About Tab Window Growth
Fixed a layout issue where the About tab would cause the settings window to grow unboundedly when resized. Wrapped the content in an inner fixed-size widget with outer stretches to absorb extra space, preventing the window from permanently expanding.

New Features
Configurable Network Polling Interval
New spinbox in Settings → Paths lets you set how often SparkyBot checks for new files when watching a network share (1-30 seconds, default 5). Faster intervals for local networks, slower for VPN connections. Does not affect local folder monitoring which uses instant OS events.

Show Strips Toggle
Added an independent "Show Strips" checkbox in the Display tab. Boon strip data was always being generated but had no toggle — now you can show or hide it independently.

Commander Name in AI Commentary
The squad commander's name is now passed to the AI analysis engine, enabling personalized commentary like "Bálls Of Steel had the tag and the numbers — the execution just wasn't there."

AI Token Limit
Default max_tokens increased from 350 to 1000 to prevent truncation, especially with models that use internal reasoning. The hard 600-character limit has been removed from the prompt — length is now controlled by the "2-4 sentences" instruction, which models follow more reliably than character counting. Truncation due to token limits is now logged as a warning.

Debug AI Prompt Flag
New --debug-ai-prompt command-line flag saves the full AI prompt (system prompt + fight data JSON) to a timestamped JSON file for debugging. Useful for inspecting exactly what data the AI receives. Run with python bootstrap.py --debug-ai-prompt and look for ai_prompt_*.json files in the current directory.

Manual File Processing Tab
New "Process Files" tab in the GUI lets you drag-and-drop or browse for .evtc/.zevtc files to process manually. Useful for reprocessing old fights, testing changes, or processing files from a different location. Supports multiple files at once with a queue, progress indicator, and per-file status. The browse dialog opens directly to the configured ArcDPS log folder. Successfully processed files are marked with "✓" and automatically cleared from the list when all files are done. Runs through the same pipeline as the automatic file watcher — GW2EI parse, fight report, Discord post, and AI analysis.

Dynamic Settings Window Width
The settings window now measures the actual rendered width of all tab labels and automatically sizes the window to fit them without scrolling. No more hardcoded pixel values to maintain — adding or renaming tabs requires no window size changes.

Auto-Update on Launch
SparkyBot now checks for updates automatically when it starts. If a newer version is available on GitHub, you will be prompted to update, skip, or suppress future prompts. Elite Insights updates are applied silently in the background without interrupting startup. A "Check for updates on launch" toggle in the Behavior tab controls this feature (enabled by default).

Display Tab Two-Column Layout
Display tab checkboxes are now arranged in a two-column grid, making better use of horizontal space and reducing scroll depth.

AI Context & Prompt Refactoring
The AI fight commentary pipeline has been significantly upgraded for more accurate and engaging analysis:

Data Pipeline (fight_report.py):

Added ally_dead and ally_damage metrics for better PUG performance context
Added individual stab_uptime and aegis_uptime tracking per player
Added outlier detection: players who beat the runner-up by 1.5x in support metrics (strips, cleanses, healing, stability, aegis, burst damage)
Outliers dictionary now includes contextual labels (unit or action) for clarity
Added top_cleanses, top_strips, top_healers, top_bursts, and top_cc arrays
Pruned enemy_breakdown and top_enemy_skills to top 5 entries only (saves tokens)
Filtered out unmapped ArcDPS skills (names starting with "Skill ")
Calculated is_outnumbered and squad_outdamaged_enemy as booleans in Python (prevents LLM hallucination)
Prompt Engineering (ai_analyst.py):

Removed hardcoded example outputs to enable dynamic, fresh commentary
Added "Narrative Focus" directive: AI chooses one of three angles (The Carry, The Enemy Failure, The Meatgrinder)
AI now prioritizes praising players in the outliers dictionary
Added strip terminology guidance: "strips" refers to boon removal, not a currency
Added even-fight threshold: fights within 15% of each other are treated as even, not disadvantaged
Removed flawed ally_damage comparison logic
Added guardrail: AI cannot claim enemy lacked certain professions (only sees top 5 threats)
Added retry loop with exponential backoff for HTTP 503 errors
Added SLANG SELECTION PROTOCOL with prioritized list of gaming terms (Skill Lag, Rallybot, Bags, Police the Rosters, Blob/Zerg) — strictly max 1 per response
Config Persistence (config.py):

Disabled configparser interpolation to prevent percent-sign issues in settings
Formatting Improvements
Wider Player Names
Player name column increased to 20 characters across all tables. Nearly all GW2 character names now display without truncation.

Boon Uptime Tables
Defensive boons: added spacing between columns for better readability
Offensive boons: full boon names (Might, Fury, Vigor, Swift, Alac, Quick) instead of 4-char abbreviations
Enemy Breakdown
Now sorted by count (most common profession first) instead of by damage
Full profession names (Dragonhunter, Chronomancer, etc.) instead of 4-char abbreviations
CC Table — Soft CC and Immob Now Working
Fixed the extraction of Soft CC (chill, cripple, slow, blind, daze, weakness) and Immobilize data. These columns previously always showed zero because the code was looking for the data in the wrong JSON fields. The fix properly reads condition application data from enemy target buffs arrays, matching MzFightReporter's approach. All four CC columns (Hard, Soft, Immob, Interrupts) now display real values.

Cleanup
Display Tab
Removed the dead "Show Spike Damage" checkbox which had no implementation. Burst Damage (which works) was already covering this functionality.

Full Changelog: v1.5.1...v1.5.2

v1.5.1

23 Mar 19:40

Choose a tag to compare

Full Changelog: v1.5...v1.5.1

SparkyBot v1.5.1 Patch Notes

Clean Updates

The self-updater now performs a clean install instead of overlaying new files on top of old ones. When updating SparkyBot, all files and directories are removed first, then the new release is extracted fresh. Only two things are preserved:

  • config.properties — your settings
  • GW2EI/ — Elite Insights installation (has its own updater)

This means stale files from previous versions (like icons that moved to assets/, removed scripts, renamed modules) are automatically cleaned up. No more orphaned files cluttering the root directory after an update.


Console Window Fix

The SparkyBot.bat launcher no longer pauses after a clean exit. Previously, the console window would stay open with "Press any key to continue..." even on normal shutdown. Now it only pauses if the process exits with an error — so you can still read crash tracebacks, but clean shutdowns close the console window immediately.


Changelog

Changed

  • Self-updater deletes all non-protected files before extracting update (clean install instead of overlay)
  • Protected paths reduced to just config.properties and GW2EI/ — everything else gets replaced
  • SparkyBot.bat uses setlocal enabledelayedexpansion and !errorlevel! for reliable exit code checking
  • Console window closes automatically on clean exit, only pauses on errors

Fixed

  • Stale files from previous versions not cleaned up during updates (e.g. root-level icons after assets/ move)
  • Console window staying open with "Press any key to continue..." after normal shutdown
  • %errorlevel% in bat file evaluated at parse time instead of runtime due to cmd.exe delayed expansion behavior